diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2018-02-11 08:21:32 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2018-02-11 08:21:32 +0000 |
commit | 527caeae76c53c56d6a15282d495a4fca08ce0f8 (patch) | |
tree | 0f4bb95d9aba4a754c4b64e357f4045a538664da | |
parent | ed57f1acc9e8646d1d3f9c09e206c1c552fecc9f (diff) | |
parent | 14420d1a408d6185a05f58be30696d57ab5d7ee0 (diff) | |
download | libcxxabi-pie-cuttlefish-testing.tar.gz |
Snap for 4598635 from 14420d1a408d6185a05f58be30696d57ab5d7ee0 to pi-releaseandroid-wear-9.0.0_r9android-wear-9.0.0_r8android-wear-9.0.0_r7android-wear-9.0.0_r6android-wear-9.0.0_r5android-wear-9.0.0_r4android-wear-9.0.0_r34android-wear-9.0.0_r33android-wear-9.0.0_r32android-wear-9.0.0_r31android-wear-9.0.0_r30android-wear-9.0.0_r3android-wear-9.0.0_r29android-wear-9.0.0_r28android-wear-9.0.0_r27android-wear-9.0.0_r26android-wear-9.0.0_r25android-wear-9.0.0_r24android-wear-9.0.0_r23android-wear-9.0.0_r22android-wear-9.0.0_r21android-wear-9.0.0_r20android-wear-9.0.0_r2android-wear-9.0.0_r19android-wear-9.0.0_r18android-wear-9.0.0_r17android-wear-9.0.0_r16android-wear-9.0.0_r15android-wear-9.0.0_r14android-wear-9.0.0_r13android-wear-9.0.0_r12android-wear-9.0.0_r11android-wear-9.0.0_r10android-wear-9.0.0_r1android-vts-9.0_r9android-vts-9.0_r8android-vts-9.0_r7android-vts-9.0_r6android-vts-9.0_r5android-vts-9.0_r4android-vts-9.0_r19android-vts-9.0_r18android-vts-9.0_r17android-vts-9.0_r16android-vts-9.0_r15android-vts-9.0_r14android-vts-9.0_r13android-vts-9.0_r12android-vts-9.0_r11android-vts-9.0_r10android-security-9.0.0_r76android-security-9.0.0_r75android-security-9.0.0_r74android-security-9.0.0_r73android-security-9.0.0_r72android-security-9.0.0_r71android-security-9.0.0_r70android-security-9.0.0_r69android-security-9.0.0_r68android-security-9.0.0_r67android-security-9.0.0_r66android-security-9.0.0_r65android-security-9.0.0_r64android-security-9.0.0_r63android-security-9.0.0_r62android-cts-9.0_r9android-cts-9.0_r8android-cts-9.0_r7android-cts-9.0_r6android-cts-9.0_r5android-cts-9.0_r4android-cts-9.0_r3android-cts-9.0_r20android-cts-9.0_r2android-cts-9.0_r19android-cts-9.0_r18android-cts-9.0_r17android-cts-9.0_r16android-cts-9.0_r15android-cts-9.0_r14android-cts-9.0_r13android-cts-9.0_r12android-cts-9.0_r11android-cts-9.0_r10android-cts-9.0_r1android-9.0.0_r9android-9.0.0_r8android-9.0.0_r7android-9.0.0_r61android-9.0.0_r60android-9.0.0_r6android-9.0.0_r59android-9.0.0_r58android-9.0.0_r57android-9.0.0_r56android-9.0.0_r55android-9.0.0_r54android-9.0.0_r53android-9.0.0_r52android-9.0.0_r51android-9.0.0_r50android-9.0.0_r5android-9.0.0_r49android-9.0.0_r48android-9.0.0_r3android-9.0.0_r2android-9.0.0_r18android-9.0.0_r17android-9.0.0_r10android-9.0.0_r1security-pi-releasepie-vts-releasepie-security-releasepie-s2-releasepie-release-2pie-releasepie-r2-s2-releasepie-r2-s1-releasepie-r2-releasepie-platform-releasepie-gsipie-cuttlefish-testingpie-cts-release
Change-Id: I980be4c5ef24555ed3dee6ee19411c3d4f1d5ed7
53 files changed, 5687 insertions, 4099 deletions
@@ -1,4 +1,4 @@ { - "project_id" : "libcxxabi", + "repository.callsign" : "CXXA", "conduit_uri" : "https://reviews.llvm.org/" } diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..2d1d3be --- /dev/null +++ b/.clang-format @@ -0,0 +1,12 @@ +BasedOnStyle: LLVM + +--- +Language: Cpp + +AlwaysBreakTemplateDeclarations: true +PointerAlignment: Left + +# Disable formatting options which may break tests. +SortIncludes: false +ReflowComments: false +--- @@ -27,7 +27,6 @@ cc_library_static { "src/cxa_exception_storage.cpp", "src/cxa_guard.cpp", "src/cxa_handlers.cpp", - "src/cxa_new_delete.cpp", "src/cxa_personality.cpp", "src/cxa_thread_atexit.cpp", "src/cxa_unexpected.cpp", @@ -36,6 +35,7 @@ cc_library_static { "src/fallback_malloc.cpp", "src/private_typeinfo.cpp", "src/stdlib_exception.cpp", + "src/stdlib_new_delete.cpp", "src/stdlib_stdexcept.cpp", "src/stdlib_typeinfo.cpp", ], diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f23b7c..35eb130 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,5 @@ +# See www/CMake.html for instructions on how to build libcxxabi with CMake. + #=============================================================================== # Setup Project #=============================================================================== @@ -8,122 +10,37 @@ if(POLICY CMP0042) cmake_policy(SET CMP0042 NEW) # Set MACOSX_RPATH=YES by default endif() -if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - project(libcxxabi) - - # Rely on llvm-config. - set(CONFIG_OUTPUT) - if (NOT LLVM_CONFIG_PATH) - find_program(LLVM_CONFIG_PATH "llvm-config") - endif() - if(DEFINED LLVM_PATH) - set(LLVM_INCLUDE_DIR ${LLVM_INCLUDE_DIR} CACHE PATH "Path to llvm/include") - set(LLVM_PATH ${LLVM_PATH} CACHE PATH "Path to LLVM source tree") - set(LLVM_MAIN_SRC_DIR ${LLVM_PATH}) - set(LLVM_CMAKE_PATH "${LLVM_PATH}/cmake/modules") - elseif(LLVM_CONFIG_PATH) - message(STATUS "Found LLVM_CONFIG_PATH as ${LLVM_CONFIG_PATH}") - set(CONFIG_COMMAND ${LLVM_CONFIG_PATH} - "--includedir" - "--prefix" - "--src-root") - execute_process( - COMMAND ${CONFIG_COMMAND} - RESULT_VARIABLE HAD_ERROR - OUTPUT_VARIABLE CONFIG_OUTPUT - ) - if(NOT HAD_ERROR) - string(REGEX REPLACE - "[ \t]*[\r\n]+[ \t]*" ";" - CONFIG_OUTPUT ${CONFIG_OUTPUT}) - else() - string(REPLACE ";" " " CONFIG_COMMAND_STR "${CONFIG_COMMAND}") - message(STATUS "${CONFIG_COMMAND_STR}") - message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}") - endif() - - list(GET CONFIG_OUTPUT 0 INCLUDE_DIR) - list(GET CONFIG_OUTPUT 1 LLVM_OBJ_ROOT) - list(GET CONFIG_OUTPUT 2 MAIN_SRC_DIR) - - set(LLVM_INCLUDE_DIR ${INCLUDE_DIR} CACHE PATH "Path to llvm/include") - set(LLVM_BINARY_DIR ${LLVM_OBJ_ROOT} CACHE PATH "Path to LLVM build tree") - set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree") - set(LLVM_LIT_PATH "${LLVM_PATH}/utils/lit/lit.py") - - # --cmakedir is supported since llvm r291218 (4.0 release) - execute_process( - COMMAND ${LLVM_CONFIG_PATH} --cmakedir - RESULT_VARIABLE HAD_ERROR - OUTPUT_VARIABLE CONFIG_OUTPUT - ERROR_QUIET) - if(NOT HAD_ERROR) - string(STRIP "${CONFIG_OUTPUT}" LLVM_CMAKE_PATH) - else() - set(LLVM_CMAKE_PATH - "${LLVM_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm") - endif() - else() - message(FATAL_ERROR "llvm-config not found and LLVM_MAIN_SRC_DIR not defined. " - "Reconfigure with -DLLVM_CONFIG_PATH=path/to/llvm-config " - "or -DLLVM_PATH=path/to/llvm-source-root.") - endif() +# Add path for custom modules +set(CMAKE_MODULE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules" + ${CMAKE_MODULE_PATH} + ) - if(EXISTS ${LLVM_CMAKE_PATH}) - list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}") - include("${LLVM_CMAKE_PATH}/AddLLVM.cmake") - include("${LLVM_CMAKE_PATH}/HandleLLVMOptions.cmake") - else() - message(FATAL_ERROR "Not found: ${LLVM_CMAKE_PATH}") - endif() +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + project(libcxxabi CXX C) set(PACKAGE_NAME libcxxabi) - set(PACKAGE_VERSION 5.0.0svn) + set(PACKAGE_VERSION 7.0.0svn) set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") set(PACKAGE_BUGREPORT "llvm-bugs@lists.llvm.org") - if (NOT DEFINED LLVM_INCLUDE_TESTS) - set(LLVM_INCLUDE_TESTS ON) - endif() - - if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) - set(LLVM_LIT ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) - else() - # Seek installed Lit. - find_program(LLVM_LIT "lit.py" ${LLVM_MAIN_SRC_DIR}/utils/lit - DOC "Path to lit.py") - endif() - - if(LLVM_LIT) - # Define the default arguments to use with 'lit', and an option for the user - # to override. - set(LIT_ARGS_DEFAULT "-sv") - if (MSVC OR XCODE) - set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar") - endif() - set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit") - - # On Win32 hosts, provide an option to specify the path to the GnuWin32 tools. - if( WIN32 AND NOT CYGWIN ) - set(LLVM_LIT_TOOLS_DIR "" CACHE PATH "Path to GnuWin32 tools") - endif() - else() - set(LLVM_INCLUDE_TESTS OFF) - endif() - - set(LIBCXXABI_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}" CACHE STRING - "Define suffix of library directory name (32/64)") - - set(LIBCXXABI_STANDALONE_BUILD 1) -else() - set(LLVM_MAIN_SRC_DIR "${CMAKE_SOURCE_DIR}" CACHE PATH "Path to LLVM source tree") - set(LLVM_LIT "${CMAKE_SOURCE_DIR}/utils/lit/lit.py") - set(LIBCXXABI_LIBDIR_SUFFIX ${LLVM_LIBDIR_SUFFIX}) + # Find the LLVM sources and simulate LLVM CMake options. + include(HandleOutOfTreeLLVM) endif() +# Require out of source build. +include(MacroEnsureOutOfSourceBuild) +MACRO_ENSURE_OUT_OF_SOURCE_BUILD( + "${PROJECT_NAME} requires an out of source build. Please create a separate + build directory and run 'cmake /path/to/${PROJECT_NAME} [options]' there." + ) + #=============================================================================== # Setup CMake Options #=============================================================================== +include(CMakeDependentOption) +include(HandleCompilerRT) # Define options. option(LIBCXXABI_ENABLE_EXCEPTIONS "Use exceptions." ON) @@ -131,6 +48,7 @@ option(LIBCXXABI_ENABLE_ASSERTIONS "Enable assertions independent of build mode. option(LIBCXXABI_ENABLE_PEDANTIC "Compile with pedantic enabled." ON) option(LIBCXXABI_ENABLE_WERROR "Fail and stop if a warning is triggered." OFF) option(LIBCXXABI_USE_LLVM_UNWINDER "Build and use the LLVM unwinder." OFF) +option(LIBCXXABI_ENABLE_STATIC_UNWINDER "Statically link the LLVM unwinder." OFF) option(LIBCXXABI_USE_COMPILER_RT "Use compiler-rt instead of libgcc" OFF) option(LIBCXXABI_ENABLE_THREADS "Build with threads enabled" ON) option(LIBCXXABI_HAS_PTHREAD_API "Ignore auto-detection and force use of pthread API" OFF) @@ -140,8 +58,18 @@ option(LIBCXXABI_HAS_EXTERNAL_THREAD_API option(LIBCXXABI_BUILD_EXTERNAL_THREAD_LIBRARY "Build libc++abi with an externalized threading library. This option may only be set to ON when LIBCXXABI_ENABLE_THREADS=ON" OFF) + +# FIXME: This option should default to off. Unfortunatly GCC 4.9 fails to link +# programs to due undefined references to new/delete in libc++abi. Once this +# has been fixed or worked around the default value should be changed. +option(LIBCXXABI_ENABLE_NEW_DELETE_DEFINITIONS + "Build libc++abi with definitions for operator new/delete. Normally libc++ + provides these definitions" ON) option(LIBCXXABI_BUILD_32_BITS "Build 32 bit libc++abi." ${LLVM_BUILD_32_BITS}) option(LIBCXXABI_INCLUDE_TESTS "Generate build targets for the libc++abi unit tests." ${LLVM_INCLUDE_TESTS}) +set(LIBCXXABI_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}" CACHE STRING + "Define suffix of library directory name (32/64)") +option(LIBCXXABI_INSTALL_LIBRARY "Install the libc++abi library." ON) set(LIBCXXABI_TARGET_TRIPLE "" CACHE STRING "Target triple for cross compiling.") set(LIBCXXABI_GCC_TOOLCHAIN "" CACHE PATH "GCC toolchain for cross compiling.") set(LIBCXXABI_SYSROOT "" CACHE PATH "Sysroot for cross compiling.") @@ -156,6 +84,11 @@ set(LIBCXXABI_LIBCXX_LIBRARY_PATH "" CACHE PATH "The path to libc++ library.") option(LIBCXXABI_ENABLE_SHARED "Build libc++abi as a shared library." ON) option(LIBCXXABI_ENABLE_STATIC "Build libc++abi as a static library." ON) +option(LIBCXXABI_BAREMETAL "Build libc++abi for baremetal targets." OFF) +# The default terminate handler attempts to demangle uncaught exceptions, which +# causes extra I/O and demangling code to be pulled in. +option(LIBCXXABI_SILENT_TERMINATE "Set this to make the terminate handler default to a silent alternative" OFF) + if (NOT LIBCXXABI_ENABLE_SHARED AND NOT LIBCXXABI_ENABLE_STATIC) message(FATAL_ERROR "libc++abi must be built as either a shared or static library.") endif() @@ -182,6 +115,7 @@ find_path( ${CMAKE_BINARY_DIR}/${LIBCXXABI_LIBCXX_INCLUDES} ${LIBCXXABI_LIBCXX_INCLUDE_DIRS} ${LLVM_INCLUDE_DIR}/c++/v1 + NO_CMAKE_FIND_ROOT_PATH ) set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXXABI_LIBCXX_INCLUDES}" CACHE PATH @@ -189,11 +123,12 @@ set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXXABI_LIBCXX_INCLUDES}" CACHE PATH find_path( LIBCXXABI_LIBCXX_PATH - test/libcxx/__init__.py + utils/libcxx/test/__init__.py PATHS ${LIBCXXABI_LIBCXX_PATH} ${LIBCXXABI_LIBCXX_INCLUDES}/../ ${LIBCXXABI_LIBCXX_SRC_DIRS} NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH ) if (LIBCXXABI_LIBCXX_PATH STREQUAL "LIBCXXABI_LIBCXX_PATH-NOTFOUND") @@ -218,10 +153,22 @@ set(CMAKE_MODULE_PATH set(LIBCXXABI_COMPILER ${CMAKE_CXX_COMPILER}) set(LIBCXXABI_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(LIBCXXABI_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) -set(LIBCXXABI_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib${LIBCXXABI_LIBDIR_SUFFIX}) +if (LLVM_LIBRARY_OUTPUT_INTDIR) + set(LIBCXXABI_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}) +else() + set(LIBCXXABI_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib${LIBCXXABI_LIBDIR_SUFFIX}) +endif() + +set(LIBCXXABI_INSTALL_PREFIX "" CACHE STRING + "Define libc++abi destination prefix.") + +if (NOT LIBCXXABI_INSTALL_PREFIX MATCHES "^$|.*/") + message(FATAL_ERROR "LIBCXXABI_INSTALL_PREFIX has to end with \"/\".") +endif() set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIBCXXABI_LIBRARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LIBCXXABI_LIBRARY_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${LIBCXXABI_LIBRARY_DIR}) # By default, for non-standalone builds, libcxx and libcxxabi share a library # directory. @@ -239,36 +186,33 @@ elseif(LIBCXXABI_BUILD_32_BITS) message(FATAL_ERROR "LIBCXXABI_BUILD_32_BITS=ON is not supported on this platform.") endif() -#=============================================================================== -# Setup Compiler Flags -#=============================================================================== - -# Get required flags. -macro(append_if list condition var) - if (${condition}) - list(APPEND ${list} ${var}) - endif() -endmacro() - -macro(add_target_flags_if condition var) - if (${condition}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${var}") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${var}") - list(APPEND LIBCXXABI_LINK_FLAGS ${var}) - endif() -endmacro() +# Declare libc++abi configuration variables. +# They are intended for use as follows: +# LIBCXXABI_C_FLAGS: General flags for both the c++ compiler and linker. +# LIBCXXABI_CXX_FLAGS: General flags for both the c++ compiler and linker. +# LIBCXXABI_COMPILE_FLAGS: Compile only flags. +# LIBCXXABI_LINK_FLAGS: Linker only flags. +# LIBCXXABI_LIBRARIES: libraries libc++abi is linked to. set(LIBCXXABI_C_FLAGS "") set(LIBCXXABI_CXX_FLAGS "") set(LIBCXXABI_COMPILE_FLAGS "") set(LIBCXXABI_LINK_FLAGS "") +set(LIBCXXABI_LIBRARIES "") + +# Include macros for adding and removing libc++abi flags. +include(HandleLibcxxabiFlags) + +#=============================================================================== +# Setup Compiler Flags +#=============================================================================== # Configure target flags add_target_flags_if(LIBCXXABI_BUILD_32_BITS "-m32") add_target_flags_if(LIBCXXABI_TARGET_TRIPLE - "-target ${LIBCXXABI_TARGET_TRIPLE}") + "--target=${LIBCXXABI_TARGET_TRIPLE}") add_target_flags_if(LIBCXXABI_GCC_TOOLCHAIN - "-gcc-toolchain ${LIBCXXABI_GCC_TOOLCHAIN}") + "--gcc-toolchain=${LIBCXXABI_GCC_TOOLCHAIN}") add_target_flags_if(LIBCXXABI_SYSROOT "--sysroot=${LIBCXXABI_SYSROOT}") @@ -296,56 +240,57 @@ add_definitions(-D_LIBCXXABI_BUILDING_LIBRARY) # Disable DLL annotations on Windows for static builds. if (WIN32 AND LIBCXXABI_ENABLE_STATIC AND NOT LIBCXXABI_ENABLE_SHARED) - add_definitions(-D_LIBCXXABI_DISABLE_DLL_IMPORT_EXPORT) + add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS) endif() -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WERROR_FLAG -Werror=return-type) +add_compile_flags_if_supported(-Werror=return-type) # Get warning flags -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_W_FLAG -W) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WALL_FLAG -Wall) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WCHAR_SUBSCRIPTS_FLAG -Wchar-subscripts) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WCONVERSION_FLAG -Wconversion) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WMISMATCHED_TAGS_FLAG -Wmismatched-tags) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WMISSING_BRACES_FLAG -Wmissing-braces) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WNEWLINE_EOF_FLAG -Wnewline-eof) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WNO_UNUSED_FUNCTION_FLAG -Wno-unused-function) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WSHADOW_FLAG -Wshadow) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WSHORTEN_64_TO_32_FLAG -Wshorten-64-to-32) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WSIGN_COMPARE_FLAG -Wsign-compare) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WSIGN_CONVERSION_FLAG -Wsign-conversion) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WSTRICT_ALIASING_FLAG -Wstrict-aliasing=2) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WSTRICT_OVERFLOW_FLAG -Wstrict-overflow=4) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WUNUSED_PARAMETER_FLAG -Wunused-parameter) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WUNUSED_VARIABLE_FLAG -Wunused-variable) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WWRITE_STRINGS_FLAG -Wwrite-strings) -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WUNDEF_FLAG -Wundef) +add_compile_flags_if_supported(-W) +add_compile_flags_if_supported(-Wall) +add_compile_flags_if_supported(-Wchar-subscripts) +add_compile_flags_if_supported(-Wconversion) +add_compile_flags_if_supported(-Wmismatched-tags) +add_compile_flags_if_supported(-Wmissing-braces) +add_compile_flags_if_supported(-Wnewline-eof) +add_compile_flags_if_supported(-Wunused-function) +add_compile_flags_if_supported(-Wshadow) +add_compile_flags_if_supported(-Wshorten-64-to-32) +add_compile_flags_if_supported(-Wsign-compare) +add_compile_flags_if_supported(-Wsign-conversion) +add_compile_flags_if_supported(-Wstrict-aliasing=2) +add_compile_flags_if_supported(-Wstrict-overflow=4) +add_compile_flags_if_supported(-Wunused-parameter) +add_compile_flags_if_supported(-Wunused-variable) +add_compile_flags_if_supported(-Wwrite-strings) +add_compile_flags_if_supported(-Wundef) if (LIBCXXABI_ENABLE_WERROR) - append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WERROR_FLAG -Werror) - append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WX_FLAG -WX) + add_compile_flags_if_supported(-Werror) + add_compile_flags_if_supported(-WX) else() - append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_WNO_ERROR_FLAG -Wno-error) - append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_NO_WX_FLAG -WX-) + add_compile_flags_if_supported(-Wno-error) + add_compile_flags_if_supported(-WX-) endif() if (LIBCXXABI_ENABLE_PEDANTIC) - append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_PEDANTIC_FLAG -pedantic) + add_compile_flags_if_supported(-pedantic) endif() # Get feature flags. -append_if(LIBCXXABI_CXX_FLAGS LIBCXXABI_HAS_FSTRICT_ALIASING_FLAG -fstrict-aliasing) +add_compile_flags_if_supported(-fstrict-aliasing) # Exceptions if (LIBCXXABI_ENABLE_EXCEPTIONS) # Catches C++ exceptions only and tells the compiler to assume that extern C # functions never throw a C++ exception. - append_if(LIBCXXABI_CXX_FLAGS LIBCXXABI_HAS_EHSC_FLAG -EHsc) - append_if(LIBCXXABI_C_FLAGS LIBCXXABI_HAS_FUNWIND_TABLES -funwind-tables) + add_compile_flags_if_supported(-EHsc) + # Do we really need to be run through the C compiler ? + add_c_compile_flags_if_supported(-funwind-tables) else() add_definitions(-D_LIBCXXABI_NO_EXCEPTIONS) - append_if(LIBCXXABI_CXX_FLAGS LIBCXXABI_HAS_NO_EXCEPTIONS_FLAG -fno-exceptions) - append_if(LIBCXXABI_CXX_FLAGS LIBCXXABI_HAS_NO_EHS_FLAG -EHs-) - append_if(LIBCXXABI_CXX_FLAGS LIBCXXABI_HAS_NO_EHA_FLAG -EHa-) + add_compile_flags_if_supported(-fno-exceptions) + add_compile_flags_if_supported(-EHs-) + add_compile_flags_if_supported(-EHa-) endif() # Assert @@ -403,10 +348,23 @@ if (LIBCXXABI_HAS_EXTERNAL_THREAD_API) endif() endif() -if (LIBCXXABI_BUILD_EXTERNAL_THREAD_LIBRARY AND LIBCXXABI_ENABLE_SHARED) +if (LLVM_ENABLE_MODULES) + # Ignore that the rest of the modules flags are now unused. + add_compile_flags_if_supported(-Wno-unused-command-line-argument) + add_compile_flags(-fno-modules) +endif() + +set(LIBCXXABI_HAS_UNDEFINED_SYMBOLS OFF) +if ((NOT LIBCXXABI_ENABLE_NEW_DELETE_DEFINITIONS) + OR (LIBCXXABI_BUILD_EXTERNAL_THREAD_LIBRARY AND LIBCXXABI_ENABLE_SHARED) + OR MINGW) + set(LIBCXXABI_HAS_UNDEFINED_SYMBOLS ON) +endif() + +if (LIBCXXABI_HAS_UNDEFINED_SYMBOLS) # Need to allow unresolved symbols if this is to work with shared library builds if (APPLE) - add_link_flags("-undefined dynamic_lookup") + list(APPEND LIBCXXABI_LINK_FLAGS "-undefined dynamic_lookup") else() # Relax this restriction from HandleLLVMOptions string(REPLACE "-Wl,-z,defs" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") @@ -425,17 +383,24 @@ if (LIBCXXABI_BUILD_EXTERNAL_THREAD_LIBRARY) add_definitions(-D_LIBCPP_HAS_THREAD_LIBRARY_EXTERNAL) endif() +# Prevent libc++abi from having library dependencies on libc++ +add_definitions(-D_LIBCPP_DISABLE_EXTERN_TEMPLATE) + if (MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS) endif() # Define LIBCXXABI_USE_LLVM_UNWINDER for conditional compilation. if (LIBCXXABI_USE_LLVM_UNWINDER) - add_definitions(-DLIBCXXABI_USE_LLVM_UNWINDER=1) + add_definitions(-DLIBCXXABI_USE_LLVM_UNWINDER) endif() if (LIBCXXABI_SILENT_TERMINATE) - add_definitions(-DLIBCXXABI_SILENT_TERMINATE=1) + add_definitions(-DLIBCXXABI_SILENT_TERMINATE) +endif() + +if (LIBCXXABI_BAREMETAL) + add_definitions(-DLIBCXXABI_BAREMETAL) endif() string(REPLACE ";" " " LIBCXXABI_CXX_FLAGS "${LIBCXXABI_CXX_FLAGS}") @@ -462,47 +427,37 @@ if (LIBCXXABI_USE_LLVM_UNWINDER OR LLVM_NATIVE_ARCH MATCHES ARM) ${LLVM_MAIN_SRC_DIR}/projects/libunwind/include ${LLVM_MAIN_SRC_DIR}/runtimes/libunwind/include NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH ) - find_path( - LIBCXXABI_LIBUNWIND_SOURCES - libunwind_ext.h - PATHS ${LIBCXXABI_LIBUNWIND_PATH}/src/ - ${LIBCXXABI_LIBUNWIND_INCLUDES}/../src/ - ${LLVM_MAIN_SRC_DIR}/projects/libunwind/src/ - ${LLVM_MAIN_SRC_DIR}/runtimes/libunwind/src/ - NO_DEFAULT_PATH - ) - - if (LIBCXXABI_LIBUNWIND_SOURCES STREQUAL "LIBCXXABI_LIBUNWIND_SOURCES-NOTFOUND") - message(WARNING "LIBCXXABI_LIBUNWIND_SOURCES was not specified and couldn't be infered.") - set(LIBCXXABI_LIBUNWIND_SOURCES "") + if (LIBCXXABI_LIBUNWIND_INCLUDES_INTERNAL STREQUAL "LIBCXXABI_LIBUNWIND_INCLUDES_INTERNAL-NOTFOUND") + set(LIBCXXABI_LIBUNWIND_INCLUDES_INTERNAL "") endif() - if (NOT LIBCXXABI_LIBUNWIND_INCLUDES_INTERNAL STREQUAL "LIBCXXABI_LIBUNWIND_INCLUDES_INTERNAL-NOTFOUND") + if (NOT LIBCXXABI_LIBUNWIND_INCLUDES_INTERNAL STREQUAL "") include_directories("${LIBCXXABI_LIBUNWIND_INCLUDES_INTERNAL}") endif() - if (NOT LIBCXXABI_LIBUNWIND_SOURCES STREQUAL "") - include_directories("${LIBCXXABI_LIBUNWIND_SOURCES}") - endif() endif() # Add source code. This also contains all of the logic for deciding linker flags # soname, etc... add_subdirectory(src) -if (NOT LIBCXXABI_INCLUDE_TESTS OR (LIBCXXABI_STANDALONE_BUILD AND NOT LIBCXXABI_ENABLE_SHARED)) - # We can't reasonably test the system C++ library with a static libc++abi. - # We either need to be able to replace libc++abi at run time (with a shared - # libc++abi), or we need to be able to replace the C++ runtime (with a non- - # standalone build). - message(WARNING "The libc++abi tests aren't valid when libc++abi is built " - "standalone (i.e. outside of llvm/projects/libcxxabi ) and " - "is built without a shared library. Either build a shared " - "library, build libc++abi at the same time as you build " - "libc++, or do without testing. No check target will be " - "available!") -else() - add_subdirectory(test) - add_subdirectory(fuzz) +if (LIBCXXABI_INCLUDE_TESTS) + if (LIBCXXABI_STANDALONE_BUILD AND NOT LIBCXXABI_ENABLE_SHARED) + # We can't reasonably test the system C++ library with a static + # libc++abi. We either need to be able to replace libc++abi at + # run time (with a shared libc++abi), or we need to be able to + # replace the C++ runtime (with a non- standalone build). + message(WARNING "The libc++abi tests aren't valid when libc++abi " + "is built standalone (i.e. outside of " + "llvm/projects/libcxxabi ) and is built without " + "a shared library. Either build a shared " + "library, build libc++abi at the same time as " + "you build libc++, or do without testing. No " + "check target will be available!") + else() + add_subdirectory(test) + add_subdirectory(fuzz) + endif() endif() diff --git a/CREDITS.TXT b/CREDITS.TXT index 9c910fc..fbb6cbf 100644 --- a/CREDITS.TXT +++ b/CREDITS.TXT @@ -58,7 +58,7 @@ E: erik@olofsson.info D: Minor patches and fixes N: Jon Roelofs -E: jonathan@codesourcery.com +E: jroelofs@jroelofs.com D: ARM EHABI Unwind & Exception Handling, Bare-metal N: Nico Weber diff --git a/cmake/Modules/HandleCompilerRT.cmake b/cmake/Modules/HandleCompilerRT.cmake new file mode 100644 index 0000000..395c21a --- /dev/null +++ b/cmake/Modules/HandleCompilerRT.cmake @@ -0,0 +1,58 @@ +function(find_compiler_rt_library name dest) + if (NOT DEFINED LIBCXXABI_COMPILE_FLAGS) + message(FATAL_ERROR "LIBCXXABI_COMPILE_FLAGS must be defined when using this function") + endif() + set(dest "" PARENT_SCOPE) + set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${LIBCXXABI_COMPILE_FLAGS} + "--rtlib=compiler-rt" "--print-libgcc-file-name") + if (CMAKE_CXX_COMPILER_ID MATCHES Clang AND CMAKE_CXX_COMPILER_TARGET) + list(APPEND CLANG_COMMAND "--target=${CMAKE_CXX_COMPILER_TARGET}") + endif() + execute_process( + COMMAND ${CLANG_COMMAND} + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LIBRARY_FILE + ) + string(STRIP "${LIBRARY_FILE}" LIBRARY_FILE) + string(REPLACE "builtins" "${name}" LIBRARY_FILE "${LIBRARY_FILE}") + if (NOT HAD_ERROR AND EXISTS "${LIBRARY_FILE}") + message(STATUS "Found compiler-rt library: ${LIBRARY_FILE}") + set(${dest} "${LIBRARY_FILE}" PARENT_SCOPE) + else() + message(STATUS "Failed to find compiler-rt library") + endif() +endfunction() + +function(find_compiler_rt_dir dest) + if (NOT DEFINED LIBCXXABI_COMPILE_FLAGS) + message(FATAL_ERROR "LIBCXXABI_COMPILE_FLAGS must be defined when using this function") + endif() + set(dest "" PARENT_SCOPE) + if (APPLE) + set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${LIBCXXABI_COMPILE_FLAGS} + "-print-file-name=lib") + execute_process( + COMMAND ${CLANG_COMMAND} + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LIBRARY_DIR + ) + string(STRIP "${LIBRARY_DIR}" LIBRARY_DIR) + set(LIBRARY_DIR "${LIBRARY_DIR}/darwin") + else() + set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${LIBCXXABI_COMPILE_FLAGS} + "--rtlib=compiler-rt" "--print-libgcc-file-name") + execute_process( + COMMAND ${CLANG_COMMAND} + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LIBRARY_FILE + ) + string(STRIP "${LIBRARY_FILE}" LIBRARY_FILE) + get_filename_component(LIBRARY_DIR "${LIBRARY_FILE}" DIRECTORY) + endif() + if (NOT HAD_ERROR AND EXISTS "${LIBRARY_DIR}") + message(STATUS "Found compiler-rt directory: ${LIBRARY_DIR}") + set(${dest} "${LIBRARY_DIR}" PARENT_SCOPE) + else() + message(STATUS "Failed to find compiler-rt directory") + endif() +endfunction() diff --git a/cmake/Modules/HandleLibcxxabiFlags.cmake b/cmake/Modules/HandleLibcxxabiFlags.cmake new file mode 100644 index 0000000..3eddd7b --- /dev/null +++ b/cmake/Modules/HandleLibcxxabiFlags.cmake @@ -0,0 +1,208 @@ +# HandleLibcxxFlags - A set of macros used to setup the flags used to compile +# and link libc++abi. These macros add flags to the following CMake variables. +# - LIBCXXABI_COMPILE_FLAGS: flags used to compile libc++abi +# - LIBCXXABI_LINK_FLAGS: flags used to link libc++abi +# - LIBCXXABI_LIBRARIES: libraries to link libc++abi to. + +include(CheckCXXCompilerFlag) + +unset(add_flag_if_supported) + +# Mangle the name of a compiler flag into a valid CMake identifier. +# Ex: --std=c++11 -> STD_EQ_CXX11 +macro(mangle_name str output) + string(STRIP "${str}" strippedStr) + string(REGEX REPLACE "^/" "" strippedStr "${strippedStr}") + string(REGEX REPLACE "^-+" "" strippedStr "${strippedStr}") + string(REGEX REPLACE "-+$" "" strippedStr "${strippedStr}") + string(REPLACE "-" "_" strippedStr "${strippedStr}") + string(REPLACE "=" "_EQ_" strippedStr "${strippedStr}") + string(REPLACE "+" "X" strippedStr "${strippedStr}") + string(TOUPPER "${strippedStr}" ${output}) +endmacro() + +# Remove a list of flags from all CMake variables that affect compile flags. +# This can be used to remove unwanted flags specified on the command line +# or added in other parts of LLVM's cmake configuration. +macro(remove_flags) + foreach(var ${ARGN}) + string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") + string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}") + string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") + string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + string(REPLACE "${var}" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REPLACE "${var}" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + string(REPLACE "${var}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") + string(REPLACE "${var}" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") + string(REPLACE "${var}" "" CMAKE_SHARED_MODULE_FLAGS "${CMAKE_SHARED_MODULE_FLAGS}") + remove_definitions(${var}) + endforeach() +endmacro(remove_flags) + +macro(check_flag_supported flag) + mangle_name("${flag}" flagname) + check_cxx_compiler_flag("${flag}" "LIBCXXABI_SUPPORTS_${flagname}_FLAG") +endmacro() + +# Add a macro definition if condition is true. +macro(define_if condition def) + if (${condition}) + add_definitions(${def}) + endif() +endmacro() + +# Add a macro definition if condition is not true. +macro(define_if_not condition def) + if (NOT ${condition}) + add_definitions(${def}) + endif() +endmacro() + +# Add a macro definition to the __config_site file if the specified condition +# is 'true'. Note that '-D${def}' is not added. Instead it is expected that +# the build include the '__config_site' header. +macro(config_define_if condition def) + if (${condition}) + set(${def} ON) + set(LIBCXXABI_NEEDS_SITE_CONFIG ON) + endif() +endmacro() + +macro(config_define_if_not condition def) + if (NOT ${condition}) + set(${def} ON) + set(LIBCXXABI_NEEDS_SITE_CONFIG ON) + endif() +endmacro() + +macro(config_define value def) + set(${def} ${value}) + set(LIBCXXABI_NEEDS_SITE_CONFIG ON) +endmacro() + +# Add a list of flags to all of 'CMAKE_CXX_FLAGS', 'CMAKE_C_FLAGS', +# 'LIBCXXABI_COMPILE_FLAGS' and 'LIBCXXABI_LINK_FLAGS'. +macro(add_target_flags) + foreach(value ${ARGN}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${value}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${value}") + list(APPEND LIBCXXABI_COMPILE_FLAGS ${value}) + list(APPEND LIBCXXABI_LINK_FLAGS ${value}) + endforeach() +endmacro() + +# If the specified 'condition' is true then add a list of flags to +# all of 'CMAKE_CXX_FLAGS', 'CMAKE_C_FLAGS', 'LIBCXXABI_COMPILE_FLAGS' +# and 'LIBCXXABI_LINK_FLAGS'. +macro(add_target_flags_if condition) + if (${condition}) + add_target_flags(${ARGN}) + endif() +endmacro() + +# Add a specified list of flags to both 'LIBCXXABI_COMPILE_FLAGS' and +# 'LIBCXXABI_LINK_FLAGS'. +macro(add_flags) + foreach(value ${ARGN}) + list(APPEND LIBCXXABI_COMPILE_FLAGS ${value}) + list(APPEND LIBCXXABI_LINK_FLAGS ${value}) + endforeach() +endmacro() + +# If the specified 'condition' is true then add a list of flags to both +# 'LIBCXXABI_COMPILE_FLAGS' and 'LIBCXXABI_LINK_FLAGS'. +macro(add_flags_if condition) + if (${condition}) + add_flags(${ARGN}) + endif() +endmacro() + +# Add each flag in the list to LIBCXXABI_COMPILE_FLAGS and LIBCXXABI_LINK_FLAGS +# if that flag is supported by the current compiler. +macro(add_flags_if_supported) + foreach(flag ${ARGN}) + mangle_name("${flag}" flagname) + check_cxx_compiler_flag("${flag}" "LIBCXXABI_SUPPORTS_${flagname}_FLAG") + add_flags_if(LIBCXXABI_SUPPORTS_${flagname}_FLAG ${flag}) + endforeach() +endmacro() + +# Add a list of flags to 'LIBCXXABI_COMPILE_FLAGS'. +macro(add_compile_flags) + foreach(f ${ARGN}) + list(APPEND LIBCXXABI_COMPILE_FLAGS ${f}) + endforeach() +endmacro() + +# If 'condition' is true then add the specified list of flags to +# 'LIBCXXABI_COMPILE_FLAGS' +macro(add_compile_flags_if condition) + if (${condition}) + add_compile_flags(${ARGN}) + endif() +endmacro() + +# For each specified flag, add that flag to 'LIBCXXABI_COMPILE_FLAGS' if the +# flag is supported by the C++ compiler. +macro(add_compile_flags_if_supported) + foreach(flag ${ARGN}) + mangle_name("${flag}" flagname) + check_cxx_compiler_flag("${flag}" "LIBCXXABI_SUPPORTS_${flagname}_FLAG") + add_compile_flags_if(LIBCXXABI_SUPPORTS_${flagname}_FLAG ${flag}) + endforeach() +endmacro() + +# For each specified flag, add that flag to 'LIBCXXABI_COMPILE_FLAGS' if the +# flag is supported by the C compiler. +macro(add_c_compile_flags_if_supported) + foreach(flag ${ARGN}) + mangle_name("${flag}" flagname) + check_c_compiler_flag("${flag}" "LIBCXXABI_SUPPORTS_${flagname}_FLAG") + add_compile_flags_if(LIBCXXABI_SUPPORTS_${flagname}_FLAG ${flag}) + endforeach() +endmacro() + +# Add a list of flags to 'LIBCXXABI_LINK_FLAGS'. +macro(add_link_flags) + foreach(f ${ARGN}) + list(APPEND LIBCXXABI_LINK_FLAGS ${f}) + endforeach() +endmacro() + +# If 'condition' is true then add the specified list of flags to +# 'LIBCXXABI_LINK_FLAGS' +macro(add_link_flags_if condition) + if (${condition}) + add_link_flags(${ARGN}) + endif() +endmacro() + +# For each specified flag, add that flag to 'LIBCXXABI_LINK_FLAGS' if the +# flag is supported by the C++ compiler. +macro(add_link_flags_if_supported) + foreach(flag ${ARGN}) + mangle_name("${flag}" flagname) + check_cxx_compiler_flag("${flag}" "LIBCXXABI_SUPPORTS_${flagname}_FLAG") + add_link_flags_if(LIBCXXABI_SUPPORTS_${flagname}_FLAG ${flag}) + endforeach() +endmacro() + +# Add a list of libraries or link flags to 'LIBCXXABI_LIBRARIES'. +macro(add_library_flags) + foreach(lib ${ARGN}) + list(APPEND LIBCXXABI_LIBRARIES ${lib}) + endforeach() +endmacro() + +# if 'condition' is true then add the specified list of libraries and flags +# to 'LIBCXXABI_LIBRARIES'. +macro(add_library_flags_if condition) + if(${condition}) + add_library_flags(${ARGN}) + endif() +endmacro() + +# Turn a comma separated CMake list into a space separated string. +macro(split_list listname) + string(REPLACE ";" " " ${listname} "${${listname}}") +endmacro() diff --git a/cmake/Modules/HandleOutOfTreeLLVM.cmake b/cmake/Modules/HandleOutOfTreeLLVM.cmake new file mode 100644 index 0000000..ed0b527 --- /dev/null +++ b/cmake/Modules/HandleOutOfTreeLLVM.cmake @@ -0,0 +1,139 @@ +macro(find_llvm_parts) +# Rely on llvm-config. + set(CONFIG_OUTPUT) + if(NOT LLVM_CONFIG_PATH) + find_program(LLVM_CONFIG_PATH "llvm-config") + endif() + if(DEFINED LLVM_PATH) + set(LLVM_INCLUDE_DIR ${LLVM_INCLUDE_DIR} CACHE PATH "Path to llvm/include") + set(LLVM_PATH ${LLVM_PATH} CACHE PATH "Path to LLVM source tree") + set(LLVM_MAIN_SRC_DIR ${LLVM_PATH}) + set(LLVM_CMAKE_PATH "${LLVM_PATH}/cmake/modules") + elseif(LLVM_CONFIG_PATH) + message(STATUS "Found LLVM_CONFIG_PATH as ${LLVM_CONFIG_PATH}") + set(LIBCXXABI_USING_INSTALLED_LLVM 1) + set(CONFIG_COMMAND ${LLVM_CONFIG_PATH} + "--includedir" + "--prefix" + "--src-root") + execute_process( + COMMAND ${CONFIG_COMMAND} + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE CONFIG_OUTPUT + ) + if(NOT HAD_ERROR) + string(REGEX REPLACE + "[ \t]*[\r\n]+[ \t]*" ";" + CONFIG_OUTPUT ${CONFIG_OUTPUT}) + else() + string(REPLACE ";" " " CONFIG_COMMAND_STR "${CONFIG_COMMAND}") + message(STATUS "${CONFIG_COMMAND_STR}") + message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}") + endif() + + list(GET CONFIG_OUTPUT 0 INCLUDE_DIR) + list(GET CONFIG_OUTPUT 1 LLVM_OBJ_ROOT) + list(GET CONFIG_OUTPUT 2 MAIN_SRC_DIR) + + set(LLVM_INCLUDE_DIR ${INCLUDE_DIR} CACHE PATH "Path to llvm/include") + set(LLVM_BINARY_DIR ${LLVM_OBJ_ROOT} CACHE PATH "Path to LLVM build tree") + set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree") + + # --cmakedir is supported since llvm r291218 (4.0 release) + execute_process( + COMMAND ${LLVM_CONFIG_PATH} --cmakedir + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE CONFIG_OUTPUT + ERROR_QUIET) + if(NOT HAD_ERROR) + string(STRIP "${CONFIG_OUTPUT}" LLVM_CMAKE_PATH) + else() + set(LLVM_CMAKE_PATH + "${LLVM_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm") + endif() + else() + set(LLVM_FOUND OFF) + message(WARNING "UNSUPPORTED LIBCXXABI CONFIGURATION DETECTED: " + "llvm-config not found and LLVM_PATH not defined.\n" + "Reconfigure with -DLLVM_CONFIG_PATH=path/to/llvm-config " + "or -DLLVM_PATH=path/to/llvm-source-root.") + return() + endif() + + if (EXISTS "${LLVM_CMAKE_PATH}") + list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}") + elseif (EXISTS "${LLVM_MAIN_SRC_DIR}/cmake/modules") + list(APPEND CMAKE_MODULE_PATH "${LLVM_MAIN_SRC_DIR}/cmake/modules") + else() + set(LLVM_FOUND OFF) + message(WARNING "Neither ${LLVM_CMAKE_PATH} nor ${LLVM_MAIN_SRC_DIR}/cmake/modules found") + return() + endif() + + set(LLVM_FOUND ON) +endmacro(find_llvm_parts) + +macro(configure_out_of_tree_llvm) + message(STATUS "Configuring for standalone build.") + set(LIBCXXABI_STANDALONE_BUILD 1) + + find_llvm_parts() + + # Add LLVM Functions -------------------------------------------------------- + if (LLVM_FOUND AND LIBCXXABI_USING_INSTALLED_LLVM) + include(LLVMConfig) # For TARGET_TRIPLE + else() + if (WIN32) + set(LLVM_ON_UNIX 0) + set(LLVM_ON_WIN32 1) + else() + set(LLVM_ON_UNIX 1) + set(LLVM_ON_WIN32 0) + endif() + endif() + if (LLVM_FOUND) + include(AddLLVM OPTIONAL) + include(HandleLLVMOptions OPTIONAL) + endif() + + # LLVM Options -------------------------------------------------------------- + if (NOT DEFINED LLVM_INCLUDE_TESTS) + set(LLVM_INCLUDE_TESTS ${LLVM_FOUND}) + endif() + if (NOT DEFINED LLVM_INCLUDE_DOCS) + set(LLVM_INCLUDE_DOCS ${LLVM_FOUND}) + endif() + if (NOT DEFINED LLVM_ENABLE_SPHINX) + set(LLVM_ENABLE_SPHINX OFF) + endif() + + # In a standalone build, we don't have llvm to automatically generate the + # llvm-lit script for us. So we need to provide an explicit directory that + # the configurator should write the script into. + set(LLVM_LIT_OUTPUT_DIR "${libcxxabi_BINARY_DIR}/bin") + + if (LLVM_INCLUDE_TESTS) + # Required LIT Configuration ------------------------------------------------ + # Define the default arguments to use with 'lit', and an option for the user + # to override. + set(LLVM_EXTERNAL_LIT "${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py") + set(LIT_ARGS_DEFAULT "-sv --show-xfail --show-unsupported") + if (MSVC OR XCODE) + set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar") + endif() + set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit") + endif() + + # Required doc configuration + if (LLVM_ENABLE_SPHINX) + find_package(Sphinx REQUIRED) + endif() + + if (LLVM_ON_UNIX AND NOT APPLE) + set(LLVM_HAVE_LINK_VERSION_SCRIPT 1) + else() + set(LLVM_HAVE_LINK_VERSION_SCRIPT 0) + endif() +endmacro(configure_out_of_tree_llvm) + +configure_out_of_tree_llvm() diff --git a/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake b/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake new file mode 100644 index 0000000..e75002b --- /dev/null +++ b/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake @@ -0,0 +1,18 @@ +# MACRO_ENSURE_OUT_OF_SOURCE_BUILD(<errorMessage>) + +macro( MACRO_ENSURE_OUT_OF_SOURCE_BUILD _errorMessage ) + +string( COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" _insource ) +if( _insource ) + message( SEND_ERROR "${_errorMessage}" ) + message( FATAL_ERROR + "In-source builds are not allowed. + CMake would overwrite the makefiles distributed with libcxxabi. + Please create a directory and run cmake from there, passing the path + to this source directory as the last argument. + This process created the file `CMakeCache.txt' and the directory `CMakeFiles'. + Please delete them." + ) +endif( _insource ) + +endmacro( MACRO_ENSURE_OUT_OF_SOURCE_BUILD ) diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake index 6ac6152..379b554 100644 --- a/cmake/config-ix.cmake +++ b/cmake/config-ix.cmake @@ -2,17 +2,61 @@ include(CheckLibraryExists) include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) +check_library_exists(c fopen "" LIBCXXABI_HAS_C_LIB) +if (NOT LIBCXXABI_USE_COMPILER_RT) + check_library_exists(gcc_s __gcc_personality_v0 "" LIBCXXABI_HAS_GCC_S_LIB) +endif () + +# libc++abi is built with -nodefaultlibs, so we want all our checks to also +# use this option, otherwise we may end up with an inconsistency between +# the flags we think we require during configuration (if the checks are +# performed without -nodefaultlibs) and the flags that are actually +# required during compilation (which has the -nodefaultlibs). libc is +# required for the link to go through. We remove sanitizers from the +# configuration checks to avoid spurious link errors. +check_c_compiler_flag(-nodefaultlibs LIBCXXABI_HAS_NODEFAULTLIBS_FLAG) +if (LIBCXXABI_HAS_NODEFAULTLIBS_FLAG) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -nodefaultlibs") + if (LIBCXXABI_HAS_C_LIB) + list(APPEND CMAKE_REQUIRED_LIBRARIES c) + endif () + if (LIBCXXABI_USE_COMPILER_RT) + list(APPEND CMAKE_REQUIRED_FLAGS -rtlib=compiler-rt) + find_compiler_rt_library(builtins LIBCXXABI_BUILTINS_LIBRARY) + list(APPEND CMAKE_REQUIRED_LIBRARIES "${LIBCXXABI_BUILTINS_LIBRARY}") + elseif (LIBCXXABI_HAS_GCC_S_LIB) + list(APPEND CMAKE_REQUIRED_LIBRARIES gcc_s) + endif () + if (MINGW) + # Mingw64 requires quite a few "C" runtime libraries in order for basic + # programs to link successfully with -nodefaultlibs. + if (LIBCXXABI_USE_COMPILER_RT) + set(MINGW_RUNTIME ${LIBCXXABI_BUILTINS_LIBRARY}) + else () + set(MINGW_RUNTIME gcc_s gcc) + endif() + set(MINGW_LIBRARIES mingw32 ${MINGW_RUNTIME} moldname mingwex msvcrt advapi32 + shell32 user32 kernel32 mingw32 ${MINGW_RUNTIME} + moldname mingwex msvcrt) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${MINGW_LIBRARIES}) + endif() + if (CMAKE_C_FLAGS MATCHES -fsanitize OR CMAKE_CXX_FLAGS MATCHES -fsanitize) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-sanitize=all") + endif () + if (CMAKE_C_FLAGS MATCHES -fsanitize-coverage OR CMAKE_CXX_FLAGS MATCHES -fsanitize-coverage) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters") + endif () +endif () + # Check compiler flags check_c_compiler_flag(-funwind-tables LIBCXXABI_HAS_FUNWIND_TABLES) -check_cxx_compiler_flag(-fPIC LIBCXXABI_HAS_FPIC_FLAG) check_cxx_compiler_flag(-fno-exceptions LIBCXXABI_HAS_NO_EXCEPTIONS_FLAG) check_cxx_compiler_flag(-fno-rtti LIBCXXABI_HAS_NO_RTTI_FLAG) check_cxx_compiler_flag(-fstrict-aliasing LIBCXXABI_HAS_FSTRICT_ALIASING_FLAG) -check_cxx_compiler_flag(-nodefaultlibs LIBCXXABI_HAS_NODEFAULTLIBS_FLAG) check_cxx_compiler_flag(-nostdinc++ LIBCXXABI_HAS_NOSTDINCXX_FLAG) check_cxx_compiler_flag(-Wall LIBCXXABI_HAS_WALL_FLAG) check_cxx_compiler_flag(-W LIBCXXABI_HAS_W_FLAG) -check_cxx_compiler_flag(-Wno-unused-function LIBCXXABI_HAS_WNO_UNUSED_FUNCTION_FLAG) +check_cxx_compiler_flag(-Wunused-function LIBCXXABI_HAS_WUNUSED_FUNCTION_FLAG) check_cxx_compiler_flag(-Wunused-variable LIBCXXABI_HAS_WUNUSED_VARIABLE_FLAG) check_cxx_compiler_flag(-Wunused-parameter LIBCXXABI_HAS_WUNUSED_PARAMETER_FLAG) check_cxx_compiler_flag(-Wstrict-aliasing LIBCXXABI_HAS_WSTRICT_ALIASING_FLAG) @@ -37,18 +81,9 @@ check_cxx_compiler_flag(/EHsc LIBCXXABI_HAS_EHSC_FLAG) check_cxx_compiler_flag(/EHs- LIBCXXABI_HAS_NO_EHS_FLAG) check_cxx_compiler_flag(/EHa- LIBCXXABI_HAS_NO_EHA_FLAG) check_cxx_compiler_flag(/GR- LIBCXXABI_HAS_NO_GR_FLAG) -check_cxx_compiler_flag(-std=c++11 LIBCXXABI_HAS_STD_CXX11) - -if(LIBCXXABI_HAS_STD_CXX11) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") -endif() # Check libraries -check_library_exists(c fopen "" LIBCXXABI_HAS_C_LIB) check_library_exists(dl dladdr "" LIBCXXABI_HAS_DL_LIB) check_library_exists(pthread pthread_once "" LIBCXXABI_HAS_PTHREAD_LIB) -if (NOT LIBCXXABI_USE_COMPILER_RT) - check_library_exists(gcc_s __gcc_personality_v0 "" LIBCXXABI_HAS_GCC_S_LIB) -endif () check_library_exists(c __cxa_thread_atexit_impl "" LIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL) diff --git a/include/__cxxabi_config.h b/include/__cxxabi_config.h index 2c58a22..65b1961 100644 --- a/include/__cxxabi_config.h +++ b/include/__cxxabi_config.h @@ -12,9 +12,7 @@ #if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \ !defined(__ARM_DWARF_EH__) -#define LIBCXXABI_ARM_EHABI 1 -#else -#define LIBCXXABI_ARM_EHABI 0 +#define _LIBCXXABI_ARM_EHABI #endif #if !defined(__has_attribute) @@ -56,4 +54,10 @@ #endif #endif +#if defined(_WIN32) +#define _LIBCXXABI_WEAK +#else +#define _LIBCXXABI_WEAK __attribute__((__weak__)) +#endif + #endif // ____CXXABI_CONFIG_H diff --git a/include/cxxabi.h b/include/cxxabi.h index e4a6797..2596560 100644 --- a/include/cxxabi.h +++ b/include/cxxabi.h @@ -21,7 +21,7 @@ #include <__cxxabi_config.h> #define _LIBCPPABI_VERSION 1002 -#define LIBCXXABI_NORETURN __attribute__((noreturn)) +#define _LIBCXXABI_NORETURN __attribute__((noreturn)) #ifdef __cplusplus @@ -45,7 +45,7 @@ extern _LIBCXXABI_FUNC_VIS void __cxa_free_exception(void *thrown_exception) throw(); // 2.4.3 Throwing the Exception Object -extern _LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void +extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_throw(void *thrown_exception, std::type_info *tinfo, void (*dest)(void *)); @@ -55,7 +55,7 @@ __cxa_get_exception_ptr(void *exceptionObject) throw(); extern _LIBCXXABI_FUNC_VIS void * __cxa_begin_catch(void *exceptionObject) throw(); extern _LIBCXXABI_FUNC_VIS void __cxa_end_catch(); -#if LIBCXXABI_ARM_EHABI +#if defined(_LIBCXXABI_ARM_EHABI) extern _LIBCXXABI_FUNC_VIS bool __cxa_begin_cleanup(void *exceptionObject) throw(); extern _LIBCXXABI_FUNC_VIS void __cxa_end_cleanup(); @@ -63,19 +63,19 @@ extern _LIBCXXABI_FUNC_VIS void __cxa_end_cleanup(); extern _LIBCXXABI_FUNC_VIS std::type_info *__cxa_current_exception_type(); // 2.5.4 Rethrowing Exceptions -extern _LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void __cxa_rethrow(); +extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_rethrow(); // 2.6 Auxiliary Runtime APIs -extern _LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void __cxa_bad_cast(void); -extern _LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void __cxa_bad_typeid(void); -extern _LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void +extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_cast(void); +extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_typeid(void); +extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_throw_bad_array_new_length(void); // 3.2.6 Pure Virtual Function API -extern _LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void __cxa_pure_virtual(void); +extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_pure_virtual(void); // 3.2.7 Deleted Virtual Function API -extern _LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void __cxa_deleted_virtual(void); +extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_deleted_virtual(void); // 3.3.2 One-time Construction API #ifdef __arm__ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index efbea67..5a929cf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,7 +7,6 @@ set(LIBCXXABI_SOURCES cxa_exception_storage.cpp cxa_guard.cpp cxa_handlers.cpp - cxa_new_delete.cpp cxa_unexpected.cpp cxa_vector.cpp cxa_virtual.cpp @@ -21,6 +20,10 @@ set(LIBCXXABI_SOURCES private_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) @@ -49,32 +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) # 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 (TARGET unwind_shared) - list(APPEND libraries unwind_shared) - elseif(TARGET unwind_static) - list(APPEND libraries unwind_static) + 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 libraries unwind) + 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) @@ -94,9 +97,9 @@ 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. @@ -106,54 +109,98 @@ 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}) + if(COMMAND llvm_setup_rpath) + llvm_setup_rpath(cxxabi_shared) + endif() + 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 cxxabi - ARCHIVE DESTINATION lib${LIBCXXABI_LIBDIR_SUFFIX} COMPONENT cxxabi - ) +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) +if (NOT CMAKE_CONFIGURATION_TYPES AND LIBCXXABI_INSTALL_LIBRARY) add_custom_target(install-cxxabi DEPENDS cxxabi COMMAND "${CMAKE_COMMAND}" -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) diff --git a/src/abort_message.cpp b/src/abort_message.cpp index 5e25c0f..7a2a9f8 100644 --- a/src/abort_message.cpp +++ b/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/src/abort_message.h b/src/abort_message.h index 2c5cb20..e8f9571 100644 --- a/src/abort_message.h +++ b/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/src/config.h b/src/config.h deleted file mode 100644 index 72a05e0..0000000 --- a/src/config.h +++ /dev/null @@ -1,33 +0,0 @@ -//===----------------------------- config.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. -// -// -// Defines macros used within the libc++abi project. -// -//===----------------------------------------------------------------------===// - - -#ifndef LIBCXXABI_CONFIG_H -#define LIBCXXABI_CONFIG_H - -#include <unistd.h> - -// Set this in the CXXFLAGS when you need it, because otherwise we'd have to -// #if !defined(__linux__) && !defined(__APPLE__) && ... -// and so-on for *every* platform. -#ifndef LIBCXXABI_BAREMETAL -# define LIBCXXABI_BAREMETAL 0 -#endif - -// The default terminate handler attempts to demangle uncaught exceptions, which -// causes extra I/O and demangling code to be pulled in. -// Set this to make the terminate handler default to a silent alternative. -#ifndef LIBCXXABI_SILENT_TERMINATE -# define LIBCXXABI_SILENT_TERMINATE 0 -#endif - -#endif // LIBCXXABI_CONFIG_H diff --git a/src/cxa_aux_runtime.cpp b/src/cxa_aux_runtime.cpp index bb7c9f1..878d3bd 100644 --- a/src/cxa_aux_runtime.cpp +++ b/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/src/cxa_default_handlers.cpp b/src/cxa_default_handlers.cpp index a1ea296..8231139 100644 --- a/src/cxa_default_handlers.cpp +++ b/src/cxa_default_handlers.cpp @@ -14,12 +14,12 @@ #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" #include "private_typeinfo.h" +#if !defined(LIBCXXABI_SILENT_TERMINATE) static const char* cause = "uncaught"; __attribute__((noreturn)) @@ -85,12 +85,11 @@ static void demangling_unexpected_handler() std::terminate(); } -#if !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::abort; +static std::terminate_handler default_unexpected_handler = std::terminate; #endif // diff --git a/src/cxa_demangle.cpp b/src/cxa_demangle.cpp index 8f5cb30..01724cc 100644 --- a/src/cxa_demangle.cpp +++ b/src/cxa_demangle.cpp @@ -7,15 +7,21 @@ // //===----------------------------------------------------------------------===// -#define _LIBCPP_EXTERN_TEMPLATE(...) +// FIXME: (possibly) incomplete list of features that clang mangles that this +// file does not yet support: +// - enable_if attribute +// - C++ modules TS +// - All C++14 and C++17 features + #define _LIBCPP_NO_EXCEPTIONS #include "__cxxabi_config.h" #include <vector> #include <algorithm> -#include <string> #include <numeric> +#include <cassert> +#include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> @@ -27,89 +33,3264 @@ #endif #endif -namespace __cxxabiv1 -{ +#ifndef NDEBUG +#if __has_attribute(noinline) && __has_attribute(used) +#define DUMP_METHOD __attribute__((noinline,used)) +#else +#define DUMP_METHOD +#endif +#endif -namespace -{ +namespace { -enum -{ - unknown_error = -4, - invalid_args = -3, - invalid_mangled_name, - memory_alloc_failure, - success +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 empty() const { return First == Last; } }; -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) -{ - 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) - { - 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; +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)); } - fprintf(stderr, "template_param:\n"); - for (auto& t : db.template_param) - { - 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; - } + } + +public: + OutputStream(char *StartBuf, size_t Size) + : Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {} + + /// If a ParameterPackExpansion (or similar type) is encountered, the offset + /// into the pack that we're currently printing. + unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max(); + + 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; + } + + size_t getCurrentPosition() const { return CurrentPosition; }; + + 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; } +}; + +template <class T> +class SwapAndRestore { + T &Restore; + T OriginalValue; +public: + SwapAndRestore(T& Restore_, T NewVal) + : Restore(Restore_), OriginalValue(Restore) { + Restore = std::move(NewVal); + } + ~SwapAndRestore() { Restore = std::move(OriginalValue); } + + SwapAndRestore(const SwapAndRestore &) = delete; + SwapAndRestore &operator=(const SwapAndRestore &) = delete; +}; + +// 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, + KFunctionEncoding, + KFunctionQualType, + KFunctionRefQualType, + KLiteralOperator, + KSpecialName, + KCtorVtableSpecialName, + KQualifiedName, + KEmptyName, + KVectorType, + KParameterPack, + KTemplateArgumentPack, + KParameterPackExpansion, + KTemplateArgs, + KNameWithTemplateArgs, + KGlobalQualifiedName, + KStdQualifiedName, + KExpandedSpecialSubstitution, + KSpecialSubstitution, + KCtorDtorName, + KDtorName, + KUnnamedTypeName, + KLambdaTypeName, + KExpr, + }; + + static constexpr unsigned NoParameterPack = + std::numeric_limits<unsigned>::max(); + unsigned ParameterPackSize = NoParameterPack; + + Kind K; + + /// Three-way bool to track a cached value. Unknown is possible if this node + /// has an unexpanded parameter pack below it that may affect this cache. + enum class Cache : unsigned char { Yes, No, Unknown, }; + + /// Tracks if this node has a component on its right side, in which case we + /// need to call printRight. + Cache RHSComponentCache; + + /// Track if this node is a (possibly qualified) array type. This can affect + /// how we format the output string. + Cache ArrayCache; + + /// Track if this node is a (possibly qualified) function type. This can + /// affect how we format the output string. + Cache FunctionCache; + + Node(Kind K_, unsigned ParameterPackSize_ = NoParameterPack, + Cache RHSComponentCache_ = Cache::No, Cache ArrayCache_ = Cache::No, + Cache FunctionCache_ = Cache::No) + : ParameterPackSize(ParameterPackSize_), K(K_), + RHSComponentCache(RHSComponentCache_), ArrayCache(ArrayCache_), + FunctionCache(FunctionCache_) {} + + bool containsUnexpandedParameterPack() const { + return ParameterPackSize != NoParameterPack; + } + + bool hasRHSComponent(OutputStream &S) const { + if (RHSComponentCache != Cache::Unknown) + return RHSComponentCache == Cache::Yes; + return hasRHSComponentSlow(S); + } + + bool hasArray(OutputStream &S) const { + if (ArrayCache != Cache::Unknown) + return ArrayCache == Cache::Yes; + return hasArraySlow(S); + } + + bool hasFunction(OutputStream &S) const { + if (FunctionCache != Cache::Unknown) + return FunctionCache == Cache::Yes; + return hasFunctionSlow(S); + } + + Kind getKind() const { return K; } + + virtual bool hasRHSComponentSlow(OutputStream &) const { return false; } + virtual bool hasArraySlow(OutputStream &) const { return false; } + virtual bool hasFunctionSlow(OutputStream &) const { return false; } + + /// If this node is a pack expansion that expands to 0 elements. This can have + /// an effect on how we should format the output. + bool isEmptyPackExpansion() const; + + void print(OutputStream &S) const { + printLeft(S); + if (RHSComponentCache != Cache::No) + printRight(S); + } + + // Print the "left" side of this Node into OutputStream. + virtual void printLeft(OutputStream &) const = 0; + + // Print the "right". This distinction is necessary to represent C++ types + // that appear on the RHS of their subtype, such as arrays or functions. + // Since most types don't have such a component, provide a default + // implemenation. + virtual void printRight(OutputStream &) const {} + + virtual StringView getBaseName() const { return StringView(); } + + // Silence compiler warnings, this dtor will never be called. + virtual ~Node() = default; + +#ifndef NDEBUG + DUMP_METHOD void dump() const { + char *Buffer = static_cast<char*>(std::malloc(1024)); + OutputStream S(Buffer, 1024); + print(S); + S += '\0'; + printf("Symbol dump for %p: %s\n", (const void*)this, S.getBuffer()); + std::free(S.getBuffer()); + } +#endif +}; + +class NodeArray { + Node **Elements; + size_t NumElements; + +public: + NodeArray() : Elements(nullptr), NumElements(0) {} + NodeArray(Node **Elements_, size_t NumElements_) + : Elements(Elements_), NumElements(NumElements_) {} + + bool empty() const { return NumElements == 0; } + size_t size() const { return NumElements; } + + Node **begin() const { return Elements; } + Node **end() const { return Elements + NumElements; } + + Node *operator[](size_t Idx) const { return Elements[Idx]; } + + void printWithComma(OutputStream &S) const { + bool FirstElement = true; + for (size_t Idx = 0; Idx != NumElements; ++Idx) { + if (Elements[Idx]->isEmptyPackExpansion()) + continue; + if (!FirstElement) + S += ", "; + FirstElement = false; + Elements[Idx]->print(S); } - fprintf(stderr, "---------\n\n"); + } +}; + +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 *Ty; + StringView Ext; + +public: + VendorExtQualType(Node *Ty_, StringView Ext_) + : Node(KVendorExtQualType, Ty_->ParameterPackSize), + Ty(Ty_), Ext(Ext_) {} + + void printLeft(OutputStream &S) const override { + Ty->print(S); + S += " "; + S += Ext; + } +}; + +enum Qualifiers { + QualNone = 0, + QualConst = 0x1, + QualVolatile = 0x2, + QualRestrict = 0x4, +}; + +void addQualifiers(Qualifiers &Q1, Qualifiers Q2) { + Q1 = static_cast<Qualifiers>(Q1 | Q2); } -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); +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_->ParameterPackSize, Child_->RHSComponentCache, + Child_->ArrayCache, Child_->FunctionCache), + Quals(Quals_), Child(Child_) {} + + bool hasRHSComponentSlow(OutputStream &S) const override { + return Child->hasRHSComponent(S); + } + bool hasArraySlow(OutputStream &S) const override { + return Child->hasArray(S); + } + bool hasFunctionSlow(OutputStream &S) const override { + return Child->hasFunction(S); + } + + void printLeft(OutputStream &S) const override { + Child->printLeft(S); + printQuals(S); + } + + void printRight(OutputStream &S) const override { Child->printRight(S); } +}; + +class ConversionOperatorType final : public Node { + const Node *Ty; + +public: + ConversionOperatorType(Node *Ty_) + : Node(KConversionOperatorType, Ty_->ParameterPackSize), 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_->ParameterPackSize), + Ty(Ty_), Postfix(Postfix_) {} + + void printLeft(OutputStream &s) const override { + Ty->printLeft(s); + s += Postfix; + } +}; + +class NameType final : public Node { + const StringView Name; + +public: + NameType(StringView Name_) : Node(KNameType), Name(Name_) {} + + 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_->ParameterPackSize, Base_->RHSComponentCache, + Base_->ArrayCache, Base_->FunctionCache), + Base(Base_), Tag(Tag_) {} + + void printLeft(OutputStream &S) const override { + Base->printLeft(S); + S += "[abi:"; + S += Tag; + S += "]"; + } +}; + +class ObjCProtoName : public Node { + Node *Ty; + StringView Protocol; + + friend class PointerType; + +public: + ObjCProtoName(Node *Ty_, StringView Protocol_) + : Node(KObjCProtoName), Ty(Ty_), Protocol(Protocol_) {} + + bool isObjCObject() const { + return Ty->getKind() == KNameType && + static_cast<NameType *>(Ty)->getName() == "objc_object"; + } + + void printLeft(OutputStream &S) const override { + Ty->print(S); + S += "<"; + S += Protocol; + S += ">"; + } +}; + +class PointerType final : public Node { + const Node *Pointee; + +public: + PointerType(Node *Pointee_) + : Node(KPointerType, Pointee_->ParameterPackSize, + Pointee_->RHSComponentCache), + Pointee(Pointee_) {} + + bool hasRHSComponentSlow(OutputStream &S) const override { + return Pointee->hasRHSComponent(S); + } + + void printLeft(OutputStream &s) const override { + // We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>. + if (Pointee->getKind() != KObjCProtoName || + !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) { + Pointee->printLeft(s); + if (Pointee->hasArray(s)) + s += " "; + if (Pointee->hasArray(s) || Pointee->hasFunction(s)) + s += "("; + s += "*"; + } else { + const auto *objcProto = static_cast<const ObjCProtoName *>(Pointee); + s += "id<"; + s += objcProto->Protocol; + s += ">"; + } + } + + void printRight(OutputStream &s) const override { + if (Pointee->getKind() != KObjCProtoName || + !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) { + if (Pointee->hasArray(s) || Pointee->hasFunction(s)) + s += ")"; + Pointee->printRight(s); + } + } +}; + +class LValueReferenceType final : public Node { + const Node *Pointee; + +public: + LValueReferenceType(Node *Pointee_) + : Node(KLValueReferenceType, Pointee_->ParameterPackSize, + Pointee_->RHSComponentCache), + Pointee(Pointee_) {} + + bool hasRHSComponentSlow(OutputStream &S) const override { + return Pointee->hasRHSComponent(S); + } + + void printLeft(OutputStream &s) const override { + Pointee->printLeft(s); + if (Pointee->hasArray(s)) + s += " "; + if (Pointee->hasArray(s) || Pointee->hasFunction(s)) + s += "(&"; + else + s += "&"; + } + void printRight(OutputStream &s) const override { + if (Pointee->hasArray(s) || Pointee->hasFunction(s)) + s += ")"; + Pointee->printRight(s); + } +}; + +class RValueReferenceType final : public Node { + const Node *Pointee; + +public: + RValueReferenceType(Node *Pointee_) + : Node(KRValueReferenceType, Pointee_->ParameterPackSize, + Pointee_->RHSComponentCache), + Pointee(Pointee_) {} + + bool hasRHSComponentSlow(OutputStream &S) const override { + return Pointee->hasRHSComponent(S); + } + + void printLeft(OutputStream &s) const override { + Pointee->printLeft(s); + if (Pointee->hasArray(s)) + s += " "; + if (Pointee->hasArray(s) || Pointee->hasFunction(s)) + s += "(&&"; + else + s += "&&"; + } + + void printRight(OutputStream &s) const override { + if (Pointee->hasArray(s) || Pointee->hasFunction(s)) + s += ")"; + Pointee->printRight(s); + } +}; + +class PointerToMemberType final : public Node { + const Node *ClassType; + const Node *MemberType; + +public: + PointerToMemberType(Node *ClassType_, Node *MemberType_) + : Node(KPointerToMemberType, + std::min(MemberType_->ParameterPackSize, + ClassType_->ParameterPackSize), + MemberType_->RHSComponentCache), + ClassType(ClassType_), MemberType(MemberType_) {} + + bool hasRHSComponentSlow(OutputStream &S) const override { + return MemberType->hasRHSComponent(S); + } + + void printLeft(OutputStream &s) const override { + MemberType->printLeft(s); + if (MemberType->hasArray(s) || MemberType->hasFunction(s)) + s += "("; + else + s += " "; + ClassType->print(s); + s += "::*"; + } + + void printRight(OutputStream &s) const override { + if (MemberType->hasArray(s) || MemberType->hasFunction(s)) + s += ")"; + MemberType->printRight(s); + } +}; + +class 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, Base_->ParameterPackSize, + /*RHSComponentCache=*/Cache::Yes, + /*ArrayCache=*/Cache::Yes), + Base(Base_), Dimension(Dimension_) { + if (Dimension.isNode()) + ParameterPackSize = + std::min(ParameterPackSize, Dimension.asNode()->ParameterPackSize); + } + + // Incomplete array type. + ArrayType(Node *Base_) + : Node(KArrayType, Base_->ParameterPackSize, + /*RHSComponentCache=*/Cache::Yes, + /*ArrayCache=*/Cache::Yes), + Base(Base_) {} + + bool hasRHSComponentSlow(OutputStream &) const override { return true; } + bool hasArraySlow(OutputStream &) const override { return true; } + + void printLeft(OutputStream &S) const override { Base->printLeft(S); } + + void printRight(OutputStream &S) const override { + if (S.back() != ']') + S += " "; + S += "["; + if (Dimension.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, Ret_->ParameterPackSize, + /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No, + /*FunctionCache=*/Cache::Yes), + Ret(Ret_), Params(Params_) { + for (Node *P : Params) + ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize); + } + + bool hasRHSComponentSlow(OutputStream &) const override { return true; } + bool hasFunctionSlow(OutputStream &) const override { return true; } + + // 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.printWithComma(S); + S += ")"; + Ret->printRight(S); + } +}; + +class FunctionEncoding final : public Node { + const Node *Ret; + const Node *Name; + NodeArray Params; + +public: + FunctionEncoding(Node *Ret_, Node *Name_, NodeArray Params_) + : Node(KFunctionEncoding, NoParameterPack, + /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No, + /*FunctionCache=*/Cache::Yes), + Ret(Ret_), Name(Name_), Params(Params_) { + for (Node *P : Params) + ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize); + if (Ret) + ParameterPackSize = std::min(ParameterPackSize, Ret->ParameterPackSize); + } + + bool hasRHSComponentSlow(OutputStream &) const override { return true; } + bool hasFunctionSlow(OutputStream &) const override { return true; } + + Node *getName() { return const_cast<Node *>(Name); } + + void printLeft(OutputStream &S) const override { + if (Ret) { + Ret->printLeft(S); + if (!Ret->hasRHSComponent(S)) + S += " "; + } + Name->print(S); + } + + void printRight(OutputStream &S) const override { + S += "("; + Params.printWithComma(S); + S += ")"; + if (Ret) + Ret->printRight(S); + } +}; + +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, Fn_->ParameterPackSize, + /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No, + /*FunctionCache=*/Cache::Yes), + Fn(Fn_), Quals(Quals_) {} + + bool hasFunctionSlow(OutputStream &) const override { return true; } + bool hasRHSComponentSlow(OutputStream &) const override { return true; } + + 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(Child_, Quals_) { + K = KFunctionQualType; + } + + void printLeft(OutputStream &S) const override { Child->printLeft(S); } + + void printRight(OutputStream &S) const override { + if (Child->getKind() == 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_->ParameterPackSize), 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, Child_->ParameterPackSize), 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, std::min(FirstType_->ParameterPackSize, + SecondType_->ParameterPackSize)), + 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; + +public: + QualifiedName(Node* Qualifier_, Node* Name_) + : Node(KQualifiedName, + std::min(Qualifier_->ParameterPackSize, Name_->ParameterPackSize)), + Qualifier(Qualifier_), Name(Name_) {} + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputStream &S) const override { + Qualifier->print(S); + S += "::"; + Name->print(S); + } +}; + +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) { + if (Dimension.isNode()) + ParameterPackSize = Dimension.asNode()->ParameterPackSize; + } + VectorType(Node *BaseType_, NodeOrString Dimension_) + : Node(KVectorType, BaseType_->ParameterPackSize), BaseType(BaseType_), + Dimension(Dimension_), IsPixel(false) { + if (Dimension.isNode()) + ParameterPackSize = + std::min(ParameterPackSize, Dimension.asNode()->ParameterPackSize); + } + + 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 += "]"; + } + } +}; + +/// An unexpanded parameter pack (either in the expression or type context). If +/// this AST is correct, this node will have a ParameterPackExpansion node above +/// it. +/// +/// This node is created when some <template-args> are found that apply to an +/// <encoding>, and is stored in the TemplateParams table. In order for this to +/// appear in the final AST, it has to referenced via a <template-param> (ie, +/// T_). +class ParameterPack final : public Node { + NodeArray Data; +public: + ParameterPack(NodeArray Data_) + : Node(KParameterPack, static_cast<unsigned>(Data_.size())), Data(Data_) { + ArrayCache = FunctionCache = RHSComponentCache = Cache::Unknown; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { + return P->ArrayCache == Cache::No; + })) + ArrayCache = Cache::No; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { + return P->FunctionCache == Cache::No; + })) + FunctionCache = Cache::No; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { + return P->RHSComponentCache == Cache::No; + })) + RHSComponentCache = Cache::No; + } + + bool hasRHSComponentSlow(OutputStream &S) const override { + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasRHSComponent(S); + } + bool hasArraySlow(OutputStream &S) const override { + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasArray(S); + } + bool hasFunctionSlow(OutputStream &S) const override { + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasFunction(S); + } + + void printLeft(OutputStream &S) const override { + size_t Idx = S.CurrentPackIndex; + if (Idx < Data.size()) + Data[Idx]->printLeft(S); + } + void printRight(OutputStream &S) const override { + size_t Idx = S.CurrentPackIndex; + if (Idx < Data.size()) + Data[Idx]->printRight(S); + } +}; + +/// A variadic template argument. This node represents an occurance of +/// J<something>E in some <template-args>. It isn't itself unexpanded, unless +/// one of it's Elements is. The parser inserts a ParameterPack into the +/// TemplateParams table if the <template-args> this pack belongs to apply to an +/// <encoding>. +class TemplateArgumentPack final : public Node { + NodeArray Elements; +public: + TemplateArgumentPack(NodeArray Elements_) + : Node(KTemplateArgumentPack), Elements(Elements_) { + for (Node *E : Elements) + ParameterPackSize = std::min(E->ParameterPackSize, ParameterPackSize); + } + + NodeArray getElements() const { return Elements; } + + void printLeft(OutputStream &S) const override { + Elements.printWithComma(S); + } +}; + +/// A pack expansion. Below this node, there are some unexpanded ParameterPacks +/// which each have Child->ParameterPackSize elements. +class ParameterPackExpansion final : public Node { + const Node *Child; + +public: + ParameterPackExpansion(Node* Child_) + : Node(KParameterPackExpansion), Child(Child_) {} + + const Node *getChild() const { return Child; } + + void printLeft(OutputStream &S) const override { + unsigned PackSize = Child->ParameterPackSize; + if (PackSize == NoParameterPack) { + Child->print(S); + S += "..."; + return; + } + + SwapAndRestore<unsigned> SavePackIndex(S.CurrentPackIndex, 0); + for (unsigned I = 0; I != PackSize; ++I) { + if (I != 0) + S += ", "; + S.CurrentPackIndex = I; + Child->print(S); + } + } +}; + +inline bool Node::isEmptyPackExpansion() const { + if (getKind() == KParameterPackExpansion) { + auto *AsPack = static_cast<const ParameterPackExpansion *>(this); + return AsPack->getChild()->isEmptyPackExpansion(); + } + if (getKind() == KTemplateArgumentPack) { + auto *AsTemplateArg = static_cast<const TemplateArgumentPack *>(this); + for (Node *E : AsTemplateArg->getElements()) + if (!E->isEmptyPackExpansion()) + return false; + return true; + } + return ParameterPackSize == 0; +} + +class TemplateArgs final : public Node { + NodeArray Params; + +public: + TemplateArgs(NodeArray Params_) : Node(KTemplateArgs), Params(Params_) { + for (Node *P : Params) + ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize); + } + + NodeArray getParams() { return Params; } + + void printLeft(OutputStream &S) const override { + S += "<"; + bool FirstElement = true; + for (size_t Idx = 0, E = Params.size(); Idx != E; ++Idx) { + if (Params[Idx]->isEmptyPackExpansion()) + continue; + if (!FirstElement) + S += ", "; + FirstElement = false; + Params[Idx]->print(S); + } + if (S.back() == '>') + S += " "; + S += ">"; + } +}; + +class NameWithTemplateArgs final : public Node { + // name<template_args> + Node *Name; + Node *TemplateArgs; + +public: + NameWithTemplateArgs(Node *Name_, Node *TemplateArgs_) + : Node(KNameWithTemplateArgs, std::min(Name_->ParameterPackSize, + TemplateArgs_->ParameterPackSize)), + 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_->ParameterPackSize), 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_->ParameterPackSize), 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_->ParameterPackSize), + 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_) { + ParameterPackSize = Base->ParameterPackSize; + } + + 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_) { + for (Node *P : Params) + ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize); + } + + void printLeft(OutputStream &S) const override { + S += "\'lambda"; + S += Count; + S += "\'("; + Params.printWithComma(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_) { + ParameterPackSize = + std::min(LHS->ParameterPackSize, RHS->ParameterPackSize); + } + + 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_) { + ParameterPackSize = + std::min(Op1->ParameterPackSize, Op2->ParameterPackSize); + } + + 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_) { + ParameterPackSize = Child->ParameterPackSize; + } + + 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_) { + ParameterPackSize = + std::min(Cond->ParameterPackSize, + std::min(Then->ParameterPackSize, Else->ParameterPackSize)); + } + + 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_) { + ParameterPackSize = + std::min(LHS->ParameterPackSize, RHS->ParameterPackSize); + } + + 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_) { + ParameterPackSize = Infix->ParameterPackSize; + } + + 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_) { + ParameterPackSize = + std::min(To->ParameterPackSize, From->ParameterPackSize); + } + + void printLeft(OutputStream &S) const override { + S += CastKind; + S += "<"; + To->printLeft(S); + S += ">("; + From->printLeft(S); + S += ")"; + } +}; + +class SizeofParamPackExpr : public Expr { + Node *Pack; + +public: + SizeofParamPackExpr(Node *Pack_) : Pack(Pack_) {} + + void printLeft(OutputStream &S) const override { + S += "sizeof...("; + ParameterPackExpansion PPE(Pack); + PPE.printLeft(S); + S += ")"; + } +}; + +class CallExpr : public Expr { + const Node *Callee; + NodeArray Args; + +public: + CallExpr(Node *Callee_, NodeArray Args_) : Callee(Callee_), Args(Args_) { + for (Node *P : Args) + ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize); + ParameterPackSize = std::min(ParameterPackSize, Callee->ParameterPackSize); + } + + void printLeft(OutputStream &S) const override { + Callee->print(S); + S += "("; + Args.printWithComma(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_) { + for (Node *E : ExprList) + ParameterPackSize = std::min(ParameterPackSize, E->ParameterPackSize); + for (Node *I : InitList) + ParameterPackSize = std::min(ParameterPackSize, I->ParameterPackSize); + if (Type) + ParameterPackSize = std::min(ParameterPackSize, Type->ParameterPackSize); + } + + void printLeft(OutputStream &S) const override { + if (IsGlobal) + S += "::operator "; + S += "new"; + if (IsArray) + S += "[]"; + S += ' '; + if (!ExprList.empty()) { + S += "("; + ExprList.printWithComma(S); + S += ")"; + } + Type->print(S); + if (!InitList.empty()) { + S += "("; + InitList.printWithComma(S); + S += ")"; + } + + } +}; + +class DeleteExpr : public Expr { + Node *Op; + bool IsGlobal; + bool IsArray; + +public: + DeleteExpr(Node *Op_, bool IsGlobal_, bool IsArray_) + : Op(Op_), IsGlobal(IsGlobal_), IsArray(IsArray_) { + ParameterPackSize = Op->ParameterPackSize; + } + + 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_) { + ParameterPackSize = Child->ParameterPackSize; + } + + 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 ConversionExpr : public Expr { + const Node *Type; + NodeArray Expressions; + +public: + ConversionExpr(const Node *Type_, NodeArray Expressions_) + : Type(Type_), Expressions(Expressions_) { + for (Node *E : Expressions) + ParameterPackSize = std::min(ParameterPackSize, E->ParameterPackSize); + ParameterPackSize = std::min(ParameterPackSize, Type->ParameterPackSize); + } + + void printLeft(OutputStream &S) const override { + S += "("; + Type->print(S); + S += ")("; + Expressions.printWithComma(S); + S += ")"; + } +}; + +class ThrowExpr : public Expr { + const Node *Op; + +public: + ThrowExpr(Node *Op_) : Op(Op_) { + ParameterPackSize = Op->ParameterPackSize; + } + + 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_) { + ParameterPackSize = Ty->ParameterPackSize; + } + + 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); + } +}; + +struct Db { + const char *First; + const char *Last; + + // 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 44nd entry (base-36) in this + // table. + PODSmallVector<Node *, 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. + PODSmallVector<Node *, 8> 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) { + return new (ASTAllocator.allocate(sizeof(T))) + T(std::forward<Args>(args)...); + } + + template <class It> NodeArray makeNodeArray(It begin, It end) { + size_t sz = static_cast<size_t>(end - begin); + void *mem = ASTAllocator.allocate(sizeof(Node *) * sz); + Node **data = new (mem) Node *[sz]; + std::copy(begin, end, data); + return NodeArray(data, sz); + } + + NodeArray popTrailingNodeArray(size_t FromPosition) { + assert(FromPosition <= Names.size()); + NodeArray res = + makeNodeArray(Names.begin() + (long)FromPosition, Names.end()); + Names.dropBack(FromPosition); + return res; + } + + bool consumeIf(StringView S) { + if (StringView(First, Last).startsWith(S)) { + First += S.size(); + return true; + } + return false; + } + + bool consumeIf(char C) { + if (First != Last && *First == C) { + ++First; + return true; + } + return false; + } + + char consume() { return First != Last ? *First++ : '\0'; } + + char look(unsigned Lookahead = 0) { + if (static_cast<size_t>(Last - First) <= Lookahead) + return '\0'; + return First[Lookahead]; + } + + size_t numLeft() const { return static_cast<size_t>(Last - First); } + + StringView parseNumber(bool AllowNegative = false); + Qualifiers parseCVQualifiers(); + bool parsePositiveInteger(size_t *Out); + StringView parseBareSourceName(); + + /// Parse the <expr> production. + Node *parseExpr(); + Node *parsePrefixExpr(StringView Kind); + Node *parseBinaryExpr(StringView Kind); + Node *parseIntegerLiteral(StringView Lit); + Node *parseExprPrimary(); + template <class Float> Node *parseFloatingLiteral(); + Node *parseFunctionParam(); + Node *parseNewExpr(); + Node *parseConversionExpr(); + + /// Parse the <type> production. + Node *parseType(); + Node *parseFunctionType(); + Node *parseVectorType(); + Node *parseDecltype(); + Node *parseArrayType(); + Node *parsePointerToMemberType(); + Node *parseClassEnumType(); + + // FIXME: remove this when all the parse_* functions have been rewritten. + template <const char *(*parse_fn)(const char *, const char *, Db &)> + Node *legacyParse() { + size_t BeforeType = Names.size(); + const char *OrigFirst = First; + const char *T = parse_fn(First, Last, *this); + if (T == OrigFirst || BeforeType + 1 != Names.size()) + return nullptr; + First = T; + Node *R = Names.back(); + Names.pop_back(); + return R; + } + template <const char *(*parse_fn)(const char *, const char *, Db &, bool *)> + Node *legacyParse() { + size_t BeforeType = Names.size(); + const char *OrigFirst = First; + const char *T = parse_fn(First, Last, *this, nullptr); + if (T == OrigFirst || BeforeType + 1 != Names.size()) + return nullptr; + First = T; + Node *R = Names.back(); + Names.pop_back(); + return R; + } +}; + +const char *parse_expression(const char *first, const char *last, Db &db) { + db.First = first; + db.Last = last; + Node *R = db.parseExpr(); + if (R == nullptr) + return first; + db.Names.push_back(R); + return db.First; +} + +const char *parse_expr_primary(const char *first, const char *last, Db &db) { + db.First = first; + db.Last = last; + Node *R = db.parseExprPrimary(); + if (R == nullptr) + return first; + db.Names.push_back(R); + return db.First; +} + +const char *parse_type(const char *first, const char *last, Db &db) { + db.First = first; + db.Last = last; + Node *R = db.parseType(); + if (R == nullptr) + return first; + db.Names.push_back(R); + return db.First; +} + +const char *parse_decltype(const char *first, const char *last, Db &db) { + db.First = first; + db.Last = last; + Node *R = db.parseDecltype(); + if (R == nullptr) + return first; + db.Names.push_back(R); + return db.First; +} + +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_template_args(const char *first, const char *last, Db &db); +const char *parse_template_param(const char *, const char *, 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); +const char *parse_unresolved_name(const char *, const char *, Db &); +const char *parse_substitution(const char *, const char *, Db &); + +// <number> ::= [n] <non-negative decimal integer> +StringView Db::parseNumber(bool AllowNegative) { + const char *Tmp = First; + if (AllowNegative) + consumeIf('n'); + if (numLeft() == 0 || !std::isdigit(*First)) + return StringView(); + while (numLeft() != 0 && std::isdigit(*First)) + ++First; + return StringView(Tmp, First); +} + +// <positive length number> ::= [0-9]* +bool Db::parsePositiveInteger(size_t *Out) { + *Out = 0; + if (look() < '0' || look() > '9') + return true; + while (look() >= '0' && look() <= '9') { + *Out *= 10; + *Out += static_cast<size_t>(consume() - '0'); + } + return false; +} + +StringView Db::parseBareSourceName() { + size_t Int = 0; + if (parsePositiveInteger(&Int) || numLeft() < Int) + return StringView(); + StringView R(First, First + Int); + First += Int; + return R; +} + +// <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] E +// +// <ref-qualifier> ::= R # & ref-qualifier +// <ref-qualifier> ::= O # && ref-qualifier +Node *Db::parseFunctionType() { + if (!consumeIf('F')) + return nullptr; + consumeIf('Y'); // extern "C" + Node *ReturnType = parseType(); + if (ReturnType == nullptr) + return nullptr; + + FunctionRefQual ReferenceQualifier = FrefQualNone; + size_t ParamsBegin = Names.size(); + while (true) { + if (consumeIf('E')) + break; + if (consumeIf('v')) + continue; + if (consumeIf("RE")) { + ReferenceQualifier = FrefQualLValue; + break; + } + if (consumeIf("OE")) { + ReferenceQualifier = FrefQualRValue; + break; + } + Node *T = parseType(); + if (T == nullptr) + return nullptr; + Names.push_back(T); + } + + NodeArray Params = popTrailingNodeArray(ParamsBegin); + Node *Fn = make<FunctionType>(ReturnType, Params); + if (ReferenceQualifier != FrefQualNone) + Fn = make<FunctionRefQualType>(Fn, ReferenceQualifier); + return Fn; +} + +// extension: +// <vector-type> ::= Dv <positive dimension number> _ <extended element type> +// ::= Dv [<dimension expression>] _ <element type> +// <extended element type> ::= <element type> +// ::= p # AltiVec vector pixel +Node *Db::parseVectorType() { + if (!consumeIf("Dv")) + return nullptr; + if (look() >= '1' && look() <= '9') { + StringView DimensionNumber = parseNumber(); + if (!consumeIf('_')) + return nullptr; + if (consumeIf('p')) + return make<VectorType>(DimensionNumber); + Node *ElemType = parseType(); + if (ElemType == nullptr) + return nullptr; + return make<VectorType>(ElemType, DimensionNumber); + } + + if (!consumeIf('_')) { + Node *DimExpr = parseExpr(); + if (!DimExpr) + return nullptr; + if (!consumeIf('_')) + return nullptr; + Node *ElemType = parseType(); + if (!ElemType) + return nullptr; + return make<VectorType>(ElemType, DimExpr); + } + Node *ElemType = parseType(); + if (!ElemType) + return nullptr; + return make<VectorType>(ElemType, StringView()); +} + +// <decltype> ::= Dt <expression> E # decltype of an id-expression or class member access (C++0x) +// ::= DT <expression> E # decltype of an expression (C++0x) +Node *Db::parseDecltype() { + if (!consumeIf('D')) + return nullptr; + if (!consumeIf('t') && !consumeIf('T')) + return nullptr; + Node *E = parseExpr(); + if (E == nullptr) + return nullptr; + if (!consumeIf('E')) + return nullptr; + return make<EnclosingExpr>("decltype(", E, ")"); +} + +// <array-type> ::= A <positive dimension number> _ <element type> +// ::= A [<dimension expression>] _ <element type> +Node *Db::parseArrayType() { + if (!consumeIf('A')) + return nullptr; + + if (std::isdigit(look())) { + StringView Dimension = parseNumber(); + if (!consumeIf('_')) + return nullptr; + Node *Ty = parseType(); + if (Ty == nullptr) + return nullptr; + return make<ArrayType>(Ty, Dimension); + } + + if (!consumeIf('_')) { + Node *DimExpr = parseExpr(); + if (DimExpr == nullptr) + return nullptr; + if (!consumeIf('_')) + return nullptr; + Node *ElementType = parseType(); + if (ElementType == nullptr) + return nullptr; + return make<ArrayType>(ElementType, DimExpr); + } + + Node *Ty = parseType(); + if (Ty == nullptr) + return nullptr; + return make<ArrayType>(Ty); +} + +// <pointer-to-member-type> ::= M <class type> <member type> +Node *Db::parsePointerToMemberType() { + if (!consumeIf('M')) + return nullptr; + Node *ClassType = parseType(); + if (ClassType == nullptr) + return nullptr; + Node *MemberType = parseType(); + if (MemberType == nullptr) + return nullptr; + return make<PointerToMemberType>(ClassType, MemberType); +} + +// <class-enum-type> ::= <name> # non-dependent type name, dependent type name, or dependent typename-specifier +// ::= Ts <name> # dependent elaborated type specifier using 'struct' or 'class' +// ::= Tu <name> # dependent elaborated type specifier using 'union' +// ::= Te <name> # dependent elaborated type specifier using 'enum' +Node *Db::parseClassEnumType() { + // FIXME: try to parse the elaborated type specifiers here! + return legacyParse<parse_name>(); +} + +// <type> ::= <builtin-type> +// ::= <qualified-type> +// ::= <function-type> +// ::= <class-enum-type> +// ::= <array-type> +// ::= <pointer-to-member-type> +// ::= <template-param> +// ::= <template-template-param> <template-args> +// ::= <decltype> +// ::= P <type> # pointer +// ::= R <type> # l-value reference +// ::= O <type> # r-value reference (C++11) +// ::= C <type> # complex pair (C99) +// ::= G <type> # imaginary (C99) +// ::= <substitution> # See Compression below +// extension ::= U <objc-name> <objc-type> # objc-type<identifier> +// extension ::= <vector-type> # <vector-type> starts with Dv +// +// <objc-name> ::= <k0 number> objcproto <k1 number> <identifier> # k0 = 9 + <number of digits in k1> + k1 +// <objc-type> ::= <source-name> # PU<11+>objcproto 11objc_object<source-name> 11objc_object -> id<source-name> +Node *Db::parseType() { + Node *Result = nullptr; + + switch (look()) { + // ::= <qualified-type> + case 'r': + case 'V': + case 'K': { + Qualifiers Q = parseCVQualifiers(); + bool AppliesToFunction = look() == 'F'; + + Node *Child = parseType(); + if (Child == nullptr) + return nullptr; + + if (AppliesToFunction) + Result = make<FunctionQualType>(Child, Q); + else + Result = make<QualType>(Child, Q); + + // Itanium C++ ABI 5.1.5.3: + // For the purposes of substitution, the CV-qualifiers and ref-qualifier + // of a function type are an indivisible part of the type. + if (AppliesToFunction) + return Result; + break; + } + // <extended-qualifier> ::= U <source-name> [<template-args>] # vendor extended type qualifier + case 'U': { + // FIXME: We should fold this into the cvr qualifier parsing above. This + // currently adds too many entries into the substitution table if multiple + // qualifiers are present on the same type, as all the qualifiers on a type + // should just get one entry in the substitution table. + ++First; + StringView Qual = parseBareSourceName(); + if (Qual.empty()) + return nullptr; + + // FIXME parse the optional <template-args> here! + + Result = parseType(); + if (Result == nullptr) + return nullptr; + + // extension ::= U <objc-name> <objc-type> # objc-type<identifier> + if (Qual.startsWith("objcproto")) { + StringView ProtoSourceName = Qual.dropFront(std::strlen("objcproto")); + StringView Proto; + { + SwapAndRestore<const char *> SaveFirst(First, ProtoSourceName.begin()), + SaveLast(Last, ProtoSourceName.end()); + Proto = parseBareSourceName(); + } + if (Proto.empty()) + return nullptr; + Result = make<ObjCProtoName>(Result, Proto); + } else + Result = make<VendorExtQualType>(Result, Qual); + break; + } + // <builtin-type> ::= v # void + case 'v': + ++First; + return make<NameType>("void"); + // ::= w # wchar_t + case 'w': + ++First; + return make<NameType>("wchar_t"); + // ::= b # bool + case 'b': + ++First; + return make<NameType>("bool"); + // ::= c # char + case 'c': + ++First; + return make<NameType>("char"); + // ::= a # signed char + case 'a': + ++First; + return make<NameType>("signed char"); + // ::= h # unsigned char + case 'h': + ++First; + return make<NameType>("unsigned char"); + // ::= s # short + case 's': + ++First; + return make<NameType>("short"); + // ::= t # unsigned short + case 't': + ++First; + return make<NameType>("unsigned short"); + // ::= i # int + case 'i': + ++First; + return make<NameType>("int"); + // ::= j # unsigned int + case 'j': + ++First; + return make<NameType>("unsigned int"); + // ::= l # long + case 'l': + ++First; + return make<NameType>("long"); + // ::= m # unsigned long + case 'm': + ++First; + return make<NameType>("unsigned long"); + // ::= x # long long, __int64 + case 'x': + ++First; + return make<NameType>("long long"); + // ::= y # unsigned long long, __int64 + case 'y': + ++First; + return make<NameType>("unsigned long long"); + // ::= n # __int128 + case 'n': + ++First; + return make<NameType>("__int128"); + // ::= o # unsigned __int128 + case 'o': + ++First; + return make<NameType>("unsigned __int128"); + // ::= f # float + case 'f': + ++First; + return make<NameType>("float"); + // ::= d # double + case 'd': + ++First; + return make<NameType>("double"); + // ::= e # long double, __float80 + case 'e': + ++First; + return make<NameType>("long double"); + // ::= g # __float128 + case 'g': + ++First; + return make<NameType>("__float128"); + // ::= z # ellipsis + case 'z': + ++First; + return make<NameType>("..."); + + // <builtin-type> ::= u <source-name> # vendor extended type + case 'u': { + ++First; + StringView Res = parseBareSourceName(); + if (Res.empty()) + return nullptr; + return make<NameType>(Res); + } + case 'D': + switch (look(1)) { + // ::= Dd # IEEE 754r decimal floating point (64 bits) + case 'd': + First += 2; + return make<NameType>("decimal64"); + // ::= De # IEEE 754r decimal floating point (128 bits) + case 'e': + First += 2; + return make<NameType>("decimal128"); + // ::= Df # IEEE 754r decimal floating point (32 bits) + case 'f': + First += 2; + return make<NameType>("decimal32"); + // ::= Dh # IEEE 754r half-precision floating point (16 bits) + case 'h': + First += 2; + return make<NameType>("decimal16"); + // ::= Di # char32_t + case 'i': + First += 2; + return make<NameType>("char32_t"); + // ::= Ds # char16_t + case 's': + First += 2; + return make<NameType>("char16_t"); + // ::= Da # auto (in dependent new-expressions) + case 'a': + First += 2; + return make<NameType>("auto"); + // ::= Dc # decltype(auto) + case 'c': + First += 2; + return make<NameType>("decltype(auto)"); + // ::= Dn # std::nullptr_t (i.e., decltype(nullptr)) + case 'n': + First += 2; + return make<NameType>("std::nullptr_t"); + + // ::= <decltype> + case 't': + case 'T': { + Result = parseDecltype(); + break; + } + // extension ::= <vector-type> # <vector-type> starts with Dv + case 'v': { + Result = parseVectorType(); + break; + } + // ::= Dp <type> # pack expansion (C++0x) + case 'p': { + First += 2; + Node *Child = parseType(); + if (!Child) + return nullptr; + Result = make<ParameterPackExpansion>(Child); + break; + } + } + break; + // ::= <function-type> + case 'F': { + Result = parseFunctionType(); + break; + } + // ::= <array-type> + case 'A': { + Result = parseArrayType(); + break; + } + // ::= <pointer-to-member-type> + case 'M': { + Result = parsePointerToMemberType(); + break; + } + // ::= <template-param> + case 'T': { + Result = legacyParse<parse_template_param>(); + if (Result == nullptr) + return nullptr; + + // Result could be either of: + // <type> ::= <template-param> + // <type> ::= <template-template-param> <template-args> + // + // <template-template-param> ::= <template-param> + // ::= <substitution> + // + // If this is followed by some <template-args>, and we're permitted to + // parse them, take the second production. + + if (TryToParseTemplateArgs && look() == 'I') { + Node *TA = legacyParse<parse_template_args>(); + if (TA == nullptr) + return nullptr; + Result = make<NameWithTemplateArgs>(Result, TA); + } + break; + } + // ::= P <type> # pointer + case 'P': { + ++First; + Node *Ptr = parseType(); + if (Ptr == nullptr) + return nullptr; + Result = make<PointerType>(Ptr); + break; + } + // ::= R <type> # l-value reference + case 'R': { + ++First; + Node *Ref = parseType(); + if (Ref == nullptr) + return nullptr; + Result = make<LValueReferenceType>(Ref); + break; + } + // ::= O <type> # r-value reference (C++11) + case 'O': { + ++First; + Node *Ref = parseType(); + if (Ref == nullptr) + return nullptr; + Result = make<RValueReferenceType>(Ref); + break; + } + // ::= C <type> # complex pair (C99) + case 'C': { + ++First; + Node *P = parseType(); + if (P == nullptr) + return nullptr; + Result = make<PostfixQualifiedType>(P, " complex"); + break; + } + // ::= G <type> # imaginary (C99) + case 'G': { + ++First; + Node *P = parseType(); + if (P == nullptr) + return P; + Result = make<PostfixQualifiedType>(P, " imaginary"); + break; + } + // ::= <substitution> # See Compression below + case 'S': { + if (look(1) && look(1) != 't') { + Node *Sub = legacyParse<parse_substitution>(); + if (Sub == nullptr) + return nullptr; + + // Sub could be either of: + // <type> ::= <substitution> + // <type> ::= <template-template-param> <template-args> + // + // <template-template-param> ::= <template-param> + // ::= <substitution> + // + // If this is followed by some <template-args>, and we're permitted to + // parse them, take the second production. + + if (TryToParseTemplateArgs && look() == 'I') { + Node *TA = legacyParse<parse_template_args>(); + if (TA == nullptr) + return nullptr; + Result = make<NameWithTemplateArgs>(Sub, TA); + break; + } + + // If all we parsed was a substitution, don't re-insert into the + // substitution table. + return Sub; + } + _LIBCPP_FALLTHROUGH(); + } + // ::= <class-enum-type> + default: { + Result = parseClassEnumType(); + break; + } + } + + // If we parsed a type, insert it into the substitution table. Note that all + // <builtin-type>s and <substitution>s have already bailed out, because they + // don't get substitutions. + if (Result != nullptr) + Subs.push_back(Result); + return Result; +} + +Node *Db::parsePrefixExpr(StringView Kind) { + Node *E = parseExpr(); + if (E == nullptr) + return nullptr; + return make<PrefixExpr>(Kind, E); +} + +Node *Db::parseBinaryExpr(StringView Kind) { + Node *LHS = parseExpr(); + if (LHS == nullptr) + return nullptr; + Node *RHS = parseExpr(); + if (RHS == nullptr) + return nullptr; + return make<BinaryExpr>(LHS, Kind, RHS); +} + +Node *Db::parseIntegerLiteral(StringView Lit) { + StringView Tmp = parseNumber(true); + if (!Tmp.empty() && consumeIf('E')) + return make<IntegerExpr>(Lit, Tmp); + return nullptr; +} + +Qualifiers Db::parseCVQualifiers() { + Qualifiers CVR = QualNone; + if (consumeIf('r')) + addQualifiers(CVR, QualRestrict); + if (consumeIf('V')) + addQualifiers(CVR, QualVolatile); + if (consumeIf('K')) + addQualifiers(CVR, QualConst); + return CVR; +} + +// <function-param> ::= fp <top-level CV-Qualifiers> _ # L == 0, first parameter +// ::= fp <top-level CV-Qualifiers> <parameter-2 non-negative number> _ # L == 0, second and later parameters +// ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> _ # L > 0, first parameter +// ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> <parameter-2 non-negative number> _ # L > 0, second and later parameters +Node *Db::parseFunctionParam() { + if (consumeIf("fp")) { + parseCVQualifiers(); + StringView Num = parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make<FunctionParam>(Num); + } + if (consumeIf("fL")) { + if (parseNumber().empty()) + return nullptr; + if (!consumeIf('p')) + return nullptr; + parseCVQualifiers(); + StringView Num = parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make<FunctionParam>(Num); + } + return nullptr; +} + +// [gs] nw <expression>* _ <type> E # new (expr-list) type +// [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) +// [gs] na <expression>* _ <type> E # new[] (expr-list) type +// [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init) +// <initializer> ::= pi <expression>* E # parenthesized initialization +Node *Db::parseNewExpr() { + bool Global = consumeIf("gs"); + bool IsArray = look(1) == 'a'; + if (!consumeIf("nw") && !consumeIf("na")) + return nullptr; + size_t Exprs = Names.size(); + while (!consumeIf('_')) { + Node *Ex = parseExpr(); + if (Ex == nullptr) + return nullptr; + Names.push_back(Ex); + } + NodeArray ExprList = popTrailingNodeArray(Exprs); + Node *Ty = parseType(); + if (Ty == nullptr) + return Ty; + if (consumeIf("pi")) { + size_t InitsBegin = Names.size(); + while (!consumeIf('E')) { + Node *Init = parseExpr(); + if (Init == nullptr) + return Init; + Names.push_back(Init); + } + NodeArray Inits = popTrailingNodeArray(InitsBegin); + return make<NewExpr>(ExprList, Ty, Inits, Global, IsArray); + } else if (!consumeIf('E')) + return nullptr; + return make<NewExpr>(ExprList, Ty, NodeArray(), Global, IsArray); +} + +// cv <type> <expression> # conversion with one argument +// cv <type> _ <expression>* E # conversion with a different number of arguments +Node *Db::parseConversionExpr() { + if (!consumeIf("cv")) + return nullptr; + Node *Ty; + { + SwapAndRestore<bool> SaveTemp(TryToParseTemplateArgs, false); + Ty = parseType(); + } + + if (Ty == nullptr) + return nullptr; + + if (consumeIf('_')) { + size_t ExprsBegin = Names.size(); + while (!consumeIf('E')) { + Node *E = parseExpr(); + if (E == nullptr) + return E; + Names.push_back(E); + } + NodeArray Exprs = popTrailingNodeArray(ExprsBegin); + return make<ConversionExpr>(Ty, Exprs); + } + + Node *E[1] = {parseExpr()}; + if (E[0] == nullptr) + return nullptr; + return make<ConversionExpr>(Ty, makeNodeArray(E, E + 1)); +} + +// <expr-primary> ::= L <type> <value number> E # integer literal +// ::= L <type> <value float> E # floating literal +// ::= L <string type> E # string literal +// ::= L <nullptr type> E # nullptr literal (i.e., "LDnE") +// FIXME: ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000) +// ::= L <mangled-name> E # external name +Node *Db::parseExprPrimary() { + if (!consumeIf('L')) + return nullptr; + switch (look()) { + case 'w': + ++First; + return parseIntegerLiteral("wchar_t"); + case 'b': + if (consumeIf("b0E")) + return make<BoolExpr>(0); + if (consumeIf("b1E")) + return make<BoolExpr>(1); + return nullptr; + case 'c': + ++First; + return parseIntegerLiteral("char"); + case 'a': + ++First; + return parseIntegerLiteral("signed char"); + case 'h': + ++First; + return parseIntegerLiteral("unsigned char"); + case 's': + ++First; + return parseIntegerLiteral("short"); + case 't': + ++First; + return parseIntegerLiteral("unsigned short"); + case 'i': + ++First; + return parseIntegerLiteral(""); + case 'j': + ++First; + return parseIntegerLiteral("u"); + case 'l': + ++First; + return parseIntegerLiteral("l"); + case 'm': + ++First; + return parseIntegerLiteral("ul"); + case 'x': + ++First; + return parseIntegerLiteral("ll"); + case 'y': + ++First; + return parseIntegerLiteral("ull"); + case 'n': + ++First; + return parseIntegerLiteral("__int128"); + case 'o': + ++First; + return parseIntegerLiteral("unsigned __int128"); + case 'f': + ++First; + return parseFloatingLiteral<float>(); + case 'd': + ++First; + return parseFloatingLiteral<double>(); + case 'e': + ++First; + return parseFloatingLiteral<long double>(); + case '_': + if (consumeIf("_Z")) { + Node *R = legacyParse<parse_encoding>(); + if (R != nullptr && consumeIf('E')) + return R; + } + return nullptr; + case 'T': + // Invalid mangled name per + // http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html + return nullptr; + default: { + // might be named type + Node *T = parseType(); + if (T == nullptr) + return nullptr; + StringView N = parseNumber(); + if (!N.empty()) { + if (!consumeIf('E')) + return nullptr; + return make<IntegerCastExpr>(T, N); + } + if (consumeIf('E')) + return T; + return nullptr; + } + } +} + +// <expression> ::= <unary operator-name> <expression> +// ::= <binary operator-name> <expression> <expression> +// ::= <ternary operator-name> <expression> <expression> <expression> +// ::= cl <expression>+ E # call +// ::= cv <type> <expression> # conversion with one argument +// ::= cv <type> _ <expression>* E # conversion with a different number of arguments +// ::= [gs] nw <expression>* _ <type> E # new (expr-list) type +// ::= [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) +// ::= [gs] na <expression>* _ <type> E # new[] (expr-list) type +// ::= [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init) +// ::= [gs] dl <expression> # delete expression +// ::= [gs] da <expression> # delete[] expression +// ::= pp_ <expression> # prefix ++ +// ::= mm_ <expression> # prefix -- +// ::= ti <type> # typeid (type) +// ::= te <expression> # typeid (expression) +// ::= dc <type> <expression> # dynamic_cast<type> (expression) +// ::= sc <type> <expression> # static_cast<type> (expression) +// ::= cc <type> <expression> # const_cast<type> (expression) +// ::= rc <type> <expression> # reinterpret_cast<type> (expression) +// ::= st <type> # sizeof (a type) +// ::= sz <expression> # sizeof (an expression) +// ::= at <type> # alignof (a type) +// ::= az <expression> # alignof (an expression) +// ::= nx <expression> # noexcept (expression) +// ::= <template-param> +// ::= <function-param> +// ::= dt <expression> <unresolved-name> # expr.name +// ::= pt <expression> <unresolved-name> # expr->name +// ::= ds <expression> <expression> # expr.*expr +// ::= sZ <template-param> # size of a parameter pack +// ::= sZ <function-param> # size of a function parameter pack +// ::= sp <expression> # pack expansion +// ::= tw <expression> # throw expression +// ::= tr # throw with no operand (rethrow) +// ::= <unresolved-name> # f(p), N::f(p), ::f(p), +// # freestanding dependent name (e.g., T::x), +// # objectless nonstatic member reference +// ::= fL <binary-operator-name> <expression> <expression> +// ::= fR <binary-operator-name> <expression> <expression> +// ::= fl <binary-operator-name> <expression> +// ::= fr <binary-operator-name> <expression> +// ::= <expr-primary> +Node *Db::parseExpr() { + bool Global = consumeIf("gs"); + if (numLeft() < 2) + return nullptr; + + switch (*First) { + case 'L': + return parseExprPrimary(); + case 'T': + return legacyParse<parse_template_param>(); + case 'f': + return parseFunctionParam(); + case 'a': + switch (First[1]) { + case 'a': + First += 2; + return parseBinaryExpr("&&"); + case 'd': + First += 2; + return parsePrefixExpr("&"); + case 'n': + First += 2; + return parseBinaryExpr("&"); + case 'N': + First += 2; + return parseBinaryExpr("&="); + case 'S': + First += 2; + return parseBinaryExpr("="); + case 't': { + First += 2; + Node *Ty = parseType(); + if (Ty == nullptr) + return nullptr; + return make<EnclosingExpr>("alignof (", Ty, ")"); + } + case 'z': { + First += 2; + Node *Ty = parseExpr(); + if (Ty == nullptr) + return nullptr; + return make<EnclosingExpr>("alignof (", Ty, ")"); + } + } + return nullptr; + case 'c': + switch (First[1]) { + // cc <type> <expression> # const_cast<type>(expression) + case 'c': { + First += 2; + Node *Ty = parseType(); + if (Ty == nullptr) + return Ty; + Node *Ex = parseExpr(); + if (Ex == nullptr) + return Ex; + return make<CastExpr>("const_cast", Ty, Ex); + } + // cl <expression>+ E # call + case 'l': { + First += 2; + Node *Callee = parseExpr(); + if (Callee == nullptr) + return Callee; + size_t ExprsBegin = Names.size(); + while (!consumeIf('E')) { + Node *E = parseExpr(); + if (E == nullptr) + return E; + Names.push_back(E); + } + return make<CallExpr>(Callee, popTrailingNodeArray(ExprsBegin)); + } + case 'm': + First += 2; + return parseBinaryExpr(","); + case 'o': + First += 2; + return parsePrefixExpr("~"); + case 'v': + return parseConversionExpr(); + } + return nullptr; + case 'd': + switch (First[1]) { + case 'a': { + First += 2; + Node *Ex = parseExpr(); + if (Ex == nullptr) + return Ex; + return make<DeleteExpr>(Ex, Global, /*is_array=*/true); + } + case 'c': { + First += 2; + Node *T = parseType(); + if (T == nullptr) + return T; + Node *Ex = parseExpr(); + if (Ex == nullptr) + return Ex; + return make<CastExpr>("dynamic_cast", T, Ex); + } + case 'e': + First += 2; + return parsePrefixExpr("*"); + case 'l': { + First += 2; + Node *E = parseExpr(); + if (E == nullptr) + return E; + return make<DeleteExpr>(E, Global, /*is_array=*/false); + } + case 'n': + return legacyParse<parse_unresolved_name>(); + case 's': { + First += 2; + Node *LHS = parseExpr(); + if (LHS == nullptr) + return nullptr; + Node *RHS = parseExpr(); + if (RHS == nullptr) + return nullptr; + return make<MemberExpr>(LHS, ".*", RHS); + } + case 't': { + First += 2; + Node *LHS = parseExpr(); + if (LHS == nullptr) + return LHS; + Node *RHS = parseExpr(); + if (RHS == nullptr) + return nullptr; + return make<MemberExpr>(LHS, ".", RHS); + } + case 'v': + First += 2; + return parseBinaryExpr("/"); + case 'V': + First += 2; + return parseBinaryExpr("/="); + } + return nullptr; + case 'e': + switch (First[1]) { + case 'o': + First += 2; + return parseBinaryExpr("^"); + case 'O': + First += 2; + return parseBinaryExpr("^="); + case 'q': + First += 2; + return parseBinaryExpr("=="); + } + return nullptr; + case 'g': + switch (First[1]) { + case 'e': + First += 2; + return parseBinaryExpr(">="); + case 't': + First += 2; + return parseBinaryExpr(">"); + } + return nullptr; + case 'i': + if (First[1] == 'x') { + First += 2; + Node *Base = parseExpr(); + if (Base == nullptr) + return nullptr; + Node *Index = parseExpr(); + if (Index == nullptr) + return Index; + return make<ArraySubscriptExpr>(Base, Index); + } + return nullptr; + case 'l': + switch (First[1]) { + case 'e': + First += 2; + return parseBinaryExpr("<="); + case 's': + First += 2; + return parseBinaryExpr("<<"); + case 'S': + First += 2; + return parseBinaryExpr("<<="); + case 't': + First += 2; + return parseBinaryExpr("<"); + } + return nullptr; + case 'm': + switch (First[1]) { + case 'i': + First += 2; + return parseBinaryExpr("-"); + case 'I': + First += 2; + return parseBinaryExpr("-="); + case 'l': + First += 2; + return parseBinaryExpr("*"); + case 'L': + First += 2; + return parseBinaryExpr("*="); + case 'm': + First += 2; + if (consumeIf('_')) + return parsePrefixExpr("--"); + Node *Ex = parseExpr(); + if (Ex == nullptr) + return nullptr; + return make<PostfixExpr>(Ex, "--"); + } + return nullptr; + case 'n': + switch (First[1]) { + case 'a': + case 'w': + return parseNewExpr(); + case 'e': + First += 2; + return parseBinaryExpr("!="); + case 'g': + First += 2; + return parsePrefixExpr("-"); + case 't': + First += 2; + return parsePrefixExpr("!"); + case 'x': + First += 2; + Node *Ex = parseExpr(); + if (Ex == nullptr) + return Ex; + return make<EnclosingExpr>("noexcept (", Ex, ")"); + } + return nullptr; + case 'o': + switch (First[1]) { + case 'n': + return legacyParse<parse_unresolved_name>(); + case 'o': + First += 2; + return parseBinaryExpr("||"); + case 'r': + First += 2; + return parseBinaryExpr("|"); + case 'R': + First += 2; + return parseBinaryExpr("|="); + } + return nullptr; + case 'p': + switch (First[1]) { + case 'm': + First += 2; + return parseBinaryExpr("->*"); + case 'l': + First += 2; + return parseBinaryExpr("+"); + case 'L': + First += 2; + return parseBinaryExpr("+="); + case 'p': { + First += 2; + if (consumeIf('_')) + return parsePrefixExpr("++"); + Node *Ex = parseExpr(); + if (Ex == nullptr) + return Ex; + return make<PostfixExpr>(Ex, "++"); + } + case 's': + First += 2; + return parsePrefixExpr("+"); + case 't': { + First += 2; + Node *L = parseExpr(); + if (L == nullptr) + return nullptr; + Node *R = parseExpr(); + if (R == nullptr) + return nullptr; + return make<MemberExpr>(L, "->", R); + } + } + return nullptr; + case 'q': + if (First[1] == 'u') { + First += 2; + Node *Cond = parseExpr(); + if (Cond == nullptr) + return nullptr; + Node *LHS = parseExpr(); + if (LHS == nullptr) + return nullptr; + Node *RHS = parseExpr(); + if (RHS == nullptr) + return nullptr; + return make<ConditionalExpr>(Cond, LHS, RHS); + } + return nullptr; + case 'r': + switch (First[1]) { + case 'c': { + First += 2; + Node *T = parseType(); + if (T == nullptr) + return T; + Node *Ex = parseExpr(); + if (Ex == nullptr) + return Ex; + return make<CastExpr>("reinterpret_cast", T, Ex); + } + case 'm': + First += 2; + return parseBinaryExpr("%"); + case 'M': + First += 2; + return parseBinaryExpr("%="); + case 's': + First += 2; + return parseBinaryExpr(">>"); + case 'S': + First += 2; + return parseBinaryExpr(">>="); + } + return nullptr; + case 's': + switch (First[1]) { + case 'c': { + First += 2; + Node *T = parseType(); + if (T == nullptr) + return T; + Node *Ex = parseExpr(); + if (Ex == nullptr) + return Ex; + return make<CastExpr>("static_cast", T, Ex); + } + case 'p': { + First += 2; + Node *Child = parseExpr(); + if (Child == nullptr) + return nullptr; + return make<ParameterPackExpansion>(Child); + } + case 'r': + return legacyParse<parse_unresolved_name>(); + case 't': { + First += 2; + Node *Ty = parseType(); + if (Ty == nullptr) + return Ty; + return make<EnclosingExpr>("sizeof (", Ty, ")"); + } + case 'z': { + First += 2; + Node *Ex = parseExpr(); + if (Ex == nullptr) + return Ex; + return make<EnclosingExpr>("sizeof (", Ex, ")"); + } + case 'Z': + First += 2; + if (look() == 'T') { + Node *R = legacyParse<parse_template_param>(); + if (R == nullptr) + return nullptr; + return make<SizeofParamPackExpr>(R); + } else if (look() == 'f') { + Node *FP = parseFunctionParam(); + if (FP == nullptr) + return nullptr; + return make<EnclosingExpr>("sizeof...", FP, ")"); + } + return nullptr; + } + return nullptr; + case 't': + switch (First[1]) { + case 'e': { + First += 2; + Node *Ex = parseExpr(); + if (Ex == nullptr) + return Ex; + return make<EnclosingExpr>("typeid (", Ex, ")"); + } + case 'i': { + First += 2; + Node *Ty = parseType(); + if (Ty == nullptr) + return Ty; + return make<EnclosingExpr>("typeid (", Ty, ")"); + } + case 'r': + First += 2; + return make<NameType>("throw"); + case 'w': { + First += 2; + Node *Ex = parseExpr(); + if (Ex == nullptr) + return nullptr; + return make<ThrowExpr>(Ex); + } + } + return nullptr; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return legacyParse<parse_unresolved_name>(); + } + return nullptr; } // <number> ::= [n] <non-negative decimal integer> @@ -140,30 +3321,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__) @@ -174,58 +3355,28 @@ struct float_data<long double> static const size_t mangled_size = 20; // May need to be adjusted to 16 or 24 on other platforms #endif static const size_t max_demangled_size = 40; - static constexpr const char* spec = "%LaL"; + static constexpr const char *spec = "%LaL"; }; -constexpr const char* float_data<long double>::spec; - -template <class Float, class C> -const char* -parse_floating_number(const char* first, const char* last, C& db) -{ - const size_t N = float_data<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) - { - 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; - } - } - return first; +constexpr const char *FloatData<long double>::spec; + +template <class Float> Node *Db::parseFloatingLiteral() { + const size_t N = FloatData<Float>::mangled_size; + if (numLeft() <= N) + return nullptr; + StringView Data(First, First + N); + for (char C : Data) + if (!std::isxdigit(C)) + return nullptr; + First += N; + if (!consumeIf('E')) + return nullptr; + return make<FloatExpr<Float>>(Data); } -// <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 +3391,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 +3454,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 +3464,38 @@ 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); + db.Names.push_back(db.Subs[0]); first += 2; } break; @@ -327,10 +3519,9 @@ 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); + db.Names.push_back(db.Subs[sub]); first = t+1; } } @@ -341,207 +3532,27 @@ parse_substitution(const char* first, const char* last, C& db) return first; } -// <builtin-type> ::= v # void -// ::= w # wchar_t -// ::= b # bool -// ::= c # char -// ::= a # signed char -// ::= h # unsigned char -// ::= s # short -// ::= t # unsigned short -// ::= i # int -// ::= j # unsigned int -// ::= l # long -// ::= m # unsigned long -// ::= x # long long, __int64 -// ::= y # unsigned long long, __int64 -// ::= n # __int128 -// ::= o # unsigned __int128 -// ::= f # float -// ::= d # double -// ::= e # long double, __float80 -// ::= g # __float128 -// ::= z # ellipsis -// ::= Dd # IEEE 754r decimal floating point (64 bits) -// ::= De # IEEE 754r decimal floating point (128 bits) -// ::= Df # IEEE 754r decimal floating point (32 bits) -// ::= Dh # IEEE 754r half-precision floating point (16 bits) -// ::= Di # char32_t -// ::= Ds # char16_t -// ::= Da # auto (in dependent new-expressions) -// ::= Dc # decltype(auto) -// ::= 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) -{ - if (first != last) - { - switch (*first) - { - case 'v': - db.names.push_back("void"); - ++first; - break; - case 'w': - db.names.push_back("wchar_t"); - ++first; - break; - case 'b': - db.names.push_back("bool"); - ++first; - break; - case 'c': - db.names.push_back("char"); - ++first; - break; - case 'a': - db.names.push_back("signed char"); - ++first; - break; - case 'h': - db.names.push_back("unsigned char"); - ++first; - break; - case 's': - db.names.push_back("short"); - ++first; - break; - case 't': - db.names.push_back("unsigned short"); - ++first; - break; - case 'i': - db.names.push_back("int"); - ++first; - break; - case 'j': - db.names.push_back("unsigned int"); - ++first; - break; - case 'l': - db.names.push_back("long"); - ++first; - break; - case 'm': - db.names.push_back("unsigned long"); - ++first; - break; - case 'x': - db.names.push_back("long long"); - ++first; - break; - case 'y': - db.names.push_back("unsigned long long"); - ++first; - break; - case 'n': - db.names.push_back("__int128"); - ++first; - break; - case 'o': - db.names.push_back("unsigned __int128"); - ++first; - break; - case 'f': - db.names.push_back("float"); - ++first; - break; - case 'd': - db.names.push_back("double"); - ++first; - break; - case 'e': - db.names.push_back("long double"); - ++first; - break; - case 'g': - db.names.push_back("__float128"); - ++first; - break; - case 'z': - db.names.push_back("..."); - ++first; - break; - case 'u': - { - const char*t = parse_source_name(first+1, last, db); - if (t != first+1) - first = t; - } - break; - case 'D': - if (first+1 != last) - { - switch (first[1]) - { - case 'd': - db.names.push_back("decimal64"); - first += 2; - break; - case 'e': - db.names.push_back("decimal128"); - first += 2; - break; - case 'f': - db.names.push_back("decimal32"); - first += 2; - break; - case 'h': - db.names.push_back("decimal16"); - first += 2; - break; - case 'i': - db.names.push_back("char32_t"); - first += 2; - break; - case 's': - db.names.push_back("char16_t"); - first += 2; - break; - case 'a': - db.names.push_back("auto"); - first += 2; - break; - case 'c': - db.names.push_back("decltype(auto)"); - first += 2; - break; - case 'n': - db.names.push_back("std::nullptr_t"); - first += 2; - break; - } - } - break; - } - } - 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 +3562,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 +3571,16 @@ 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); + db.Names.push_back(db.TemplateParams[0]); 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,258 +3592,20 @@ 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); + db.Names.push_back(db.TemplateParams[sub]); 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; - } - } - } - } - return first; -} - -// cc <type> <expression> # const_cast<type> (expression) - -template <class C> -const char* -parse_const_cast_expr(const char* first, const char* last, C& db) -{ - if (last - first >= 3 && first[0] == 'c' && first[1] == 'c') - { - const char* t = parse_type(first+2, last, db); - if (t != first+2) - { - const char* t1 = parse_expression(t, last, db); - if (t1 != t) - { - if (db.names.size() < 2) - return first; - auto expr = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) - return first; - db.names.back() = "const_cast<" + db.names.back().move_full() + ">(" + expr + ")"; - first = t1; - } - } - } - return first; -} - -// dc <type> <expression> # dynamic_cast<type> (expression) - -template <class C> -const char* -parse_dynamic_cast_expr(const char* first, const char* last, C& db) -{ - if (last - first >= 3 && first[0] == 'd' && first[1] == 'c') - { - const char* t = parse_type(first+2, last, db); - if (t != first+2) - { - const char* t1 = parse_expression(t, last, db); - if (t1 != t) - { - if (db.names.size() < 2) - return first; - auto expr = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) - return first; - db.names.back() = "dynamic_cast<" + db.names.back().move_full() + ">(" + expr + ")"; - first = t1; - } - } - } - return first; -} - -// rc <type> <expression> # reinterpret_cast<type> (expression) - -template <class C> -const char* -parse_reinterpret_cast_expr(const char* first, const char* last, C& db) -{ - if (last - first >= 3 && first[0] == 'r' && first[1] == 'c') - { - const char* t = parse_type(first+2, last, db); - if (t != first+2) - { - const char* t1 = parse_expression(t, last, db); - if (t1 != t) - { - if (db.names.size() < 2) - return first; - auto expr = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) - return first; - db.names.back() = "reinterpret_cast<" + db.names.back().move_full() + ">(" + expr + ")"; - first = t1; - } - } - } - return first; -} - -// sc <type> <expression> # static_cast<type> (expression) - -template <class C> -const char* -parse_static_cast_expr(const char* first, const char* last, C& db) -{ - if (last - first >= 3 && first[0] == 's' && first[1] == 'c') - { - const char* t = parse_type(first+2, last, db); - if (t != first+2) - { - const char* t1 = parse_expression(t, last, db); - if (t1 != t) - { - 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 + ")"; - first = t1; - } - } - } - return first; -} - -// sp <expression> # pack expansion - -template <class C> -const char* -parse_pack_expansion(const char* first, const char* last, C& db) -{ - if (last - first >= 3 && first[0] == 's' && first[1] == 'p') - { - const char* t = parse_expression(first+2, last, db); - if (t != first+2) - first = t; - } - return first; -} - -// st <type> # sizeof (a type) - -template <class C> -const char* -parse_sizeof_type_expr(const char* first, const char* last, C& 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()) - return first; - db.names.back() = "sizeof (" + db.names.back().move_full() + ")"; - first = t; - } - } - return first; -} - -// sz <expr> # sizeof (a expression) - -template <class C> -const char* -parse_sizeof_expr_expr(const char* first, const char* last, C& 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()) - return first; - db.names.back() = "sizeof (" + db.names.back().move_full() + ")"; - first = t; - } - } - return first; -} - -// 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) -{ - if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'T') - { - 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) - { - 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)); - 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 - -template <class C> -const char* -parse_function_param(const char* first, const char* last, C& db) -{ - if (last - first >= 3 && *first == 'f') - { - if (first[1] == 'p') - { - unsigned 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)); - first = t1+1; - } - } - else if (first[1] == 'L') - { - unsigned cv; - const char* t0 = parse_number(first+2, last); - if (t0 != last && *t0 == 'p') - { - ++t0; - const char* t = parse_cv_qualifiers(t0, last, cv); - const char* t1 = parse_number(t, last); - if (t1 != last && *t1 == '_') - { - db.names.push_back("fp" + typename C::String(t, t1)); - first = t1+1; + db.FixForwardReferences = true; } } } @@ -844,102 +3613,10 @@ parse_function_param(const char* first, const char* last, C& db) return first; } -// 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) -{ - 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()) - return first; - db.names.back() = "sizeof...(" + db.names.back().move_full() + ")"; - first = t; - } - } - return first; -} - -// te <expression> # typeid (expression) -// ti <type> # typeid (type) - -template <class C> -const char* -parse_typeid_expr(const char* first, const char* last, C& db) -{ - if (last - first >= 3 && first[0] == 't' && (first[1] == 'e' || first[1] == 'i')) - { - const char* t; - if (first[1] == 'e') - t = parse_expression(first+2, last, db); - else - t = parse_type(first+2, last, db); - if (t != first+2) - { - if (db.names.empty()) - return first; - db.names.back() = "typeid(" + db.names.back().move_full() + ")"; - first = t; - } - } - return first; -} - -// tw <expression> # throw expression - -template <class C> -const char* -parse_throw_expr(const char* first, const char* last, C& 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()) - return first; - db.names.back() = "throw " + db.names.back().move_full(); - first = t; - } - } - return first; -} - -// ds <expression> <expression> # expr.*expr - -template <class C> -const char* -parse_dot_star_expr(const char* first, const char* last, C& db) -{ - if (last - first >= 3 && first[0] == 'd' && first[1] == 's') - { - const char* t = parse_expression(first+2, last, db); - if (t != first+2) - { - const char* t1 = parse_expression(t, last, db); - if (t1 != t) - { - if (db.names.size() < 2) - return first; - auto expr = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += ".*" + expr; - first = t1; - } - } - } - return first; -} - // <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 +3626,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 +3645,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 +3655,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.push_back(db.Names.back()); first = t; } else { for (; k1 != k0; --k1) - db.names.pop_back(); + db.Names.pop_back(); } break; } @@ -997,9 +3674,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.push_back(db.Names.back()); first = t; } break; @@ -1014,10 +3691,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.push_back(db.Names.back()); first = t; } } @@ -1031,9 +3709,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 +3719,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 +3736,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 +3751,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 +3779,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 +3798,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 +3814,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 +3831,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 +3850,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 +3900,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 +3932,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; } } @@ -1282,1012 +3971,6 @@ parse_unresolved_name(const char* first, const char* last, C& db) return first; } -// dt <expression> <unresolved-name> # expr.name - -template <class C> -const char* -parse_dot_expr(const char* first, const char* last, C& db) -{ - if (last - first >= 3 && first[0] == 'd' && first[1] == 't') - { - const char* t = parse_expression(first+2, last, db); - if (t != first+2) - { - const char* t1 = parse_unresolved_name(t, last, db); - if (t1 != t) - { - if (db.names.size() < 2) - return first; - auto name = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) - return first; - db.names.back().first += "." + name; - first = t1; - } - } - } - return first; -} - -// cl <expression>+ E # call - -template <class C> -const char* -parse_call_expr(const char* first, const char* last, C& 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) - 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()) - return first; - db.names.back().first.append(")"); - first = t; - } - } - return first; -} - -// [gs] nw <expression>* _ <type> E # new (expr-list) type -// [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) -// [gs] na <expression>* _ <type> E # new[] (expr-list) type -// [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init) -// <initializer> ::= pi <expression>* E # parenthesized initialization - -template <class C> -const char* -parse_new_expr(const char* first, const char* last, C& db) -{ - if (last - first >= 4) - { - const char* t = first; - bool parsed_gs = false; - if (t[0] == 'g' && t[1] == 's') - { - t += 2; - parsed_gs = true; - } - if (t[0] == 'n' && (t[1] == 'w' || t[1] == 'a')) - { - bool is_array = t[1] == 'a'; - t += 2; - if (t == last) - return first; - bool has_expr_list = false; - bool first_expr = true; - 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; - } - ++t; - const char* t1 = parse_type(t, last, db); - if (t1 == t || t1 == last) - return first; - t = t1; - bool has_init = false; - if (last - t >= 3 && t[0] == 'p' && t[1] == 'i') - { - t += 2; - has_init = true; - first_expr = true; - 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()) - return first; - init_list = db.names.back().move_full(); - db.names.pop_back(); - } - if (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)); - first = t+1; - } - } - return first; -} - -// 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) -{ - 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; - const char* t = parse_type(first+2, last, db); - db.try_to_parse_template_args = try_to_parse_template_args; - if (t != first+2 && t != last) - { - if (*t != '_') - { - const char* t1 = parse_expression(t, last, db); - if (t1 == t) - return first; - t = t1; - } - else - { - ++t; - if (t == last) - return first; - if (*t == 'E') - db.names.emplace_back(); - else - { - 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) - return first; - auto tmp = db.names.back().move_full(); - db.names.pop_back(); - db.names.back() = "(" + db.names.back().move_full() + ")(" + tmp + ")"; - first = t; - } - } - return first; -} - -// pt <expression> <expression> # expr->name - -template <class C> -const char* -parse_arrow_expr(const char* first, const char* last, C& db) -{ - if (last - first >= 3 && first[0] == 'p' && first[1] == 't') - { - const char* t = parse_expression(first+2, last, db); - if (t != first+2) - { - const char* t1 = parse_expression(t, last, db); - if (t1 != t) - { - 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; - first = t1; - } - } - } - return first; -} - -// <ref-qualifier> ::= R # & ref-qualifier -// <ref-qualifier> ::= O # && ref-qualifier - -// <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) -{ - if (first != last && *first == 'F') - { - const char* t = first+1; - if (t != last) - { - if (*t == 'Y') - { - /* extern "C" */ - if (++t == last) - return first; - } - const char* t1 = parse_type(t, last, db); - if (t1 != t) - { - t = t1; - typename C::String sig("("); - int ref_qual = 0; - while (true) - { - if (t == last) - { - db.names.pop_back(); - return first; - } - if (*t == 'E') - { - ++t; - break; - } - if (*t == 'v') - { - ++t; - continue; - } - if (*t == 'R' && t+1 != last && t[1] == 'E') - { - ref_qual = 1; - ++t; - continue; - } - if (*t == 'O' && t+1 != last && t[1] == 'E') - { - ref_qual = 2; - ++t; - continue; - } - size_t k0 = db.names.size(); - t1 = parse_type(t, last, db); - size_t k1 = db.names.size(); - if (t1 == t || t1 == last) - 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()) - return first; - db.names.back().first += " "; - db.names.back().second.insert(0, sig); - first = t; - } - } - } - return first; -} - -// <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) -{ - if (first != last && *first == 'M') - { - const char* t = parse_type(first+1, last, db); - if (t != first+1) - { - const char* t2 = parse_type(t, last, db); - if (t2 != t) - { - 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); - } - first = t2; - } - } - } - return first; -} - -// <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) -{ - if (first != last && *first == 'A' && first+1 != last) - { - if (first[1] == '_') - { - const char* t = parse_type(first+2, last, db); - if (t != first+2) - { - 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, " []"); - first = t; - } - } - else if ('1' <= first[1] && first[1] <= '9') - { - const char* t = parse_number(first+1, last); - if (t != last && *t == '_') - { - const char* t2 = parse_type(t+1, last, db); - if (t2 != t+1) - { - 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) + "]"); - first = t2; - } - } - } - else - { - const char* t = parse_expression(first+1, last, db); - if (t != first+1 && t != last && *t == '_') - { - const char* t2 = parse_type(++t, last, db); - if (t2 != t) - { - 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); - first = t2; - } - } - } - } - return first; -} - -// <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) -{ - if (last - first >= 4 && first[0] == 'D') - { - switch (first[1]) - { - case 't': - case 'T': - { - const char* t = parse_expression(first+2, last, db); - if (t != first+2 && t != last && *t == 'E') - { - if (db.names.empty()) - return first; - db.names.back() = "decltype(" + db.names.back().move_full() + ")"; - first = t+1; - } - } - break; - } - } - return first; -} - -// extension: -// <vector-type> ::= Dv <positive dimension number> _ -// <extended element type> -// ::= Dv [<dimension expression>] _ <element type> -// <extended element type> ::= <element type> -// ::= p # AltiVec vector pixel - -template <class C> -const char* -parse_vector_type(const char* first, const char* last, C& db) -{ - if (last - first > 3 && first[0] == 'D' && first[1] == 'v') - { - if ('1' <= first[2] && first[2] <= '9') - { - const char* t = parse_number(first+2, last); - if (t == last || *t != '_') - return first; - const char* num = first + 2; - size_t sz = static_cast<size_t>(t - num); - if (++t != last) - { - if (*t != 'p') - { - const char* t1 = parse_type(t, last, db); - if (t1 != t) - { - if (db.names.empty()) - return first; - db.names.back().first += " vector[" + typename C::String(num, sz) + "]"; - first = t1; - } - } - else - { - ++t; - db.names.push_back("pixel vector[" + typename C::String(num, sz) + "]"); - first = t; - } - } - } - else - { - typename C::String num; - const char* t1 = first+2; - if (*t1 != '_') - { - const char* t = parse_expression(t1, last, db); - if (t != t1) - { - if (db.names.empty()) - return first; - num = db.names.back().move_full(); - db.names.pop_back(); - t1 = t; - } - } - if (t1 != last && *t1 == '_' && ++t1 != last) - { - const char* t = parse_type(t1, last, db); - if (t != t1) - { - if (db.names.empty()) - return first; - db.names.back().first += " vector[" + num + "]"; - first = t; - } - } - } - } - return first; -} - -// <type> ::= <builtin-type> -// ::= <function-type> -// ::= <class-enum-type> -// ::= <array-type> -// ::= <pointer-to-member-type> -// ::= <template-param> -// ::= <template-template-param> <template-args> -// ::= <decltype> -// ::= <substitution> -// ::= <CV-qualifiers> <type> -// ::= P <type> # pointer-to -// ::= R <type> # reference-to -// ::= O <type> # rvalue reference-to (C++0x) -// ::= C <type> # complex pair (C 2000) -// ::= G <type> # imaginary (C 2000) -// ::= Dp <type> # pack expansion (C++0x) -// ::= U <source-name> <type> # vendor extended type qualifier -// extension := U <objc-name> <objc-type> # objc-type<identifier> -// extension := <vector-type> # <vector-type> starts with Dv - -// <objc-name> ::= <k0 number> objcproto <k1 number> <identifier> # k0 = 9 + <number of digits in k1> + k1 -// <objc-type> := <source-name> # PU<11+>objcproto 11objc_object<source-name> 11objc_object -> id<source-name> - -template <class C> -const char* -parse_type(const char* first, const char* last, C& db) -{ - if (first != last) - { - switch (*first) - { - case 'r': - case 'V': - case 'K': - { - unsigned cv = 0; - const char* t = parse_cv_qualifiers(first, last, cv); - if (t != first) - { - bool is_function = *t == 'F'; - size_t k0 = db.names.size(); - const char* t1 = parse_type(t, last, db); - size_t k1 = db.names.size(); - if (t1 != t) - { - if (is_function) - db.subs.pop_back(); - db.subs.emplace_back(db.names.get_allocator()); - 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] == '&' && - db.names[k].second[p - 1] == '&') - p -= 2; - else if (db.names[k].second.back() == '&') - p -= 1; - 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"); - } - db.subs.back().push_back(db.names[k]); - } - first = t1; - } - } - } - break; - default: - { - const char* t = parse_builtin_type(first, last, db); - if (t != first) - { - first = t; - } - else - { - switch (*first) - { - case 'A': - t = parse_array_type(first, last, db); - if (t != first) - { - if (db.names.empty()) - return first; - first = t; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); - } - break; - case 'C': - t = parse_type(first+1, last, db); - if (t != first+1) - { - if (db.names.empty()) - return first; - db.names.back().first.append(" complex"); - first = t; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); - } - break; - case 'F': - t = parse_function_type(first, last, db); - if (t != first) - { - if (db.names.empty()) - return first; - first = t; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); - } - break; - case 'G': - t = parse_type(first+1, last, db); - if (t != first+1) - { - if (db.names.empty()) - return first; - db.names.back().first.append(" imaginary"); - first = t; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); - } - break; - case 'M': - t = parse_pointer_to_member_type(first, last, db); - if (t != first) - { - if (db.names.empty()) - return first; - first = t; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); - } - break; - case 'O': - { - size_t k0 = db.names.size(); - t = parse_type(first+1, last, db); - size_t k1 = db.names.size(); - if (t != first+1) - { - db.subs.emplace_back(db.names.get_allocator()); - 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]); - } - first = t; - } - break; - } - case 'P': - { - size_t k0 = db.names.size(); - t = parse_type(first+1, last, db); - size_t k1 = db.names.size(); - if (t != first+1) - { - db.subs.emplace_back(db.names.get_allocator()); - 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]); - } - first = t; - } - break; - } - case 'R': - { - size_t k0 = db.names.size(); - t = parse_type(first+1, last, db); - size_t k1 = db.names.size(); - if (t != first+1) - { - db.subs.emplace_back(db.names.get_allocator()); - 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]); - } - first = t; - } - break; - } - case 'T': - { - size_t k0 = db.names.size(); - t = parse_template_param(first, last, db); - size_t k1 = db.names.size(); - if (t != first) - { - db.subs.emplace_back(db.names.get_allocator()); - 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) - { - 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())); - t = t1; - } - } - first = t; - } - break; - } - case 'U': - if (first+1 != last) - { - t = parse_source_name(first+1, last, db); - if (t != first+1) - { - const char* t2 = parse_type(t, last, db); - if (t2 != t) - { - 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") - { - db.names.back() = type + " " + db.names.back().move_full(); - } - 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) - { - db.names.back() = type + "<" + db.names.back().move_full() + ">"; - } - else - { - db.names.push_back(type + " " + proto); - } - } - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); - first = t2; - } - } - } - break; - case 'S': - if (first+1 != last && first[1] == 't') - { - t = parse_name(first, last, db); - if (t != first) - { - if (db.names.empty()) - return first; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); - first = t; - } - } - else - { - t = parse_substitution(first, last, db); - if (t != first) - { - 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.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; - } - } - } - break; - case 'D': - if (first+1 != last) - { - switch (first[1]) - { - case 'p': - { - size_t k0 = db.names.size(); - t = parse_type(first+2, last, db); - size_t k1 = db.names.size(); - if (t != first+2) - { - db.subs.emplace_back(db.names.get_allocator()); - for (size_t k = k0; k < k1; ++k) - db.subs.back().push_back(db.names[k]); - first = t; - return first; - } - break; - } - case 't': - case 'T': - t = parse_decltype(first, last, db); - if (t != first) - { - if (db.names.empty()) - return first; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); - first = t; - return first; - } - break; - case 'v': - t = parse_vector_type(first, last, db); - if (t != first) - { - if (db.names.empty()) - return first; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); - first = t; - return first; - } - break; - } - } - // drop through - default: - // must check for builtin-types before class-enum-types to avoid - // ambiguities with operator-names - t = parse_builtin_type(first, last, db); - if (t != first) - { - first = t; - } - else - { - t = parse_name(first, last, db); - if (t != first) - { - if (db.names.empty()) - return first; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); - first = t; - } - } - break; - } - } - break; - } - } - } - return first; -} - // <operator-name> // ::= aa # && // ::= ad # & (unary) @@ -2339,11 +4022,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]) @@ -2352,20 +4035,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; } @@ -2374,29 +4057,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; } } @@ -2407,23 +4091,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; } @@ -2432,15 +4116,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; } @@ -2449,11 +4133,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; } @@ -2461,7 +4145,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; @@ -2469,7 +4153,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': @@ -2477,23 +4161,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; } @@ -2502,23 +4187,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; } @@ -2527,23 +4212,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; } @@ -2552,15 +4237,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; } @@ -2569,27 +4254,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; } @@ -2597,7 +4282,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; @@ -2605,19 +4290,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; } @@ -2628,295 +4313,39 @@ 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; } } - return first; -} -template <class C> -const char* -parse_integer_literal(const char* first, const char* last, const typename C::String& lit, C& 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; - first = t+1; - } - return first; -} - -// <expr-primary> ::= L <type> <value number> E # integer literal -// ::= L <type> <value float> E # floating literal -// ::= L <string type> E # string literal -// ::= L <nullptr type> E # nullptr literal (i.e., "LDnE") -// ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000) -// ::= L <mangled-name> E # external name + if (original_first != first) + first = parse_abi_tag_seq(first, last, db); -template <class C> -const char* -parse_expr_primary(const char* first, const char* last, C& db) -{ - if (last - first >= 4 && *first == 'L') - { - switch (first[1]) - { - case 'w': - { - const char* t = parse_integer_literal(first+2, last, "wchar_t", db); - if (t != first+2) - first = t; - } - break; - case 'b': - if (first[3] == 'E') - { - switch (first[2]) - { - case '0': - db.names.push_back("false"); - first += 4; - break; - case '1': - db.names.push_back("true"); - first += 4; - break; - } - } - break; - case 'c': - { - const char* t = parse_integer_literal(first+2, last, "char", db); - if (t != first+2) - first = t; - } - break; - case 'a': - { - const char* t = parse_integer_literal(first+2, last, "signed char", db); - if (t != first+2) - first = t; - } - break; - case 'h': - { - const char* t = parse_integer_literal(first+2, last, "unsigned char", db); - if (t != first+2) - first = t; - } - break; - case 's': - { - const char* t = parse_integer_literal(first+2, last, "short", db); - if (t != first+2) - first = t; - } - break; - case 't': - { - const char* t = parse_integer_literal(first+2, last, "unsigned short", db); - if (t != first+2) - first = t; - } - break; - case 'i': - { - const char* t = parse_integer_literal(first+2, last, "", db); - if (t != first+2) - first = t; - } - break; - case 'j': - { - const char* t = parse_integer_literal(first+2, last, "u", db); - if (t != first+2) - first = t; - } - break; - case 'l': - { - const char* t = parse_integer_literal(first+2, last, "l", db); - if (t != first+2) - first = t; - } - break; - case 'm': - { - const char* t = parse_integer_literal(first+2, last, "ul", db); - if (t != first+2) - first = t; - } - break; - case 'x': - { - const char* t = parse_integer_literal(first+2, last, "ll", db); - if (t != first+2) - first = t; - } - break; - case 'y': - { - const char* t = parse_integer_literal(first+2, last, "ull", db); - if (t != first+2) - first = t; - } - break; - case 'n': - { - const char* t = parse_integer_literal(first+2, last, "__int128", db); - if (t != first+2) - first = t; - } - break; - case 'o': - { - const char* t = parse_integer_literal(first+2, last, "unsigned __int128", db); - if (t != first+2) - first = t; - } - break; - case 'f': - { - const char* t = parse_floating_number<float>(first+2, last, db); - if (t != first+2) - first = t; - } - break; - case 'd': - { - const char* t = parse_floating_number<double>(first+2, last, db); - if (t != first+2) - first = t; - } - break; - case 'e': - { - const char* t = parse_floating_number<long double>(first+2, last, db); - if (t != first+2) - first = t; - } - break; - case '_': - if (first[2] == 'Z') - { - const char* t = parse_encoding(first+3, last, db); - if (t != first+3 && t != last && *t == 'E') - first = t+1; - } - break; - case 'T': - // Invalid mangled name per - // http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html - break; - default: - { - // might be named type - const char* t = parse_type(first+1, last, db); - if (t != first+1 && t != last) - { - if (*t != 'E') - { - const char* n = t; - for (; n != last && isdigit(*n); ++n) - ; - if (n != t && n != last && *n == 'E') - { - if (db.names.empty()) - return first; - db.names.back() = "(" + db.names.back().move_full() + ")" + typename C::String(t, n); - first = n+1; - break; - } - } - else - { - first = t+1; - break; - } - } - } - } - } 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"; + case SpecialSubKind::string: + case SpecialSubKind::istream: + case SpecialSubKind::ostream: + case SpecialSubKind::iostream: + return db.make<ExpandedSpecialSubstitution>(Kind); + default: + break; } - 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; - } - } - return String(p0, pe); + return inp; } // <ctor-dtor-name> ::= C1 # complete object constructor @@ -2927,12 +4356,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]) { @@ -2943,11 +4371,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; @@ -2958,11 +4390,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; @@ -2971,16 +4405,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') { @@ -2989,101 +4421,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; - } + 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; @@ -3097,9 +4492,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) { @@ -3144,9 +4538,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) { @@ -3164,9 +4557,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; } @@ -3174,647 +4568,13 @@ parse_unscoped_name(const char* first, const char* last, C& db) return first; } -// at <type> # alignof (a type) - -template <class C> -const char* -parse_alignof_type(const char* first, const char* last, C& 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()) - return first; - db.names.back().first = "alignof (" + db.names.back().move_full() + ")"; - first = t; - } - } - return first; -} - -// az <expression> # alignof (a expression) - -template <class C> -const char* -parse_alignof_expr(const char* first, const char* last, C& 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()) - return first; - db.names.back().first = "alignof (" + db.names.back().move_full() + ")"; - first = t; - } - } - return first; -} - -template <class C> -const char* -parse_noexcept_expression(const char* first, const char* last, C& db) -{ - const char* t1 = parse_expression(first, last, db); - if (t1 != first) - { - if (db.names.empty()) - return first; - db.names.back().first = "noexcept (" + db.names.back().move_full() + ")"; - 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) -{ - const char* t1 = parse_expression(first, last, db); - if (t1 != first) - { - if (db.names.empty()) - return first; - db.names.back().first = op + "(" + db.names.back().move_full() + ")"; - 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) -{ - const char* t1 = parse_expression(first, last, db); - if (t1 != first) - { - const char* t2 = parse_expression(t1, last, db); - if (t2 != t1) - { - 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 += ')'; - first = t2; - } - else if(!db.names.empty()) - db.names.pop_back(); - } - return first; -} - -// <expression> ::= <unary operator-name> <expression> -// ::= <binary operator-name> <expression> <expression> -// ::= <ternary operator-name> <expression> <expression> <expression> -// ::= cl <expression>+ E # call -// ::= cv <type> <expression> # conversion with one argument -// ::= cv <type> _ <expression>* E # conversion with a different number of arguments -// ::= [gs] nw <expression>* _ <type> E # new (expr-list) type -// ::= [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) -// ::= [gs] na <expression>* _ <type> E # new[] (expr-list) type -// ::= [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init) -// ::= [gs] dl <expression> # delete expression -// ::= [gs] da <expression> # delete[] expression -// ::= pp_ <expression> # prefix ++ -// ::= mm_ <expression> # prefix -- -// ::= ti <type> # typeid (type) -// ::= te <expression> # typeid (expression) -// ::= dc <type> <expression> # dynamic_cast<type> (expression) -// ::= sc <type> <expression> # static_cast<type> (expression) -// ::= cc <type> <expression> # const_cast<type> (expression) -// ::= rc <type> <expression> # reinterpret_cast<type> (expression) -// ::= st <type> # sizeof (a type) -// ::= sz <expression> # sizeof (an expression) -// ::= at <type> # alignof (a type) -// ::= az <expression> # alignof (an expression) -// ::= nx <expression> # noexcept (expression) -// ::= <template-param> -// ::= <function-param> -// ::= dt <expression> <unresolved-name> # expr.name -// ::= pt <expression> <unresolved-name> # expr->name -// ::= ds <expression> <expression> # expr.*expr -// ::= sZ <template-param> # size of a parameter pack -// ::= sZ <function-param> # size of a function parameter pack -// ::= sp <expression> # pack expansion -// ::= tw <expression> # throw expression -// ::= tr # throw with no operand (rethrow) -// ::= <unresolved-name> # f(p), N::f(p), ::f(p), -// # freestanding dependent name (e.g., T::x), -// # objectless nonstatic member reference -// ::= <expr-primary> - -template <class C> -const char* -parse_expression(const char* first, const char* last, C& db) -{ - if (last - first >= 2) - { - const char* t = first; - bool parsed_gs = false; - if (last - first >= 4 && t[0] == 'g' && t[1] == 's') - { - t += 2; - parsed_gs = true; - } - switch (*t) - { - case 'L': - first = parse_expr_primary(first, last, db); - break; - case 'T': - first = parse_template_param(first, last, db); - break; - case 'f': - first = parse_function_param(first, last, db); - break; - case 'a': - switch (t[1]) - { - case 'a': - t = parse_binary_expression(first+2, last, "&&", db); - if (t != first+2) - first = t; - break; - case 'd': - t = parse_prefix_expression(first+2, last, "&", db); - if (t != first+2) - first = t; - break; - case 'n': - t = parse_binary_expression(first+2, last, "&", db); - if (t != first+2) - first = t; - break; - case 'N': - t = parse_binary_expression(first+2, last, "&=", db); - if (t != first+2) - first = t; - break; - case 'S': - t = parse_binary_expression(first+2, last, "=", db); - if (t != first+2) - first = t; - break; - case 't': - first = parse_alignof_type(first, last, db); - break; - case 'z': - first = parse_alignof_expr(first, last, db); - break; - } - break; - case 'c': - switch (t[1]) - { - case 'c': - first = parse_const_cast_expr(first, last, db); - break; - case 'l': - first = parse_call_expr(first, last, db); - break; - case 'm': - t = parse_binary_expression(first+2, last, ",", db); - if (t != first+2) - first = t; - break; - case 'o': - t = parse_prefix_expression(first+2, last, "~", db); - if (t != first+2) - first = t; - break; - case 'v': - first = parse_conversion_expr(first, last, db); - break; - } - break; - case 'd': - switch (t[1]) - { - case 'a': - { - const char* t1 = parse_expression(t+2, last, db); - if (t1 != t+2) - { - if (db.names.empty()) - return first; - db.names.back().first = (parsed_gs ? typename C::String("::") : typename C::String()) + - "delete[] " + db.names.back().move_full(); - first = t1; - } - } - break; - case 'c': - first = parse_dynamic_cast_expr(first, last, db); - break; - case 'e': - t = parse_prefix_expression(first+2, last, "*", db); - if (t != first+2) - first = t; - break; - case 'l': - { - const char* t1 = parse_expression(t+2, last, db); - if (t1 != t+2) - { - if (db.names.empty()) - return first; - db.names.back().first = (parsed_gs ? typename C::String("::") : typename C::String()) + - "delete " + db.names.back().move_full(); - first = t1; - } - } - break; - case 'n': - return parse_unresolved_name(first, last, db); - case 's': - first = parse_dot_star_expr(first, last, db); - break; - case 't': - first = parse_dot_expr(first, last, db); - break; - case 'v': - t = parse_binary_expression(first+2, last, "/", db); - if (t != first+2) - first = t; - break; - case 'V': - t = parse_binary_expression(first+2, last, "/=", db); - if (t != first+2) - first = t; - break; - } - break; - case 'e': - switch (t[1]) - { - case 'o': - t = parse_binary_expression(first+2, last, "^", db); - if (t != first+2) - first = t; - break; - case 'O': - t = parse_binary_expression(first+2, last, "^=", db); - if (t != first+2) - first = t; - break; - case 'q': - t = parse_binary_expression(first+2, last, "==", db); - if (t != first+2) - first = t; - break; - } - break; - case 'g': - switch (t[1]) - { - case 'e': - t = parse_binary_expression(first+2, last, ">=", db); - if (t != first+2) - first = t; - break; - case 't': - t = parse_binary_expression(first+2, last, ">", db); - if (t != first+2) - first = t; - break; - } - break; - case 'i': - if (t[1] == 'x') - { - const char* t1 = parse_expression(first+2, last, db); - if (t1 != first+2) - { - const char* t2 = parse_expression(t1, last, db); - if (t2 != t1) - { - 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 + "]"; - first = t2; - } - else if (!db.names.empty()) - db.names.pop_back(); - } - } - break; - case 'l': - switch (t[1]) - { - case 'e': - t = parse_binary_expression(first+2, last, "<=", db); - if (t != first+2) - first = t; - break; - case 's': - t = parse_binary_expression(first+2, last, "<<", db); - if (t != first+2) - first = t; - break; - case 'S': - t = parse_binary_expression(first+2, last, "<<=", db); - if (t != first+2) - first = t; - break; - case 't': - t = parse_binary_expression(first+2, last, "<", db); - if (t != first+2) - first = t; - break; - } - break; - case 'm': - switch (t[1]) - { - case 'i': - t = parse_binary_expression(first+2, last, "-", db); - if (t != first+2) - first = t; - break; - case 'I': - t = parse_binary_expression(first+2, last, "-=", db); - if (t != first+2) - first = t; - break; - case 'l': - t = parse_binary_expression(first+2, last, "*", db); - if (t != first+2) - first = t; - break; - case 'L': - t = parse_binary_expression(first+2, last, "*=", db); - if (t != first+2) - first = t; - break; - case 'm': - if (first+2 != last && first[2] == '_') - { - t = parse_prefix_expression(first+3, last, "--", db); - if (t != first+3) - first = t; - } - else - { - const char* t1 = parse_expression(first+2, last, db); - if (t1 != first+2) - { - if (db.names.empty()) - return first; - db.names.back() = "(" + db.names.back().move_full() + ")--"; - first = t1; - } - } - break; - } - break; - case 'n': - switch (t[1]) - { - case 'a': - case 'w': - first = parse_new_expr(first, last, db); - break; - case 'e': - t = parse_binary_expression(first+2, last, "!=", db); - if (t != first+2) - first = t; - break; - case 'g': - t = parse_prefix_expression(first+2, last, "-", db); - if (t != first+2) - first = t; - break; - case 't': - t = parse_prefix_expression(first+2, last, "!", db); - if (t != first+2) - first = t; - break; - case 'x': - t = parse_noexcept_expression(first+2, last, db); - if (t != first+2) - first = t; - break; - } - break; - case 'o': - switch (t[1]) - { - case 'n': - return parse_unresolved_name(first, last, db); - case 'o': - t = parse_binary_expression(first+2, last, "||", db); - if (t != first+2) - first = t; - break; - case 'r': - t = parse_binary_expression(first+2, last, "|", db); - if (t != first+2) - first = t; - break; - case 'R': - t = parse_binary_expression(first+2, last, "|=", db); - if (t != first+2) - first = t; - break; - } - break; - case 'p': - switch (t[1]) - { - case 'm': - t = parse_binary_expression(first+2, last, "->*", db); - if (t != first+2) - first = t; - break; - case 'l': - t = parse_binary_expression(first+2, last, "+", db); - if (t != first+2) - first = t; - break; - case 'L': - t = parse_binary_expression(first+2, last, "+=", db); - if (t != first+2) - first = t; - break; - case 'p': - if (first+2 != last && first[2] == '_') - { - t = parse_prefix_expression(first+3, last, "++", db); - if (t != first+3) - first = t; - } - else - { - const char* t1 = parse_expression(first+2, last, db); - if (t1 != first+2) - { - if (db.names.empty()) - return first; - db.names.back() = "(" + db.names.back().move_full() + ")++"; - first = t1; - } - } - break; - case 's': - t = parse_prefix_expression(first+2, last, "+", db); - if (t != first+2) - first = t; - break; - case 't': - first = parse_arrow_expr(first, last, db); - break; - } - break; - case 'q': - if (t[1] == 'u') - { - const char* t1 = parse_expression(first+2, last, db); - if (t1 != first+2) - { - const char* t2 = parse_expression(t1, last, db); - if (t2 != t1) - { - const char* t3 = parse_expression(t2, last, db); - if (t3 != t2) - { - 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 + ")"; - first = t3; - } - else - { - if (db.names.size() < 2) - return first; - db.names.pop_back(); - db.names.pop_back(); - } - } - else if (!db.names.empty()) - db.names.pop_back(); - } - } - break; - case 'r': - switch (t[1]) - { - case 'c': - first = parse_reinterpret_cast_expr(first, last, db); - break; - case 'm': - t = parse_binary_expression(first+2, last, "%", db); - if (t != first+2) - first = t; - break; - case 'M': - t = parse_binary_expression(first+2, last, "%=", db); - if (t != first+2) - first = t; - break; - case 's': - t = parse_binary_expression(first+2, last, ">>", db); - if (t != first+2) - first = t; - break; - case 'S': - t = parse_binary_expression(first+2, last, ">>=", db); - if (t != first+2) - first = t; - break; - } - break; - case 's': - switch (t[1]) - { - case 'c': - first = parse_static_cast_expr(first, last, db); - break; - case 'p': - first = parse_pack_expansion(first, last, db); - break; - case 'r': - return parse_unresolved_name(first, last, db); - case 't': - first = parse_sizeof_type_expr(first, last, db); - break; - case 'z': - first = parse_sizeof_expr_expr(first, last, db); - break; - case 'Z': - if (last - t >= 3) - { - switch (t[2]) - { - case 'T': - first = parse_sizeof_param_pack_expr(first, last, db); - break; - case 'f': - first = parse_sizeof_function_param_pack_expr(first, last, db); - break; - } - } - break; - } - break; - case 't': - switch (t[1]) - { - case 'e': - case 'i': - first = parse_typeid_expr(first, last, db); - break; - case 'r': - db.names.push_back("throw"); - first += 2; - break; - case 'w': - first = parse_throw_expr(first, last, db); - break; - } - break; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return parse_unresolved_name(first, last, db); - } - } - return first; -} - // <template-arg> ::= <type> # type or template // ::= X <expression> E # expression // ::= <expr-primary> # simple expressions // ::= J <template-arg>* E # argument pack // ::= LZ <encoding> E # extension - -template <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) { @@ -3829,10 +4589,11 @@ parse_template_arg(const char* first, const char* last, C& db) first = t+1; } break; - case 'J': + case 'J': { t = first+1; if (t == last) return first; + size_t ArgsBegin = db.Names.size(); while (*t != 'E') { const char* t1 = parse_template_arg(t, last, db); @@ -3840,8 +4601,11 @@ parse_template_arg(const char* first, const char* last, C& db) return first; t = t1; } + NodeArray Args = db.popTrailingNodeArray(ArgsBegin); + db.Names.push_back(db.make<TemplateArgumentPack>(Args)); first = t+1; break; + } case 'L': // <expr-primary> or LZ <encoding> E if (first+1 != last && first[1] == 'Z') @@ -3864,58 +4628,54 @@ 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) - { - db.template_param.back().emplace_back(db.names.get_allocator()); - 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 (db.TagTemplates) { - if (args.size() > 1) - args += ", "; - args += db.names[k].move_full(); + 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 + 1 != k1) + return first; + Node *TableEntry = db.Names.back(); + if (TableEntry->getKind() == Node::KTemplateArgumentPack) + TableEntry = db.make<ParameterPack>( + static_cast<TemplateArgumentPack*>(TableEntry) + ->getElements()); + db.TemplateParams.push_back(TableEntry); + 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)); - + auto *tp = db.make<TemplateArgs>( + 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> @@ -3930,39 +4690,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') @@ -3977,17 +4733,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.push_back(db.Names.back()); } else - db.names.back().first = name; + db.Names.back() = name; pop_subs = true; t0 = t1; } @@ -3998,15 +4755,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.push_back(db.Names.back()); pop_subs = true; t0 = t1; } @@ -4019,15 +4777,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.push_back(db.Names.back()); pop_subs = true; t0 = t1; } @@ -4038,12 +4797,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.push_back(db.Names.back()); t0 = t1; component_ends_with_template_args = true; } @@ -4059,15 +4819,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.push_back(db.Names.back()); pop_subs = true; t0 = t1; } @@ -4076,9 +4837,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.pop_back(); if (ends_with_template_args) *ends_with_template_args = component_ends_with_template_args; } @@ -4127,9 +4888,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') @@ -4141,9 +4901,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) @@ -4156,18 +4917,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; @@ -4179,17 +4940,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; } @@ -4206,9 +4967,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) @@ -4242,20 +5002,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.push_back(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; @@ -4273,13 +5035,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; @@ -4344,12 +5108,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) { @@ -4364,9 +5129,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; @@ -4375,9 +5141,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; @@ -4386,9 +5153,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; @@ -4397,9 +5165,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; @@ -4415,9 +5184,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; } } @@ -4433,20 +5204,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> { @@ -4456,16 +5251,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; } } @@ -4481,9 +5280,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; @@ -4492,9 +5292,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; @@ -4529,17 +5331,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': @@ -4551,96 +5354,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) + if (db.Names.size() < 1) 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()) - 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<FunctionEncoding>( + 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<FunctionEncoding>( + 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 @@ -4657,12 +5436,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) @@ -4683,9 +5462,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; @@ -4694,29 +5475,35 @@ 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; } +enum { + unknown_error = -4, + invalid_args = -3, + invalid_mangled_name, + memory_alloc_failure, + success +}; + // <block-involcaton-function> ___Z<encoding>_block_invoke // <block-involcaton-function> ___Z<encoding>_block_invoke<decimal-digit>+ // <block-involcaton-function> ___Z<encoding>_block_invoke_<decimal-digit>+ // <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) { @@ -4759,218 +5546,14 @@ 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 = 0; - unsigned ref = 0; - unsigned encoding_depth = 0; - bool parsed_ctor_dtor_cv = false; - bool tag_templates = true; - bool fix_forward_references = false; - bool try_to_parse_template_args = true; - - template <size_t N> - Db(arena<N>& ar) : - names(ar), - subs(0, names, ar), - template_param(0, subs, ar) - {} -}; - } // unnamed namespace + +namespace __cxxabiv1 { extern "C" _LIBCXXABI_FUNC_VIS char * __cxa_demangle(const char *mangled_name, char *buf, size_t *n, int *status) { if (mangled_name == nullptr || (buf != nullptr && n == nullptr)) @@ -4981,48 +5564,46 @@ __cxa_demangle(const char *mangled_name, char *buf, size_t *n, int *status) { } size_t internal_size = buf != nullptr ? *n : 0; - arena<bs> a; - Db db(a); - db.template_param.emplace_back(a); + 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 && + db.Names.back()->containsUnexpandedParameterPack()) + 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; @@ -5030,5 +5611,4 @@ __cxa_demangle(const char *mangled_name, char *buf, size_t *n, int *status) { *status = internal_status; return buf; } - } // __cxxabiv1 diff --git a/src/cxa_exception.cpp b/src/cxa_exception.cpp index 757b3d4..d5230cd 100644 --- a/src/cxa_exception.cpp +++ b/src/cxa_exception.cpp @@ -11,7 +11,6 @@ // //===----------------------------------------------------------------------===// -#include "config.h" #include "cxxabi.h" #include <exception> // for std::terminate @@ -20,6 +19,10 @@ #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 | // +---------------------------+-----------------------------+---------------+ @@ -33,8 +36,6 @@ namespace __cxxabiv1 { -#pragma GCC visibility push(default) - // Utility routines static inline @@ -65,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) { @@ -120,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. @@ -132,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. @@ -139,20 +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 *>(__malloc_with_fallback(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() { - __free_with_fallback(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); } @@ -161,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 = __malloc_with_fallback(actual_size); + void *ptr = __aligned_malloc_with_fallback(actual_size); if (NULL == ptr) std::terminate(); std::memset(ptr, 0, actual_size); @@ -172,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) { - __free_with_fallback(dependent_exception); + __aligned_free_with_fallback(dependent_exception); } @@ -202,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); @@ -216,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 @@ -235,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 @@ -246,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(); @@ -329,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 @@ -389,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; @@ -423,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)"); @@ -500,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) @@ -525,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) @@ -570,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 ) { @@ -587,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 ) { @@ -611,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) @@ -697,6 +738,4 @@ __cxa_uncaught_exceptions() throw() } // extern "C" -#pragma GCC visibility pop - } // abi diff --git a/src/cxa_exception.hpp b/src/cxa_exception.hpp index 6e68f98..c8b0fb1 100644 --- a/src/cxa_exception.hpp +++ b/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/src/cxa_exception_storage.cpp b/src/cxa_exception_storage.cpp index 549d747..c641e02 100644 --- a/src/cxa_exception_storage.cpp +++ b/src/cxa_exception_storage.cpp @@ -13,7 +13,6 @@ #include "cxa_exception.hpp" -#include "config.h" #include <__threading_support> #if defined(_LIBCXXABI_HAS_NO_THREADS) @@ -57,7 +56,7 @@ namespace { std::__libcpp_tls_key key_; std::__libcpp_exec_once_flag flag_ = _LIBCPP_EXEC_ONCE_INITIALIZER; - void destruct_ (void *p) { + 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()"); diff --git a/src/cxa_guard.cpp b/src/cxa_guard.cpp index 49f83ed..f4c2a18 100644 --- a/src/cxa_guard.cpp +++ b/src/cxa_guard.cpp @@ -10,7 +10,6 @@ #include "__cxxabi_config.h" #include "abort_message.h" -#include "config.h" #include <__threading_support> #include <stdint.h> @@ -113,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 { @@ -125,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 { @@ -137,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 { @@ -150,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 { @@ -163,7 +159,9 @@ set_lock(uint32_t& x, lock_type y) x = f.guard; } -#endif // __APPLE__ +#endif // !defined(__arm__) + +#endif // __APPLE__ && !__arm__ } // unnamed namespace diff --git a/src/cxa_handlers.cpp b/src/cxa_handlers.cpp index ce2cfba..2881a2a 100644 --- a/src/cxa_handlers.cpp +++ b/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 { @@ -105,7 +102,7 @@ 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; +new_handler __cxa_new_handler = 0; } new_handler diff --git a/src/cxa_handlers.hpp b/src/cxa_handlers.hpp index bfd5abe..14c0e10 100644 --- a/src/cxa_handlers.hpp +++ b/src/cxa_handlers.hpp @@ -20,11 +20,11 @@ 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; diff --git a/src/cxa_new_delete.cpp b/src/cxa_new_delete.cpp deleted file mode 100644 index 7609d76..0000000 --- a/src/cxa_new_delete.cpp +++ /dev/null @@ -1,243 +0,0 @@ -//===------------------------ cxa_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 - -#include <new> -#include <cstdlib> - -#if !defined(_THROW_BAD_ALLOC) || !defined(_NOEXCEPT) -#error _THROW_BAD_ALLOC and _NOEXCEPT libc++ macros must already be defined \ - by libc++. -#endif - -/* -[new.delete.single] - -* Executes a loop: Within the loop, the function first attempts to allocate - the requested storage. Whether the attempt involves a call to the Standard C - library function malloc is unspecified. - -* Returns a pointer to the allocated storage if the attempt is successful. - Otherwise, if the current new_handler (18.6.2.5) is a null pointer value, - throws bad_alloc. - -* Otherwise, the function calls the current new_handler function (18.6.2.3). - If the called function returns, the loop repeats. - -* The loop terminates when an attempt to allocate the requested storage is - successful or when a called new_handler function does not return. -*/ -__attribute__((__weak__, __visibility__("default"))) -void * -operator new(std::size_t size) _THROW_BAD_ALLOC -{ - if (size == 0) - size = 1; - void* p; - while ((p = std::malloc(size)) == 0) - { - 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; -} - -/* -Note: The relationships among these operators is both carefully considered -and standard in C++11. Please do not change them without fully understanding -the consequences of doing so. Reference: -http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2158.html -*/ -/* -[new.delete.single] - -Calls operator new(size). If the call returns normally, returns the result of -that call. Otherwise, returns a null pointer. -*/ -__attribute__((__weak__, __visibility__("default"))) -void* -operator new(size_t size, const std::nothrow_t&) _NOEXCEPT -{ - void* p = 0; -#ifndef _LIBCXXABI_NO_EXCEPTIONS - try - { -#endif - p = ::operator new(size); -#ifndef _LIBCXXABI_NO_EXCEPTIONS - } - catch (...) - { - } -#endif - return p; -} - -/* -[new.delete.array] - -Returns operator new(size). -*/ -__attribute__((__weak__, __visibility__("default"))) -void* -operator new[](size_t size) _THROW_BAD_ALLOC -{ - return ::operator new(size); -} - -/* -[new.delete.array] - -Calls operator new[](size). If the call returns normally, returns the result -of that call. Otherwise, returns a null pointer. -*/ -__attribute__((__weak__, __visibility__("default"))) -void* -operator new[](size_t size, const std::nothrow_t&) _NOEXCEPT -{ - void* p = 0; -#ifndef _LIBCXXABI_NO_EXCEPTIONS - try - { -#endif - p = ::operator new[](size); -#ifndef _LIBCXXABI_NO_EXCEPTIONS - } - catch (...) - { - } -#endif - return p; -} - -/* -[new.delete.single] - -If ptr is null, does nothing. Otherwise, reclaims the storage allocated by the -earlier call to operator new. -*/ -__attribute__((__weak__, __visibility__("default"))) -void -operator delete(void* ptr) _NOEXCEPT -{ - if (ptr) - std::free(ptr); -} - -/* -[new.delete.single] - -calls operator delete(ptr) -*/ -__attribute__((__weak__, __visibility__("default"))) -void -operator delete(void* ptr, const std::nothrow_t&) _NOEXCEPT -{ - ::operator delete(ptr); -} - -/* -[new.delete.array] - -Calls operator delete(ptr) -*/ -__attribute__((__weak__, __visibility__("default"))) -void -operator delete[] (void* ptr) _NOEXCEPT -{ - ::operator delete(ptr); -} - -/* -[new.delete.array] - -calls operator delete[](ptr) -*/ -__attribute__((__weak__, __visibility__("default"))) -void -operator delete[] (void* ptr, const std::nothrow_t&) _NOEXCEPT -{ - ::operator delete[](ptr); -} - -namespace std -{ - -// 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/src/cxa_noexception.cpp b/src/cxa_noexception.cpp index e45ceff..adda552 100644 --- a/src/cxa_noexception.cpp +++ b/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/src/cxa_personality.cpp b/src/cxa_personality.cpp index 75f6b4a..7f857fa 100644 --- a/src/cxa_personality.cpp +++ b/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 @@ -358,7 +357,7 @@ get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo, 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, @@ -394,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 @@ -405,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, @@ -934,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 @@ -1194,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/src/cxa_thread_atexit.cpp b/src/cxa_thread_atexit.cpp index 748b0eb..49d15d6 100644 --- a/src/cxa_thread_atexit.cpp +++ b/src/cxa_thread_atexit.cpp @@ -20,7 +20,7 @@ namespace __cxxabiv1 { #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 - __attribute__((__weak__)) + _LIBCXXABI_WEAK #endif int __cxa_thread_atexit_impl(Dtor, void*, void*); diff --git a/src/cxa_unexpected.cpp b/src/cxa_unexpected.cpp index f6e6b6a..7d4ef80 100644 --- a/src/cxa_unexpected.cpp +++ b/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/src/cxa_virtual.cpp b/src/cxa_virtual.cpp index ac81ad3..f59fa7e 100644 --- a/src/cxa_virtual.cpp +++ b/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/src/fallback_malloc.cpp b/src/fallback_malloc.cpp index 3f8441e..336ad31 100644 --- a/src/fallback_malloc.cpp +++ b/src/fallback_malloc.cpp @@ -9,7 +9,6 @@ #include "fallback_malloc.h" -#include "config.h" #include <__threading_support> #include <cstdlib> // for malloc, calloc, free @@ -32,195 +31,222 @@ namespace { _LIBCPP_SAFE_STATIC static std::__libcpp_mutex_t heap_mutex = _LIBCPP_MUTEX_INITIALIZER; #else -static void * heap_mutex = 0; +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_ ); } + mutexor(std::__libcpp_mutex_t* m) : mtx_(m) { + std::__libcpp_mutex_lock(mtx_); + } + ~mutexor() { std::__libcpp_mutex_unlock(mtx_); } #else - mutexor ( void * ) {} - ~mutexor () {} + mutexor(void*) {} + ~mutexor() {} #endif private: - mutexor ( const mutexor &rhs ); - mutexor & operator = ( const mutexor &rhs ); + mutexor(const mutexor& rhs); + mutexor& operator=(const mutexor& rhs); #ifndef _LIBCXXABI_HAS_NO_THREADS - std::__libcpp_mutex_t *mtx_; + std::__libcpp_mutex_t* mtx_; #endif }; - static const size_t HEAP_SIZE = 512; -char heap [ HEAP_SIZE ] __attribute__((aligned)); +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)" + 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; +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_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)); } +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); - } +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); - } +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 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; } +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; +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 ); + mutexor mtx(&heap_mutex); #ifdef DEBUG_FALLBACK_MALLOC - std::cout << "Freeing item at " << offset_from_node ( cp ) << " of size " << cp->len << std::endl; + 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)) { + 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; + 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 ) { + if (after(p) == cp) { #ifdef DEBUG_FALLBACK_MALLOC - std::cout << " Appending onto chunk at " << offset_from_node ( p ) << std::endl; + 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 + 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; + 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; - } - } + 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; + std::cout << " Making new free list entry " << offset_from_node(cp) + << std::endl; #endif - cp->next_node = offset_from_node ( freelist ); - freelist = cp; + 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; - } +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 +} // end unnamed namespace namespace __cxxabiv1 { -#pragma GCC visibility push(hidden) +struct __attribute__((aligned)) __aligned_type {}; -void * __malloc_with_fallback(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; +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); +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 __free_with_fallback(void *ptr) { - if (is_fallback_ptr(ptr)) - fallback_free(ptr); - else - std::free(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 + } } -#pragma GCC visibility pop +void __free_with_fallback(void* ptr) { + if (is_fallback_ptr(ptr)) + fallback_free(ptr); + else + std::free(ptr); +} } // namespace __cxxabiv1 diff --git a/src/fallback_malloc.h b/src/fallback_malloc.h index 1078442..d6f4714 100644 --- a/src/fallback_malloc.h +++ b/src/fallback_malloc.h @@ -10,21 +10,19 @@ #ifndef _FALLBACK_MALLOC_H #define _FALLBACK_MALLOC_H +#include "__cxxabi_config.h" #include <cstddef> // for size_t namespace __cxxabiv1 { -#pragma GCC visibility push(hidden) - // Allocate some memory from _somewhere_ -void * __malloc_with_fallback(size_t size); +_LIBCXXABI_HIDDEN void * __aligned_malloc_with_fallback(size_t size); // Allocate and zero-initialize memory from _somewhere_ -void * __calloc_with_fallback(size_t count, size_t size); - -void __free_with_fallback(void *ptr); +_LIBCXXABI_HIDDEN void * __calloc_with_fallback(size_t count, size_t size); -#pragma GCC visibility pop +_LIBCXXABI_HIDDEN void __aligned_free_with_fallback(void *ptr); +_LIBCXXABI_HIDDEN void __free_with_fallback(void *ptr); } // namespace __cxxabiv1 diff --git a/src/include/refstring.h b/src/include/refstring.h new file mode 100644 index 0000000..bc131ae --- /dev/null +++ b/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/src/private_typeinfo.cpp b/src/private_typeinfo.cpp index e7995ed..ef9466e 100644 --- a/src/private_typeinfo.cpp +++ b/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 @@ -232,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) @@ -430,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) @@ -538,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" @@ -639,7 +633,7 @@ __dynamic_cast(const void *static_ptr, const __class_type_info *static_type, // 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)) @@ -679,8 +673,8 @@ __dynamic_cast(const void *static_ptr, const __class_type_info *static_type, 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}; @@ -715,9 +709,6 @@ __dynamic_cast(const void *static_ptr, const __class_type_info *static_type, #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. @@ -1300,6 +1291,4 @@ __base_class_type_info::search_below_dst(__dynamic_cast_info* info, use_strcmp); } -#pragma GCC visibility pop - } // __cxxabiv1 diff --git a/src/private_typeinfo.h b/src/private_typeinfo.h index f3d3e1d..3922ae6 100644 --- a/src/private_typeinfo.h +++ b/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; @@ -248,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/src/stdlib_exception.cpp b/src/stdlib_exception.cpp index c47a9b7..a8f71ab 100644 --- a/src/stdlib_exception.cpp +++ b/src/stdlib_exception.cpp @@ -7,10 +7,11 @@ // //===----------------------------------------------------------------------===// +#define _LIBCPP_BUILDING_LIBRARY +#define _LIBCPP_BUILDING_NEW +#include <new> #include <exception> -#pragma GCC visibility push(default) - namespace std { @@ -36,6 +37,67 @@ const char* bad_exception::what() const _NOEXCEPT return "std::bad_exception"; } -} // std -#pragma GCC visibility pop +// 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/src/stdlib_new_delete.cpp b/src/stdlib_new_delete.cpp new file mode 100644 index 0000000..0e85f6a --- /dev/null +++ b/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/src/stdlib_stdexcept.cpp b/src/stdlib_stdexcept.cpp index bd6789e..e3b7cd4 100644 --- a/src/stdlib_stdexcept.cpp +++ b/src/stdlib_stdexcept.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "__refstring" +#include "include/refstring.h" #include "stdexcept" #include "new" #include <cstdlib> diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c1819d7..5e51c44 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -24,10 +24,6 @@ set(LIBCXXABI_EXECUTOR "None" CACHE STRING "Executor to use when running tests.") set(AUTO_GEN_COMMENT "## Autogenerated by libcxxabi configuration.\n# Do not edit!") -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg - @ONLY) if (LIBCXXABI_ENABLE_SHARED) set(LIBCXXABI_TEST_DEPS cxxabi_shared) @@ -46,6 +42,11 @@ if (NOT LIBCXXABI_STANDALONE_BUILD) endif() endif() + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + add_lit_testsuite(check-cxxabi "Running libcxxabi tests" ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${LIBCXXABI_TEST_DEPS} diff --git a/test/catch_member_function_pointer_02.pass.cpp b/test/catch_member_function_pointer_02.pass.cpp index 5ce2359..6bd9e6f 100644 --- a/test/catch_member_function_pointer_02.pass.cpp +++ b/test/catch_member_function_pointer_02.pass.cpp @@ -11,6 +11,10 @@ // clause? // UNSUPPORTED: libcxxabi-no-exceptions, libcxxabi-no-noexcept-function-type +// GCC 7 and 8 support noexcept function types but this test still fails. +// This is likely a bug in their implementation. Investigation needed. +// XFAIL: gcc-7, gcc-8 + #include <cassert> struct X { diff --git a/test/exception_object_alignment.pass.cpp b/test/exception_object_alignment.pass.cpp new file mode 100644 index 0000000..960751b --- /dev/null +++ b/test/exception_object_alignment.pass.cpp @@ -0,0 +1,34 @@ +//===---------------- exception_object_alignment.pass.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. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: libcxxabi-no-exceptions + +// Check that the pointer __cxa_allocate_exception returns is aligned to the +// default alignment for the target architecture. + +#include <cassert> +#include <cstdint> +#include <cxxabi.h> +#include <type_traits> +#include <__cxxabi_config.h> + +struct S { + int a[4]; +} __attribute__((aligned)); + +int main() { +#if !defined(_LIBCXXABI_ARM_EHABI) + void *p = __cxxabiv1::__cxa_allocate_exception(16); + auto i = reinterpret_cast<uintptr_t>(p); + auto a = std::alignment_of<S>::value; + assert(i % a == 0); + __cxxabiv1::__cxa_free_exception(p); +#endif + return 0; +} diff --git a/test/incomplete_type.sh.cpp b/test/incomplete_type.sh.cpp index 3f18d36..6062dc6 100644 --- a/test/incomplete_type.sh.cpp +++ b/test/incomplete_type.sh.cpp @@ -16,10 +16,14 @@ // UNSUPPORTED: libcxxabi-no-exceptions +// NOTE: Pass -lc++abi explicitly and before -lc++ so that -lc++ doesn't drag +// in the system libc++abi installation on OS X. (DYLD_LIBRARY_PATH is ignored +// for shell tests because of Apple security features). + // RUN: %cxx %flags %compile_flags -c %s -o %t.one.o // RUN: %cxx %flags %compile_flags -c %s -o %t.two.o -DTU_ONE -// RUN: %cxx %flags %t.one.o %t.two.o %link_flags -o %t.exe -// RUN: %exec %t.exe +// RUN: %cxx %flags %t.one.o %t.two.o -lc++abi %link_flags -o %t.exe +// RUN: %t.exe #include <stdio.h> #include <cstring> @@ -91,6 +95,8 @@ int main() { } catch (int NeverDefined::*p) { assert(!p); } + catch(...) { assert(!"FAIL: Didn't catch NeverDefined::*" ); } + AssertIncompleteTypeInfoEquals(ReturnTypeInfoIncompleteMP(), typeid(int IncompleteAtThrow::*)); try { ThrowIncompleteMP(); @@ -104,6 +110,7 @@ int main() { } catch (int IncompleteAtThrow::*p) { assert(!p); } + catch(...) { assert(!"FAIL: Didn't catch IncompleteAtThrow::*" ); } AssertIncompleteTypeInfoEquals(ReturnTypeInfoIncompletePP(), typeid(IncompleteAtThrow**)); try { @@ -114,6 +121,7 @@ int main() { } catch (IncompleteAtThrow** p) { assert(!p); } + catch(...) { assert(!"FAIL: Didn't catch IncompleteAtThrow**" ); } try { ThrowIncompletePMP(); @@ -125,6 +133,7 @@ int main() { } catch (int IncompleteAtThrow::**p) { assert(!p); } + catch(...) { assert(!"FAIL: Didn't catch IncompleteAtThrow::**" ); } AssertIncompleteTypeInfoEquals(ReturnTypeInfoCompleteMP(), typeid(int CompleteAtThrow::*)); try { @@ -139,6 +148,7 @@ int main() { } catch (int CompleteAtThrow::*p) { assert(!p); } + catch(...) { assert(!"FAIL: Didn't catch CompleteAtThrow::" ); } AssertIncompleteTypeInfoEquals(ReturnTypeInfoCompletePP(), typeid(CompleteAtThrow**)); try { @@ -153,6 +163,7 @@ int main() { } catch (CompleteAtThrow**p) { assert(!p); } + catch(...) { assert(!"FAIL: Didn't catch CompleteAtThrow**" ); } try { ThrowCompletePMP(); @@ -168,6 +179,7 @@ int main() { } catch (int CompleteAtThrow::**p) { assert(!p); } + catch(...) { assert(!"FAIL: Didn't catch CompleteAtThrow::**" ); } #if __cplusplus >= 201103L // Catch nullptr as complete type @@ -176,6 +188,7 @@ int main() { } catch (int IncompleteAtThrow::*p) { assert(!p); } + catch(...) { assert(!"FAIL: Didn't catch nullptr as IncompleteAtThrow::*" ); } // Catch nullptr as an incomplete type try { @@ -183,12 +196,16 @@ int main() { } catch (int CompleteAtThrow::*p) { assert(!p); } + catch(...) { assert(!"FAIL: Didn't catch nullptr as CompleteAtThrow::*" ); } + // Catch nullptr as a type that is never complete. try { ThrowNullptr(); } catch (int NeverDefined::*p) { assert(!p); } + catch(...) { assert(!"FAIL: Didn't catch nullptr as NeverDefined::*" ); } + #endif } #endif diff --git a/test/libcxxabi/test/config.py b/test/libcxxabi/test/config.py index e219961..d24fc5b 100644 --- a/test/libcxxabi/test/config.py +++ b/test/libcxxabi/test/config.py @@ -45,6 +45,8 @@ class Configuration(LibcxxConfiguration): # test_exception_storage_nodynmem.pass.cpp fails under this specific configuration if self.get_lit_bool('cxx_ext_threads', False) and self.get_lit_bool('libcxxabi_shared', False): self.config.available_features.add('libcxxabi-shared-externally-threaded') + if not self.get_lit_bool('llvm_unwinder', False): + self.config.available_features.add('libcxxabi-has-system-unwinder') def configure_compile_flags(self): self.cxx.compile_flags += ['-DLIBCXXABI_NO_TIMER'] @@ -79,6 +81,13 @@ class Configuration(LibcxxConfiguration): % libcxxabi_headers) self.cxx.compile_flags += ['-I' + libcxxabi_headers] + libunwind_headers = self.get_lit_conf('libunwind_headers', None) + if self.get_lit_bool('llvm_unwinder', False) and libunwind_headers: + if not os.path.isdir(libunwind_headers): + self.lit_config.fatal("libunwind_headers='%s' is not a directory." + % libunwind_headers) + self.cxx.compile_flags += ['-I' + libunwind_headers] + def configure_compile_flags_exceptions(self): pass diff --git a/test/lit.cfg b/test/lit.cfg index 29d1a3b..2e23c56 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -29,7 +29,7 @@ config.test_source_root = os.path.dirname(__file__) libcxx_src_root = getattr(config, 'libcxx_src_root', None) if not libcxx_src_root: libcxx_src_root = os.path.join(config.test_source_root, '../../libcxx') -libcxx_test_src_root = os.path.join(libcxx_src_root, 'test') +libcxx_test_src_root = os.path.join(libcxx_src_root, 'utils') if os.path.isfile(os.path.join(libcxx_test_src_root, 'libcxx', '__init__.py')): site.addsitedir(libcxx_test_src_root) else: diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in index 9c0fdf2..a66c9e4 100644 --- a/test/lit.site.cfg.in +++ b/test/lit.site.cfg.in @@ -6,6 +6,7 @@ config.libcxxabi_obj_root = "@LIBCXXABI_BINARY_DIR@" config.abi_library_path = "@LIBCXXABI_LIBRARY_DIR@" config.libcxx_src_root = "@LIBCXXABI_LIBCXX_PATH@" config.cxx_headers = "@LIBCXXABI_LIBCXX_INCLUDES@" +config.libunwind_headers = "@LIBCXXABI_LIBUNWIND_INCLUDES_INTERNAL@" config.cxx_library_root = "@LIBCXXABI_LIBCXX_LIBRARY_PATH@" config.llvm_unwinder = "@LIBCXXABI_USE_LLVM_UNWINDER@" config.enable_threads = "@LIBCXXABI_ENABLE_THREADS@" @@ -19,6 +20,8 @@ config.enable_exceptions = "@LIBCXXABI_ENABLE_EXCEPTIONS@" config.host_triple = "@LLVM_HOST_TRIPLE@" config.target_triple = "@TARGET_TRIPLE@" config.use_target = len("@LIBCXXABI_TARGET_TRIPLE@") > 0 +config.sysroot = "@LIBCXXABI_SYSROOT@" +config.gcc_toolchain = "@LIBCXXABI_GCC_TOOLCHAIN@" config.cxx_ext_threads = "@LIBCXXABI_BUILD_EXTERNAL_THREAD_LIBRARY@" # Let the main config do the real work. diff --git a/test/native/arm-linux-eabi/ttype-encoding-00.pass.sh.s b/test/native/arm-linux-eabi/ttype-encoding-00.pass.sh.s index 2c9f720..0484e9f 100644 --- a/test/native/arm-linux-eabi/ttype-encoding-00.pass.sh.s +++ b/test/native/arm-linux-eabi/ttype-encoding-00.pass.sh.s @@ -1,5 +1,6 @@ @ RUN: %cxx %flags %link_flags %s -o %t.exe -@ RUN: %exec %t.exe +@ RUN: %t.exe +@ UNSUPPORTED: libcxxabi-no-exceptions @ PURPOSE: Check that 0x00 is a valid value for ttype encoding. LLVM and @ GCC 4.6 are generating 0x00 as ttype encoding. libc++abi should provide diff --git a/test/native/arm-linux-eabi/ttype-encoding-90.pass.sh.s b/test/native/arm-linux-eabi/ttype-encoding-90.pass.sh.s index bb362c4..837602b 100644 --- a/test/native/arm-linux-eabi/ttype-encoding-90.pass.sh.s +++ b/test/native/arm-linux-eabi/ttype-encoding-90.pass.sh.s @@ -1,5 +1,6 @@ @ RUN: %cxx %flags %link_flags %s -o %t.exe -@ RUN: %exec %t.exe +@ RUN: %t.exe +@ UNSUPPORTED: libcxxabi-no-exceptions @ PURPOSE: Check that 0x90 is a valid value for ttype encoding. diff --git a/test/test_demangle.pass.cpp b/test/test_demangle.pass.cpp index 4eb6a88..ab97257 100644 --- a/test/test_demangle.pass.cpp +++ b/test/test_demangle.pass.cpp @@ -29500,6 +29500,7 @@ const char* cases[][2] = {"_ZZ2f6vE1b", "f6()::b"}, {"_ZNV3$_35test9Ev", "$_3::test9() volatile"}, {"_ZN5Test8I3$_2EC1ES0_", "Test8<$_2>::Test8($_2)"}, + {"_Z3fooIZN3BarC1EvE3$_0EvT_", "void foo<Bar::Bar()::$_0>(Bar::Bar()::$_0)"}, {"_ZGVZN1N1gEvE1a", "guard variable for N::g()::a"}, {"_ZplRK1YRA100_P1X", "operator+(Y const&, X* (&) [100])"}, {"_Z1fno", "f(__int128, unsigned __int128)"}, @@ -29584,7 +29585,7 @@ const char* cases[][2] = {"_Z1fPU11objcproto1A11objc_object", "f(id<A>)"}, {"_Z1fPKU11objcproto1A7NSArray", "f(NSArray<A> const*)"}, {"_ZNK1AIJ1Z1Y1XEEcv1BIJDpPT_EEIJS2_S1_S0_EEEv", "A<Z, Y, X>::operator B<X*, Y*, Z*><X, Y, Z>() const"}, - {"_ZNK3Ncr6Silver7Utility6detail12CallOnThreadIZ53-[DeploymentSetupController handleManualServerEntry:]E3$_5EclIJEEEDTclclL_ZNS2_4getTIS4_EERT_vEEspclsr3stdE7forwardIT_Efp_EEEDpOSA_", "decltype(-[DeploymentSetupController handleManualServerEntry:]::$_5& Ncr::Silver::Utility::detail::getT<-[DeploymentSetupController handleManualServerEntry:]::$_5>()()(std::forward<-[DeploymentSetupController handleManualServerEntry:]::$_5>(fp))) Ncr::Silver::Utility::detail::CallOnThread<-[DeploymentSetupController handleManualServerEntry:]::$_5>::operator()<>(-[DeploymentSetupController handleManualServerEntry:]::$_5&&) const"}, + {"_ZNK3Ncr6Silver7Utility6detail12CallOnThreadIZ53-[DeploymentSetupController handleManualServerEntry:]E3$_5EclIJEEEDTclclL_ZNS2_4getTIS4_EERT_vEEspclsr3stdE7forwardIT_Efp_EEEDpOSA_", "decltype(-[DeploymentSetupController handleManualServerEntry:]::$_5& Ncr::Silver::Utility::detail::getT<-[DeploymentSetupController handleManualServerEntry:]::$_5>()()(std::forward<-[DeploymentSetupController handleManualServerEntry:]::$_5>(fp)...)) Ncr::Silver::Utility::detail::CallOnThread<-[DeploymentSetupController handleManualServerEntry:]::$_5>::operator()<>(-[DeploymentSetupController handleManualServerEntry:]::$_5&&...) const"}, {"_Zli2_xy", "operator\"\" _x(unsigned long long)"}, {"_Z1fIiEDcT_", "decltype(auto) f<int>(int)"}, {"_ZZ4testvEN1g3fooE5Point", "test()::g::foo(Point)"}, @@ -29599,9 +29600,62 @@ const char* cases[][2] = {"i", "int"}, {"PKFvRiE", "void (*)(int&) const"}, - // FIXME(compnerd) pretty print this as void (*)(unsigned long &) volatile && - {"PVFvRmOE", "void (*)(unsigned long&) volatile&&"}, + {"PVFvRmOE", "void (*)(unsigned long&) volatile &&"}, {"PFvRmOE", "void (*)(unsigned long&) &&"}, + {"_ZTW1x", "thread-local wrapper routine for x"}, + {"_ZTHN3fooE", "thread-local initialization routine for foo"}, + {"_Z4algoIJiiiEEvZ1gEUlDpT_E_", "void algo<int, int, int>(g::'lambda'(int, int, int))"}, + // attribute abi_tag + {"_Z1fB3foov", "f[abi:foo]()"}, + {"_Z1fB3fooB3barv", "f[abi:foo][abi:bar]()"}, + {"_ZN1SB5outer1fB5innerEv", "S[abi:outer]::f[abi:inner]()"}, + {"_ZN1SC2B8ctor_tagEv", "S::S[abi:ctor_tag]()"}, + {"_ZplB4MERP1SS_", "operator+[abi:MERP](S, S)"}, + + {"_Z1fIJifcEEvDp5unaryIT_E", "void f<int, float, char>(unary<int>, unary<float>, unary<char>)"}, + {"_Z1fIJEJiEEvDpT_DpT0_", "void f<int>(int)"}, + {"_Z1fIJicEEvDp7MuncherIAstT__S1_E", "void f<int, char>(Muncher<int [sizeof (int)]>, Muncher<char [sizeof (char)]>)"}, + {"_ZN1SIJifcEE1fIJdjEEEiDp4MerpIJifcT_EE", "int S<int, float, char>::f<double, unsigned int>(Merp<int, float, char, double>, Merp<int, float, char, unsigned int>)"}, + {"_Z1pIJicEEiDp4MerpIXsZT_EJT_EE", "int p<int, char>(Merp<sizeof...(int, char), int>, Merp<sizeof...(int, char), char>)"}, + + // Some expression symbols found in clang's test/CodeGenCXX/mangle-exprs.cpp + {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXleT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) <= (4), void>::type*)"}, + {"_ZN5Casts6cstyleILj4EEEvPN9enable_ifIXleT_cvjLi4EEvE4typeE", "void Casts::cstyle<4u>(enable_if<(4u) <= ((unsigned int)(4)), void>::type*)"}, + {"_ZN5Casts10functionalILj4EEEvPN9enable_ifIXleT_cvjLi4EEvE4typeE", "void Casts::functional<4u>(enable_if<(4u) <= ((unsigned int)(4)), void>::type*)"}, + {"_ZN5Casts7static_ILj4EEEvPN9enable_ifIXleT_scjLi4EEvE4typeE", "void Casts::static_<4u>(enable_if<(4u) <= (static_cast<unsigned int>(4)), void>::type*)"}, + {"_ZN5Casts12reinterpret_ILj4EiEEvPN9enable_ifIXleT_szrcPT0_Li0EEvE4typeE", "void Casts::reinterpret_<4u, int>(enable_if<(4u) <= (sizeof (reinterpret_cast<int*>(0))), void>::type*)"}, + {"_ZN5Casts6const_IiXadL_ZNS_1iEEEEEvPN9enable_ifIXleLi0EszccPT_T0_EvE4typeE", "void Casts::const_<int, &(Casts::i)>(enable_if<(0) <= (sizeof (const_cast<int*>(&(Casts::i)))), void>::type*)"}, + {"_ZN5Casts8dynamic_INS_1SEXadL_ZNS_1sEEEEEvPN9enable_ifIXleLi0EszdcPT_T0_EvE4typeE", "void Casts::dynamic_<Casts::S, &(Casts::s)>(enable_if<(0) <= (sizeof (dynamic_cast<Casts::S*>(&(Casts::s)))), void>::type*)"}, + {"_ZN5Casts1fILi6EEENS_1TIXT_EEEv", "Casts::T<6> Casts::f<6>()"}, + {"_ZN5Casts5auto_IiEEvDTnw_DapicvT__EEE", "void Casts::auto_<int>(decltype(new auto((int)())))"}, + {"_ZN5Casts7scalar_IiEEvDTcmcvT__Ecvi_EE", "void Casts::scalar_<int>(decltype(((int)()) , ((int)())))"}, + {"_ZN5test11aIsEEDTcl3foocvT__EEES1_", "decltype(foo((short)())) test1::a<short>(short)"}, + {"_ZN5test21aIPFfvEEEvT_DTclfL0p_EE", "void test2::a<float (*)()>(float (*)(), decltype(fp()))"}, + {"_ZN5test21bIPFfvEEEDTclfp_EET_", "decltype(fp()) test2::b<float (*)()>(float (*)())"}, + {"_ZN5test21cIPFfvEEEvT_PFvDTclfL1p_EEE", "void test2::c<float (*)()>(float (*)(), void (*)(decltype(fp())))"}, + {"_ZN5test21dIPFfvEEEvT_PFDTclfL0p_EEvE", "void test2::d<float (*)()>(float (*)(), decltype(fp()) (*)())"}, + {"_ZN5test21eIPFfvEEEvPFDTclfp_EET_E", "void test2::e<float (*)()>(decltype(fp()) (*)(float (*)()))"}, + {"_ZN5test21fIPFfvEEEvPFvT_DTclfL0p_EEE", "void test2::f<float (*)()>(void (*)(float (*)(), decltype(fp())))"}, + {"_ZN5test21gIPFfvEEEvT_DTclfL0p_EE", "void test2::g<float (*)()>(float (*)(), decltype(fp()))"}, + {"_ZN5test21hIPFfvEEEvT_DTcvPFDTclfL0p_EEvELi0EE", "void test2::h<float (*)()>(float (*)(), decltype((decltype(fp()) (*)())(0)))"}, + {"_ZN5test21iIPFfvEEEvDTcvPFDTclfp_EET_ELi0EE", "void test2::i<float (*)()>(decltype((decltype(fp()) (*)(float (*)()))(0)))"}, + {"_ZZN5test21gIPFfvEEEvT_DTclfL0p_EEE8variable", "void test2::g<float (*)()>(float (*)(), decltype(fp()))::variable"}, + {"_ZN5test31aINS_1XEMS1_PiEEvT_T0_DTdsfL0p_fL0p0_E", "void test3::a<test3::X, int* test3::X::*>(test3::X, int* test3::X::*, decltype(fp.*fp0))"}, + {"_ZN5test43tf1INS_1XEEEvDTnw_T_piLi1EEE", "void test4::tf1<test4::X>(decltype(new test4::X(1)))"}, + {"_ZN5test51aIiEEvDTnxcvT__EE", "void test5::a<int>(decltype(noexcept ((int)())))"}, + {"_ZN5test62f1IiEEvDTcvT_dtdtL_ZNS_1zEE2ua1iE", "void test6::f1<int>(decltype((int)(test6::z.ua.i)))"}, + {"_ZN5test62f2IiEEvDTcvT_dtdtL_ZNS_1zEE2ub1iE", "void test6::f2<int>(decltype((int)(test6::z.ub.i)))"}, + {"_ZN5test62f3IiEEvDTcvT_dtdtL_ZNS_1zEE1s1iE", "void test6::f3<int>(decltype((int)(test6::z.s.i)))"}, + {"_ZN5test62f4IiEEvDTcvT_dtdtL_ZNS_1zEE4uuss1iE", "void test6::f4<int>(decltype((int)(test6::z.uuss.i)))"}, + {"_ZN5test62f5IiEEvDTcvT_dtptL_ZNS_2zpEE2ua1iE", "void test6::f5<int>(decltype((int)(test6::zp->ua.i)))"}, + {"_ZN5test62f6IiEEvDTcvT_dtptL_ZNS_2zpEE2ub1iE", "void test6::f6<int>(decltype((int)(test6::zp->ub.i)))"}, + {"_ZN5test62f7IiEEvDTcvT_dtptL_ZNS_2zpEE1s1iE", "void test6::f7<int>(decltype((int)(test6::zp->s.i)))"}, + {"_ZN5test62f8IiEEvDTcvT_dtptL_ZNS_2zpEE4uuss1iE", "void test6::f8<int>(decltype((int)(test6::zp->uuss.i)))"}, + {"_ZN5test73fD2IiEEDTcmcvNS_1DEL_ZNS_1bEEcvT__EES2_", "decltype(((test7::D)(test7::b)) , ((int)())) test7::fD2<int>(int)"}, + {"_ZN5test73fT2IiEEDTcvT__EES1_", "decltype((int)()) test7::fT2<int>(int)"}, + {"_ZN5test73fT4IiEEDTcvT_Li1EES1_", "decltype((int)(1)) test7::fT4<int>(int)"}, + {"_ZN5test73fT6INS_1BEEEDTcvT__Li1ELi2EEES2_", "decltype((test7::B)(1, 2)) test7::fT6<test7::B>(test7::B)"}, + {"_ZNK5test81XIiE3barIiEEDTcl3fooIT_EEEv", "decltype(foo<int>()) test8::X<int>::bar<int>() const"}, }; const unsigned N = sizeof(cases) / sizeof(cases[0]); @@ -29662,6 +29716,19 @@ const char* invalid_cases[] = "\x44\x74\x71\x75\x35\x2A\xDF\x74\x44\x61\x73\x63\x35\x2A\x3B\x41\x72\x4D\x6E\x65\x34\x9F\xC1\x63\x41\x72\x4D\x6E\x77\x38\x9A\x8E\x44\x6F\x64\x6C\x53\xF9\x5F\x70\x74\x70\x69\x45\x33\x44\x76\x35", "\x44\x74\x70\x74\x71\x75\x32\x43\x41\x38\x65\x6E\x9B\x72\x4D\xC1\x43\x41\x72\x4D\x6E\x77\x38\x9A\x8E\x44\x6F\x64\x6C\x53\xF9\x5F\x70\x74\x70\x69\x45\x38\xD3\x73\x9E\x2A\x37", "\x46\x44\x74\x70\x74\x71\x75\x32\x43\x41\x72\x4D\x6E\x65\x34\x9F\xC1\x43\x41\x72\x4D\x6E\x77\x38\x9A\x8E\x44\x6F\x64\x6C\x53\xF9\x5F\x70\x74\x70\x69\x45\x34\xD3\x73\x9E\x2A\x37\x72\x33\x8E\x3A\x29\x8E\x44\x35", + "_ZcvCiIJEEDvT__FFFFT_vT_v", + "Z1JIJ1_T_EE3o00EUlT_E0", + "___Z2i_D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D", + "ZcvSdIZcvSdIZcvSdIZcvSdIZcvSdIZcvSdIDv_ZcvSdIZcvSdIZcvSdIZcvSdIZcvSdIZcvSdIDv_ZcvSdIZcvSdIZcvSdIZcvSdIZcvSdIZcvSdIDv_Dv_Dv_Dv_Dv_dZcvSdIZcvSdIZcvSdIZcvSdIZcvSdIZcvSdIDv_ZcvSdIZcvSdIZcvSdIZcvSdIZcvSdIZcvSdIDv_ZcvSdIZcvSdIZcvSdIZcvSdIZcvSdIZcvSdIDv_Dv_Dv_Dv_Dv_d", + "Z1 Z1 IJEEAcvZcvT_EcvT_T_", + "T_IZaaIJEEAnaaaT_T__", + "PT_IJPNT_IJEET_T_T_T_)J)JKE", + "1 IJEVNT_T_T_EE", + "AT__ZSiIJEEAnwscT_T__", + "FSiIJEENT_IoE ", + "ZTVSiIZTVSiIZTVSiIZTVSiINIJEET_T_T_T_T_ ", + "_ZSiIJEvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvttvvvvvvET_v", + "Ana_T_E_T_IJEffffffffffffffersfffffrsrsffffffbgE", }; const unsigned NI = sizeof(invalid_cases) / sizeof(invalid_cases[0]); @@ -29696,7 +29763,7 @@ void test() free(buf); } -void test2() +void test_invalid_cases() { std::size_t len = 0; char* buf = nullptr; @@ -29717,6 +29784,66 @@ void test2() free(buf); } +const char *xfail_cases[] = { + "_Z1fUa9enable_ifIXLi1EEEv", // enable_if attribute + "_ZDC2a12a2E", // decomposition decl + "_ZW6FooBarE2f3v", // C++ modules TS + + // FIXME: Why does clang generate the "cp" expr? + "_ZN5test11bIsEEDTcp3foocvT__EEES1_", + + // Initializer list expressions: + "_ZN5test43tf2INS_1XEEEvDTnw_T_piilLi1EEEE", + "_ZN5test43tf3INS_1XEEEvDTnw_T_ilLi1EEE", + "_ZN5test73fA1IiEEDTcmtlNS_1AELi1ELi2EEcvT__EES2_", + "_ZN5test73fA2IiEEDTcmcvNS_1AEilLi1ELi2EEcvT__EES2_", + "_ZN5test73fB1IiEEDTcmtlNS_1BELi1ELi2EEcvT__EES2_", + "_ZN5test73fB2IiEEDTcmcvNS_1BEilLi1ELi2EEcvT__EES2_", + "_ZN5test73fC1IiEEDTcmtlNS_1CEilLi1ELi2EEEcvT__EES2_", + "_ZN5test73fC2IiEEDTcmcvNS_1CEilLi1ELi2EEcvT__EES2_", + "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bEEEcvT__EES2_", + "_ZN5test73fE1IiEEDTcmtlNS_1EELi1ELi2EEcvT__EES2_", + "_ZN5test73fE2IiEEDTcmcvNS_1EEilLi1ELi2EEcvT__EES2_", + "_ZN5test73fF1IiEEDTcmtlNS_1FEilLi1ELi2EEEcvT__EES2_", + "_ZN5test73fF2IiEEDTcmcvNS_1FEilLi1ELi2EEcvT__EES2_", + "_ZN5test73fT1IiEEDTtlT_EES1_", + "_ZN5test73fT3IiEEDTtlT_Li1EEES1_", + "_ZN5test73fT5INS_1BEEEDTtlT_Li1ELi2EEES2_", + "_ZN5test73fT7INS_1AEEEDTtlT_ilEEES2_", + "_ZN5test73fT8INS_1AEEEDTcvT_ilEES2_", + "_ZN5test73fT9INS_1AEEEDTtlT_ilLi1EEEES2_", + "_ZN5test73fTAINS_1AEEEDTcvT_ilLi1EEES2_", + "_ZN5test73fTBINS_1CEEEDTtlT_ilLi1ELi2EEEES2_", + "_ZN5test73fTCINS_1CEEEDTcvT_ilLi1ELi2EEES2_", + + // Designated init expressions + "_ZN15designated_init1fINS_1AEEEvDTtlT_di1adi1bdxLi3EdXLi1ELi4ELi9EEE", +}; + +const size_t num_xfails = sizeof(xfail_cases) / sizeof(xfail_cases[0]); + +void test_xfail_cases() +{ + std::size_t len = 0; + char* buf = nullptr; + for (std::size_t i = 0; i < num_xfails; ++i) + { + int status; + char* demang = __cxxabiv1::__cxa_demangle(xfail_cases[i], buf, &len, &status); + if (status != -2) + { + std::cout << xfail_cases[i] << " was documented as xfail but passed\n" + << "got status = " << status << '\n'; + assert(status == -2); + } + else + { + buf = demang; + } + } + free(buf); +} + void testFPLiterals() { std::size_t len = 0; @@ -29753,7 +29880,8 @@ int main() { timer t; test(); - test2(); + test_invalid_cases(); + test_xfail_cases(); testFPLiterals(); } #if 0 diff --git a/test/test_exception_address_alignment.pass.cpp b/test/test_exception_address_alignment.pass.cpp new file mode 100644 index 0000000..138fb4d --- /dev/null +++ b/test/test_exception_address_alignment.pass.cpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// 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. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: libcxxabi-no-exceptions +// UNSUPPORTED: c++98, c++03 + +// The system unwind.h on OS X provides an incorrectly aligned _Unwind_Exception +// type. That causes these tests to fail. This XFAIL is my best attempt at +// working around this failure. +// XFAIL: darwin && libcxxabi-has-system-unwinder + +// Test that the address of the exception object is properly aligned as required +// by the relevant ABI + +#include <cstdint> +#include <cassert> + +#include <unwind.h> + +struct __attribute__((aligned)) AlignedType {}; + +// EHABI : 8-byte aligned +// Itanium: Largest supported alignment for the system +#if defined(_LIBUNWIND_ARM_EHABI) +# define EXPECTED_ALIGNMENT 8 +#else +# define EXPECTED_ALIGNMENT alignof(AlignedType) +#endif + +static_assert(alignof(_Unwind_Exception) == EXPECTED_ALIGNMENT, + "_Unwind_Exception is incorrectly aligned. This test is expected to fail"); + +struct MinAligned { }; +static_assert(alignof(MinAligned) == 1 && sizeof(MinAligned) == 1, ""); + +int main() { + for (int i=0; i < 10; ++i) { + try { + throw MinAligned{}; + } catch (MinAligned const& ref) { + assert(reinterpret_cast<uintptr_t>(&ref) % EXPECTED_ALIGNMENT == 0); + } + } +} diff --git a/test/test_exception_storage.pass.cpp b/test/test_exception_storage.pass.cpp index 67ab51d..49c9b5d 100644 --- a/test/test_exception_storage.pass.cpp +++ b/test/test_exception_storage.pass.cpp @@ -7,7 +7,10 @@ // //===----------------------------------------------------------------------===// -#include "../src/config.h" +// FIXME: cxa_exception.hpp directly references `std::unexpected` and friends. +// This breaks this test when compiled in C++17. For now fix this by manually +// re-enabling the STL functions. +#define _LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS #include <cstdlib> #include <algorithm> diff --git a/test/test_guard.pass.cpp b/test/test_guard.pass.cpp index ef86717..dc200cf 100644 --- a/test/test_guard.pass.cpp +++ b/test/test_guard.pass.cpp @@ -7,7 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "../src/config.h" #include "cxxabi.h" #include <cassert> diff --git a/test/unittest_demangle.pass.cpp b/test/unittest_demangle.pass.cpp new file mode 100644 index 0000000..3ec869c --- /dev/null +++ b/test/unittest_demangle.pass.cpp @@ -0,0 +1,87 @@ +//===----------------------- unittest_demangle.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. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +#include "../src/cxa_demangle.cpp" + +using namespace __cxxabiv1; + +void testPODSmallVector() { + { // {push/pop}_back + PODSmallVector<int, 1> PSV; + PSV.push_back(0); + PSV.push_back(1); + PSV.push_back(2); + PSV.push_back(3); + for (int i = 0; i < 4; ++i) + assert(PSV[i] == i); + PSV.pop_back(); + for (int i = 0; i < 3; ++i) + assert(PSV[i] == i); + PSV.pop_back(); + PSV.pop_back(); + assert(!PSV.empty() && PSV.size() == 1); + PSV.pop_back(); + assert(PSV.empty() && PSV.size() == 0); + } + + { + PODSmallVector<int, 1> PSV1; + PSV1.push_back(1); + PSV1.push_back(2); + PSV1.push_back(3); + + PODSmallVector<int, 1> PSV2; + std::swap(PSV1, PSV2); + assert(PSV1.size() == 0); + assert(PSV2.size() == 3); + int i = 1; + for (int x : PSV2) { + assert(x == i); + ++i; + } + assert(i == 4); + std::swap(PSV1, PSV2); + assert(PSV1.size() == 3); + assert(PSV2.size() == 0); + i = 1; + for (int x : PSV1) { + assert(x == i); + ++i; + } + assert(i == 4); + } + + { + PODSmallVector<int, 10> PSV1; + PODSmallVector<int, 10> PSV2; + PSV1.push_back(0); + PSV1.push_back(1); + PSV1.push_back(2); + assert(PSV1.size() == 3); + assert(PSV2.size() == 0); + std::swap(PSV1, PSV2); + assert(PSV1.size() == 0); + assert(PSV2.size() == 3); + int i = 0; + for (int x : PSV2) { + assert(x == i); + ++i; + } + for (int x : PSV1) { + assert(false); + (void)x; + } + } +} + +int main() { + testPODSmallVector(); +} |