From 791447f9868e7db61fb03c28e453ad1c8a033949 Mon Sep 17 00:00:00 2001 From: Nobody Date: Thu, 11 Aug 2022 23:09:28 +0300 Subject: [PATCH] libstdc++-v3 with MCST patches --- libstdc++-v3/Makefile.am | 2 +- libstdc++-v3/Makefile.in | 24 +- libstdc++-v3/acinclude.m4 | 4 +- libstdc++-v3/config/abi/pre/gnu.ver | 1 + .../config/os/nto-qnx6.5/ctype_base.h | 66 + .../os/nto-qnx6.5/ctype_configure_char.cc | 97 + .../config/os/nto-qnx6.5/ctype_inline.h | 74 + .../config/os/nto-qnx6.5/os_defines.h | 36 + libstdc++-v3/configure | 80 +- libstdc++-v3/configure.ac | 10 +- libstdc++-v3/configure.host | 3 + libstdc++-v3/include/backward/binders.h | 4 + libstdc++-v3/include/bits/atomic_base.h | 208 + libstdc++-v3/include/bits/c++config | 4 + libstdc++-v3/include/bits/random.tcc | 4 + libstdc++-v3/include/bits/stl_algo.h | 14 + libstdc++-v3/include/bits/stl_bvector.h | 11 + libstdc++-v3/include/bits/unique_ptr.h | 5 + libstdc++-v3/include/bits/valarray_after.h | 17 + libstdc++-v3/include/bits/valarray_before.h | 56 + libstdc++-v3/include/ext/array_allocator.h | 4 + libstdc++-v3/include/ext/mt_allocator.h | 8 + libstdc++-v3/include/ext/pool_allocator.h | 4 + libstdc++-v3/include/std/codecvt | 7 + libstdc++-v3/include/std/memory | 4 + libstdc++-v3/include/std/thread | 4 + libstdc++-v3/libsupc++/Makefile.am | 24 +- libstdc++-v3/libsupc++/Makefile.in | 27 +- libstdc++-v3/libsupc++/cxa/basics.h | 98 + libstdc++-v3/libsupc++/cxa/decode.c | 8428 +++++++++++++++++ libstdc++-v3/libsupc++/cxa/decode.h | 47 + libstdc++-v3/libsupc++/cxa/defines.h | 5 + libstdc++-v3/libsupc++/eh_personality.cc | 18 +- .../libsupc++/fundamental_type_info.cc | 57 + libstdc++-v3/libsupc++/guard.cc | 30 +- libstdc++-v3/libsupc++/new_opa.cc | 5 +- libstdc++-v3/libsupc++/tinfo.cc | 4 + libstdc++-v3/libsupc++/tinfo.h | 4 + libstdc++-v3/libsupc++/tinfo2.cc | 4 + libstdc++-v3/libsupc++/typeinfo | 15 + libstdc++-v3/src/c++11/cow-stdexcept.cc | 4 + libstdc++-v3/src/c++11/futex.cc | 31 +- libstdc++-v3/src/c++98/compatibility.cc | 13 +- 43 files changed, 9439 insertions(+), 126 deletions(-) create mode 100644 libstdc++-v3/config/os/nto-qnx6.5/ctype_base.h create mode 100644 libstdc++-v3/config/os/nto-qnx6.5/ctype_configure_char.cc create mode 100644 libstdc++-v3/config/os/nto-qnx6.5/ctype_inline.h create mode 100644 libstdc++-v3/config/os/nto-qnx6.5/os_defines.h create mode 100644 libstdc++-v3/libsupc++/cxa/basics.h create mode 100644 libstdc++-v3/libsupc++/cxa/decode.c create mode 100644 libstdc++-v3/libsupc++/cxa/decode.h create mode 100644 libstdc++-v3/libsupc++/cxa/defines.h diff --git a/libstdc++-v3/Makefile.am b/libstdc++-v3/Makefile.am index 2b18a24183a..f139a1901fe 100644 --- a/libstdc++-v3/Makefile.am +++ b/libstdc++-v3/Makefile.am @@ -24,7 +24,7 @@ include $(top_srcdir)/fragment.am if GLIBCXX_HOSTED ## Note that python must come after src. - hosted_source = src doc po testsuite python + hosted_source = src endif ## Keep this list sync'd with acinclude.m4:GLIBCXX_CONFIGURE. diff --git a/libstdc++-v3/Makefile.in b/libstdc++-v3/Makefile.in index 0ed59a43405..44c919a9437 100644 --- a/libstdc++-v3/Makefile.in +++ b/libstdc++-v3/Makefile.in @@ -51,13 +51,10 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ -DIST_COMMON = $(top_srcdir)/fragment.am README ChangeLog \ - $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(top_srcdir)/configure $(am__configure_deps) \ - $(srcdir)/config.h.in $(srcdir)/../mkinstalldirs \ - $(top_srcdir)/scripts/testsuite_flags.in \ - $(top_srcdir)/scripts/extract_symvers.in \ - $(top_srcdir)/doc/xsl/customization.xsl.in +DIST_COMMON = $(top_srcdir)/fragment.am $(srcdir)/Makefile.in \ + $(srcdir)/Makefile.am $(top_srcdir)/configure \ + $(am__configure_deps) $(srcdir)/config.h.in \ + $(srcdir)/../mkinstalldirs subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \ @@ -87,8 +84,7 @@ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno CONFIG_HEADER = config.h -CONFIG_CLEAN_FILES = scripts/testsuite_flags scripts/extract_symvers \ - doc/xsl/customization.xsl +CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = depcomp = am__depfiles_maybe = @@ -117,7 +113,7 @@ AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS ETAGS = etags CTAGS = ctags -DIST_SUBDIRS = include libsupc++ src doc po testsuite python +DIST_SUBDIRS = include libsupc++ src ABI_TWEAKS_SRCDIR = @ABI_TWEAKS_SRCDIR@ ACLOCAL = @ACLOCAL@ ALLOCATOR_H = @ALLOCATOR_H@ @@ -342,7 +338,7 @@ WARN_CXXFLAGS = \ # -I/-D flags to pass when compiling. AM_CPPFLAGS = $(GLIBCXX_INCLUDES) $(CPPFLAGS) -@GLIBCXX_HOSTED_TRUE@hosted_source = src doc po testsuite python +@GLIBCXX_HOSTED_TRUE@hosted_source = src SUBDIRS = include libsupc++ $(hosted_source) ACLOCAL_AMFLAGS = -I . -I .. -I ../config @BUILD_XML_FALSE@STAMP_XML = @@ -467,12 +463,6 @@ $(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) distclean-hdr: -rm -f config.h stamp-h1 -scripts/testsuite_flags: $(top_builddir)/config.status $(top_srcdir)/scripts/testsuite_flags.in - cd $(top_builddir) && $(SHELL) ./config.status $@ -scripts/extract_symvers: $(top_builddir)/config.status $(top_srcdir)/scripts/extract_symvers.in - cd $(top_builddir) && $(SHELL) ./config.status $@ -doc/xsl/customization.xsl: $(top_builddir)/config.status $(top_srcdir)/doc/xsl/customization.xsl.in - cd $(top_builddir) && $(SHELL) ./config.status $@ mostlyclean-libtool: -rm -f *.lo diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4 index 805148d3b7d..c6cc8216c72 100644 --- a/libstdc++-v3/acinclude.m4 +++ b/libstdc++-v3/acinclude.m4 @@ -49,7 +49,7 @@ AC_DEFUN([GLIBCXX_CONFIGURE], [ # Keep these sync'd with the list in Makefile.am. The first provides an # expandable list at autoconf time; the second provides an expandable list # (i.e., shell variable) at configure time. - m4_define([glibcxx_SUBDIRS],[include libsupc++ src src/c++98 src/c++11 src/filesystem doc po testsuite python]) + m4_define([glibcxx_SUBDIRS],[include libsupc++ src src/c++98 src/c++11 src/filesystem]) SUBDIRS='glibcxx_SUBDIRS' # These need to be absolute paths, yet at the same time need to @@ -816,7 +816,7 @@ AC_DEFUN([GLIBCXX_EXPORT_INSTALL_INFO], [ glibcxx_toolexecdir='${libdir}/gcc/${host_alias}' glibcxx_toolexeclibdir='${libdir}' fi - multi_os_directory=`$CXX -print-multi-os-directory` + multi_os_directory=. case $multi_os_directory in .) ;; # Avoid trailing /. *) glibcxx_toolexeclibdir=$glibcxx_toolexeclibdir/$multi_os_directory ;; diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index f365da116f0..a000bf23a33 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -1985,6 +1985,7 @@ CXXABI_1.3 { __cxa_begin_catch; __cxa_begin_cleanup; __cxa_call_unexpected; + __cxa_call_terminate; __cxa_current_exception_type; __cxa_demangle; __cxa_end_catch; diff --git a/libstdc++-v3/config/os/nto-qnx6.5/ctype_base.h b/libstdc++-v3/config/os/nto-qnx6.5/ctype_base.h new file mode 100644 index 00000000000..db6d12b0822 --- /dev/null +++ b/libstdc++-v3/config/os/nto-qnx6.5/ctype_base.h @@ -0,0 +1,66 @@ +// Locale support -*- C++ -*- + +// Copyright (C) 2002-2015 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file bits/ctype_base.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{locale} + */ + +// +// ISO C++ 14882: 22.1 Locales +// + +// Information as gleaned from /usr/include/ctype.h. + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + /// @brief Base class for ctype. + struct ctype_base + { + // Non-standard typedefs. + typedef const unsigned char* __to_type; + + // NB: Offsets into ctype::_M_table force a particular size + // on the mask type. Because of this, we don't use an enum. + typedef short mask; + static const mask upper = _UP; + static const mask lower = _LO; + static const mask alpha = _LO | _UP | _XA; + static const mask digit = _DI; + static const mask xdigit = _XD; + static const mask space = _CN | _SP | _XS; + static const mask print = _DI | _LO | _PU | _SP | _UP | _XA; + static const mask graph = _DI | _LO | _PU | _UP | _XA; + static const mask cntrl = _BB; + static const mask punct = _PU; + static const mask alnum = _DI | _LO | _UP | _XA; +#if __cplusplus >= 201103L + static const mask blank = _SP | _XB; +#endif + }; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace diff --git a/libstdc++-v3/config/os/nto-qnx6.5/ctype_configure_char.cc b/libstdc++-v3/config/os/nto-qnx6.5/ctype_configure_char.cc new file mode 100644 index 00000000000..7b010d69bb0 --- /dev/null +++ b/libstdc++-v3/config/os/nto-qnx6.5/ctype_configure_char.cc @@ -0,0 +1,97 @@ +// Locale support -*- C++ -*- + +// Copyright (C) 2011-2015 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file ctype_configure_char.cc */ + +// +// ISO C++ 14882: 22.1 Locales +// + +#include +#include +#include + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +// Information as gleaned from /usr/include/ctype.h + + const ctype_base::mask* + ctype::classic_table() throw() + { return 0; } + + ctype::ctype(__c_locale, const mask* __table, bool __del, + size_t __refs) + : facet(__refs), _M_del(__table != 0 && __del), + _M_toupper(NULL), _M_tolower(NULL), _M_table(__table ? __table : _Ctype) + { + memset(_M_widen, 0, sizeof(_M_widen)); + _M_widen_ok = 0; + memset(_M_narrow, 0, sizeof(_M_narrow)); + _M_narrow_ok = 0; + } + + ctype::ctype(const mask* __table, bool __del, size_t __refs) + : facet(__refs), _M_del(__table != 0 && __del), + _M_toupper(NULL), _M_tolower(NULL), _M_table(__table ? __table : _Ctype) + { + memset(_M_widen, 0, sizeof(_M_widen)); + _M_widen_ok = 0; + memset(_M_narrow, 0, sizeof(_M_narrow)); + _M_narrow_ok = 0; + } + + char + ctype::do_toupper(char __c) const + { return ::toupper((int) __c); } + + const char* + ctype::do_toupper(char* __low, const char* __high) const + { + while (__low < __high) + { + *__low = ::toupper((int) *__low); + ++__low; + } + return __high; + } + + char + ctype::do_tolower(char __c) const + { return ::tolower((int) __c); } + + const char* + ctype::do_tolower(char* __low, const char* __high) const + { + while (__low < __high) + { + *__low = ::tolower((int) *__low); + ++__low; + } + return __high; + } + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace diff --git a/libstdc++-v3/config/os/nto-qnx6.5/ctype_inline.h b/libstdc++-v3/config/os/nto-qnx6.5/ctype_inline.h new file mode 100644 index 00000000000..05f5f7a2e25 --- /dev/null +++ b/libstdc++-v3/config/os/nto-qnx6.5/ctype_inline.h @@ -0,0 +1,74 @@ +// Locale support -*- C++ -*- + +// Copyright (C) 2002-2015 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file bits/ctype_inline.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{locale} + */ + +// +// ISO C++ 14882: 22.1 Locales +// + +// ctype bits to be inlined go here. Non-inlinable (ie virtual do_*) +// functions go in ctype.cc + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + bool + ctype:: + is(mask __m, char __c) const + { return _M_table[(unsigned char)(__c)] & __m; } + + const char* + ctype:: + is(const char* __low, const char* __high, mask* __vec) const + { + while (__low < __high) + *__vec++ = _M_table[*__low++]; + return __high; + } + + const char* + ctype:: + scan_is(mask __m, const char* __low, const char* __high) const + { + while (__low < __high && !this->is(__m, *__low)) + ++__low; + return __low; + } + + const char* + ctype:: + scan_not(mask __m, const char* __low, const char* __high) const + { + while (__low < __high && this->is(__m, *__low) != 0) + ++__low; + return __low; + } + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace diff --git a/libstdc++-v3/config/os/nto-qnx6.5/os_defines.h b/libstdc++-v3/config/os/nto-qnx6.5/os_defines.h new file mode 100644 index 00000000000..fa976a4219e --- /dev/null +++ b/libstdc++-v3/config/os/nto-qnx6.5/os_defines.h @@ -0,0 +1,36 @@ +// Specific definitions for QNX 6.1 -*- C++ -*- + +// Copyright (C) 2002-2015 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +#ifndef _GLIBCXX_OS_DEFINES +#define _GLIBCXX_OS_DEFINES 1 + +// System-specific #define, typedefs, corrections, etc, go here. This +// file will come before all others. + +#if __cplusplus >= 199711L +#define __CORRECT_ISO_CPP_STRING_H_PROTO +#define __CORRECT_ISO_CPP_WCHAR_H_PROTO +#endif + +#endif diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure index e70fdd6421f..3059c4d0e23 100755 --- a/libstdc++-v3/configure +++ b/libstdc++-v3/configure @@ -4959,7 +4959,7 @@ $as_echo "$ac_cv_path_EGREP" >&6; } # expandable list at autoconf time; the second provides an expandable list # (i.e., shell variable) at configure time. - SUBDIRS='include libsupc++ src src/c++98 src/c++11 src/filesystem doc po testsuite python' + SUBDIRS='include libsupc++ src src/c++98 src/c++11 src/filesystem' # These need to be absolute paths, yet at the same time need to # canonicalize only relative paths, because then amd will not unmount @@ -8698,7 +8698,7 @@ $as_echo_n "checking for $compiler option to produce PIC... " >&6; } *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. - lt_prog_compiler_pic='-fPIC -shared' + lt_prog_compiler_pic='-fPIC' ;; sysv4*MP*) @@ -8825,7 +8825,7 @@ $as_echo_n "checking for $compiler option to produce PIC... " >&6; } *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. - lt_prog_compiler_pic='-fPIC -shared' + lt_prog_compiler_pic='-fPIC' ;; osf3* | osf4* | osf5*) @@ -13576,7 +13576,7 @@ $as_echo_n "checking for $compiler option to produce PIC... " >&6; } *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. - lt_prog_compiler_pic_CXX='-fPIC -shared' + lt_prog_compiler_pic_CXX='-fPIC' ;; *) lt_prog_compiler_pic_CXX='-fPIC' @@ -13726,7 +13726,7 @@ $as_echo_n "checking for $compiler option to produce PIC... " >&6; } *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. - lt_prog_compiler_pic_CXX='-fPIC -shared' + lt_prog_compiler_pic_CXX='-fPIC' ;; osf3* | osf4* | osf5*) case $cc_basename in @@ -21811,7 +21811,7 @@ done # Only do link tests if native. Else, hardcode. -if $GLIBCXX_IS_NATIVE; then +if $GLIBCXX_IS_NATIVE || true; then # We can do more elaborate tests that assume a working linker. CANADIAN=no @@ -79557,7 +79557,7 @@ int main () { -#if !defined __LONG_DOUBLE_128__ || (defined(__sparc__) && defined(__arch64__)) +#if !defined __LONG_DOUBLE_128__ || (defined(__sparc__) && defined(__arch64__)) || defined __LCC__ #error no need for long double compatibility #endif @@ -81599,7 +81599,7 @@ $as_echo "$version_specific_libs" >&6; } glibcxx_toolexecdir='${libdir}/gcc/${host_alias}' glibcxx_toolexeclibdir='${libdir}' fi - multi_os_directory=`$CXX -print-multi-os-directory` + multi_os_directory=. case $multi_os_directory in .) ;; # Avoid trailing /. *) glibcxx_toolexeclibdir=$glibcxx_toolexeclibdir/$multi_os_directory ;; @@ -81667,12 +81667,9 @@ fi ac_config_files="$ac_config_files Makefile" -ac_config_files="$ac_config_files scripts/testsuite_flags" - -ac_config_files="$ac_config_files scripts/extract_symvers" - -ac_config_files="$ac_config_files doc/xsl/customization.xsl" - +#AC_CONFIG_FILES([scripts/testsuite_flags],[chmod +x scripts/testsuite_flags]) +#AC_CONFIG_FILES([scripts/extract_symvers],[chmod +x scripts/extract_symvers]) +#AC_CONFIG_FILES([doc/xsl/customization.xsl]) # Multilibs need MULTISUBDIR defined correctly in certain makefiles so # that multilib installs will end up installed in the correct place. @@ -81682,7 +81679,7 @@ ac_config_files="$ac_config_files doc/xsl/customization.xsl" # append it here. Only modify Makefiles that have just been created. # # Also, get rid of this simulated-VPATH thing that automake does. -ac_config_files="$ac_config_files include/Makefile libsupc++/Makefile src/Makefile src/c++98/Makefile src/c++11/Makefile src/filesystem/Makefile doc/Makefile po/Makefile testsuite/Makefile python/Makefile" +ac_config_files="$ac_config_files include/Makefile libsupc++/Makefile src/Makefile src/c++98/Makefile src/c++11/Makefile src/filesystem/Makefile" ac_config_commands="$ac_config_commands generate-headers" @@ -82901,19 +82898,12 @@ do "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; "include/gstdint.h") CONFIG_COMMANDS="$CONFIG_COMMANDS include/gstdint.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; - "scripts/testsuite_flags") CONFIG_FILES="$CONFIG_FILES scripts/testsuite_flags" ;; - "scripts/extract_symvers") CONFIG_FILES="$CONFIG_FILES scripts/extract_symvers" ;; - "doc/xsl/customization.xsl") CONFIG_FILES="$CONFIG_FILES doc/xsl/customization.xsl" ;; "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;; "libsupc++/Makefile") CONFIG_FILES="$CONFIG_FILES libsupc++/Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "src/c++98/Makefile") CONFIG_FILES="$CONFIG_FILES src/c++98/Makefile" ;; "src/c++11/Makefile") CONFIG_FILES="$CONFIG_FILES src/c++11/Makefile" ;; "src/filesystem/Makefile") CONFIG_FILES="$CONFIG_FILES src/filesystem/Makefile" ;; - "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; - "po/Makefile") CONFIG_FILES="$CONFIG_FILES po/Makefile" ;; - "testsuite/Makefile") CONFIG_FILES="$CONFIG_FILES testsuite/Makefile" ;; - "python/Makefile") CONFIG_FILES="$CONFIG_FILES python/Makefile" ;; "generate-headers") CONFIG_COMMANDS="$CONFIG_COMMANDS generate-headers" ;; *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;; @@ -84689,8 +84679,6 @@ else fi ;; - "scripts/testsuite_flags":F) chmod +x scripts/testsuite_flags ;; - "scripts/extract_symvers":F) chmod +x scripts/extract_symvers ;; "include/Makefile":F) cat > vpsed$$ << \_EOF s!`test -f '$<' || echo '$(srcdir)/'`!! _EOF @@ -84748,50 +84736,6 @@ _EOF ;; "src/filesystem/Makefile":F) cat > vpsed$$ << \_EOF s!`test -f '$<' || echo '$(srcdir)/'`!! -_EOF - sed -f vpsed$$ $ac_file > tmp$$ - mv tmp$$ $ac_file - rm vpsed$$ - echo 'MULTISUBDIR =' >> $ac_file - ml_norecursion=yes - . ${multi_basedir}/config-ml.in - { ml_norecursion=; unset ml_norecursion;} - ;; - "doc/Makefile":F) cat > vpsed$$ << \_EOF -s!`test -f '$<' || echo '$(srcdir)/'`!! -_EOF - sed -f vpsed$$ $ac_file > tmp$$ - mv tmp$$ $ac_file - rm vpsed$$ - echo 'MULTISUBDIR =' >> $ac_file - ml_norecursion=yes - . ${multi_basedir}/config-ml.in - { ml_norecursion=; unset ml_norecursion;} - ;; - "po/Makefile":F) cat > vpsed$$ << \_EOF -s!`test -f '$<' || echo '$(srcdir)/'`!! -_EOF - sed -f vpsed$$ $ac_file > tmp$$ - mv tmp$$ $ac_file - rm vpsed$$ - echo 'MULTISUBDIR =' >> $ac_file - ml_norecursion=yes - . ${multi_basedir}/config-ml.in - { ml_norecursion=; unset ml_norecursion;} - ;; - "testsuite/Makefile":F) cat > vpsed$$ << \_EOF -s!`test -f '$<' || echo '$(srcdir)/'`!! -_EOF - sed -f vpsed$$ $ac_file > tmp$$ - mv tmp$$ $ac_file - rm vpsed$$ - echo 'MULTISUBDIR =' >> $ac_file - ml_norecursion=yes - . ${multi_basedir}/config-ml.in - { ml_norecursion=; unset ml_norecursion;} - ;; - "python/Makefile":F) cat > vpsed$$ << \_EOF -s!`test -f '$<' || echo '$(srcdir)/'`!! _EOF sed -f vpsed$$ $ac_file > tmp$$ mv tmp$$ $ac_file diff --git a/libstdc++-v3/configure.ac b/libstdc++-v3/configure.ac index 8e973503be0..12f19c6e639 100644 --- a/libstdc++-v3/configure.ac +++ b/libstdc++-v3/configure.ac @@ -240,7 +240,7 @@ sys/resource.h sys/sem.h sys/stat.h sys/time.h sys/types.h unistd.h \ wchar.h wctype.h]) # Only do link tests if native. Else, hardcode. -if $GLIBCXX_IS_NATIVE; then +if $GLIBCXX_IS_NATIVE || true; then # We can do more elaborate tests that assume a working linker. CANADIAN=no @@ -382,7 +382,7 @@ case "$target" in s390*-*-linux* | \ alpha*-*-linux*) AC_TRY_COMPILE(, [ -#if !defined __LONG_DOUBLE_128__ || (defined(__sparc__) && defined(__arch64__)) +#if !defined __LONG_DOUBLE_128__ || (defined(__sparc__) && defined(__arch64__)) || defined __LCC__ #error no need for long double compatibility #endif ], [ac_ldbl_compat=yes], [ac_ldbl_compat=no]) @@ -516,9 +516,9 @@ dnl In autoconf 2.5x, AC_OUTPUT is replaced by four AC_CONFIG_* macros, dnl which can all be called multiple times as needed, plus one (different) dnl AC_OUTPUT macro. This one lists the files to be created: AC_CONFIG_FILES(Makefile) -AC_CONFIG_FILES([scripts/testsuite_flags],[chmod +x scripts/testsuite_flags]) -AC_CONFIG_FILES([scripts/extract_symvers],[chmod +x scripts/extract_symvers]) -AC_CONFIG_FILES([doc/xsl/customization.xsl]) +#AC_CONFIG_FILES([scripts/testsuite_flags],[chmod +x scripts/testsuite_flags]) +#AC_CONFIG_FILES([scripts/extract_symvers],[chmod +x scripts/extract_symvers]) +#AC_CONFIG_FILES([doc/xsl/customization.xsl]) # Multilibs need MULTISUBDIR defined correctly in certain makefiles so # that multilib installs will end up installed in the correct place. diff --git a/libstdc++-v3/configure.host b/libstdc++-v3/configure.host index caea9de9c76..94167e6bddb 100644 --- a/libstdc++-v3/configure.host +++ b/libstdc++-v3/configure.host @@ -289,6 +289,9 @@ case "${host_os}" in netbsd*) os_include_dir="os/bsd/netbsd" ;; + nto-qnx6.5.*) + os_include_dir="os/nto-qnx6.5" + ;; openbsd*) os_include_dir="os/bsd/openbsd" ;; diff --git a/libstdc++-v3/include/backward/binders.h b/libstdc++-v3/include/backward/binders.h index 7564a6c1274..dda4ce3d619 100644 --- a/libstdc++-v3/include/backward/binders.h +++ b/libstdc++-v3/include/backward/binders.h @@ -57,8 +57,10 @@ #define _BACKWARD_BINDERS_H 1 // Suppress deprecated warning for this file. +#ifndef __LCC__ // suppress unrecognized pragma warning #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif // __LCC__ namespace std _GLIBCXX_VISIBILITY(default) { @@ -177,6 +179,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_END_NAMESPACE_VERSION } // namespace +#ifndef __LCC__ // suppress unrecognized pragma warning #pragma GCC diagnostic pop +#endif // __LCC__ #endif /* _BACKWARD_BINDERS_H */ diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index e79ff67ea06..1b2adda9916 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -624,43 +624,155 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __pointer_type operator++() noexcept +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + { + memory_order const m = memory_order_seq_cst; + __pointer_type p_new, p_old; + do + { + p_old = load(m); + p_new = p_old + 1; + } + while (! __atomic_compare_exchange(&_M_p, &p_old, &p_new, 0, m, m)); + return p_new; + } +#else { return __atomic_add_fetch(&_M_p, _M_type_size(1), memory_order_seq_cst); } +#endif __pointer_type operator++() volatile noexcept +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + { + memory_order const m = memory_order_seq_cst; + __pointer_type p_new, p_old; + do + { + p_old = load(m); + p_new = p_old + 1; + } + while (! __atomic_compare_exchange(&_M_p, &p_old, &p_new, 0, m, m)); + return p_new; + } +#else { return __atomic_add_fetch(&_M_p, _M_type_size(1), memory_order_seq_cst); } +#endif __pointer_type operator--() noexcept +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + { + memory_order const m = memory_order_seq_cst; + __pointer_type p_new, p_old; + do + { + p_old = load(m); + p_new = p_old - 1; + } + while (! __atomic_compare_exchange(&_M_p, &p_old, &p_new, 0, m, m)); + return p_new; + } +#else { return __atomic_sub_fetch(&_M_p, _M_type_size(1), memory_order_seq_cst); } +#endif __pointer_type operator--() volatile noexcept +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + { + memory_order const m = memory_order_seq_cst; + __pointer_type p_new, p_old; + do + { + p_old = load(m); + p_new = p_old - 1; + } + while (! __atomic_compare_exchange(&_M_p, &p_old, &p_new, 0, m, m)); + return p_new; + } +#else { return __atomic_sub_fetch(&_M_p, _M_type_size(1), memory_order_seq_cst); } +#endif __pointer_type operator+=(ptrdiff_t __d) noexcept +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + { + memory_order const m = memory_order_seq_cst; + __pointer_type p_new, p_old; + do + { + p_old = load(m); + p_new = p_old + __d; + } + while (! __atomic_compare_exchange(&_M_p, &p_old, &p_new, 0, m, m)); + return p_new; + } +#else { return __atomic_add_fetch(&_M_p, _M_type_size(__d), memory_order_seq_cst); } +#endif __pointer_type operator+=(ptrdiff_t __d) volatile noexcept +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + { + memory_order const m = memory_order_seq_cst; + __pointer_type p_new, p_old; + do + { + p_old = load(m); + p_new = p_old + __d; + } + while (! __atomic_compare_exchange(&_M_p, &p_old, &p_new, 0, m, m)); + return p_new; + } +#else { return __atomic_add_fetch(&_M_p, _M_type_size(__d), memory_order_seq_cst); } +#endif __pointer_type operator-=(ptrdiff_t __d) noexcept +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + { + memory_order const m = memory_order_seq_cst; + __pointer_type p_new, p_old; + do + { + p_old = load(m); + p_new = p_old - __d; + } + while (! __atomic_compare_exchange(&_M_p, &p_old, &p_new, 0, m, m)); + return p_new; + } +#else { return __atomic_sub_fetch(&_M_p, _M_type_size(__d), memory_order_seq_cst); } +#endif __pointer_type operator-=(ptrdiff_t __d) volatile noexcept +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + { + memory_order const m = memory_order_seq_cst; + __pointer_type p_new, p_old; + do + { + p_old = load(m); + p_new = p_old - __d; + } + while (! __atomic_compare_exchange(&_M_p, &p_old, &p_new, 0, m, m)); + return p_new; + } +#else { return __atomic_sub_fetch(&_M_p, _M_type_size(__d), memory_order_seq_cst); } +#endif bool is_lock_free() const noexcept @@ -688,7 +800,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __glibcxx_assert(__b != memory_order_acq_rel); __glibcxx_assert(__b != memory_order_consume); +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + __atomic_store(&_M_p, &__p, __m); +#else __atomic_store_n(&_M_p, __p, __m); +#endif } _GLIBCXX_ALWAYS_INLINE void @@ -700,7 +816,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __glibcxx_assert(__b != memory_order_acq_rel); __glibcxx_assert(__b != memory_order_consume); +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + __atomic_store(&_M_p, &__p, __m); +#else __atomic_store_n(&_M_p, __p, __m); +#endif } _GLIBCXX_ALWAYS_INLINE __pointer_type @@ -710,7 +830,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __glibcxx_assert(__b != memory_order_release); __glibcxx_assert(__b != memory_order_acq_rel); +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + __pointer_type ret; + __atomic_load(&_M_p, &ret, __m); + return ret; +#else return __atomic_load_n(&_M_p, __m); +#endif } _GLIBCXX_ALWAYS_INLINE __pointer_type @@ -720,14 +846,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __glibcxx_assert(__b != memory_order_release); __glibcxx_assert(__b != memory_order_acq_rel); +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + __pointer_type ret; + __atomic_load(&_M_p, &ret, __m); + return ret; +#else return __atomic_load_n(&_M_p, __m); +#endif } _GLIBCXX_ALWAYS_INLINE __pointer_type exchange(__pointer_type __p, memory_order __m = memory_order_seq_cst) noexcept { +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + __pointer_type ret; + __atomic_exchange(&_M_p, &__p, &ret, __m); + return ret; +#else return __atomic_exchange_n(&_M_p, __p, __m); +#endif } @@ -735,7 +873,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION exchange(__pointer_type __p, memory_order __m = memory_order_seq_cst) volatile noexcept { +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + __pointer_type ret; + __atomic_exchange(&_M_p, &__p, &ret, __m); + return ret; +#else return __atomic_exchange_n(&_M_p, __p, __m); +#endif } _GLIBCXX_ALWAYS_INLINE bool @@ -749,7 +893,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __glibcxx_assert(__b2 != memory_order_acq_rel); __glibcxx_assert(__b2 <= __b1); +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + return __atomic_compare_exchange(&_M_p, &__p1, &__p2, 0, __m1, __m2); +#else return __atomic_compare_exchange_n(&_M_p, &__p1, __p2, 0, __m1, __m2); +#endif } _GLIBCXX_ALWAYS_INLINE bool @@ -764,28 +912,88 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __glibcxx_assert(__b2 != memory_order_acq_rel); __glibcxx_assert(__b2 <= __b1); +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + return __atomic_compare_exchange(&_M_p, &__p1, &__p2, 0, __m1, __m2); +#else return __atomic_compare_exchange_n(&_M_p, &__p1, __p2, 0, __m1, __m2); +#endif } _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + { + __pointer_type p_new, p_old; + do + { + p_old = load(__m); + p_new = p_old + __d; + } + while (! __atomic_compare_exchange(&_M_p, &p_old, &p_new, 0, + __m, __m)); + return p_old; + } +#else { return __atomic_fetch_add(&_M_p, _M_type_size(__d), __m); } +#endif _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) volatile noexcept +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + { + __pointer_type p_new, p_old; + do + { + p_old = load(__m); + p_new = p_old + __d; + } + while (! __atomic_compare_exchange(&_M_p, &p_old, &p_new, 0, + __m, __m)); + return p_old; + } +#else { return __atomic_fetch_add(&_M_p, _M_type_size(__d), __m); } +#endif _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_sub(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + { + __pointer_type p_new, p_old; + do + { + p_old = load(__m); + p_new = p_old - __d; + } + while (! __atomic_compare_exchange(&_M_p, &p_old, &p_new, 0, + __m, __m)); + return p_old; + } +#else { return __atomic_fetch_sub(&_M_p, _M_type_size(__d), __m); } +#endif _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_sub(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) volatile noexcept +#if defined(__LCC__) && defined(__ptr128__) /* mcstbug #124127 */ + { + __pointer_type p_new, p_old; + do + { + p_old = load(__m); + p_new = p_old - __d; + } + while (! __atomic_compare_exchange(&_M_p, &p_old, &p_new, 0, + __m, __m)); + return p_old; + } +#else { return __atomic_fetch_sub(&_M_p, _M_type_size(__d), __m); } +#endif }; // @} group atomics diff --git a/libstdc++-v3/include/bits/c++config b/libstdc++-v3/include/bits/c++config index 8ca6b0345b1..e97a4578f43 100644 --- a/libstdc++-v3/include/bits/c++config +++ b/libstdc++-v3/include/bits/c++config @@ -257,7 +257,11 @@ namespace __gnu_cxx inline namespace __cxx11 __attribute__((__abi_tag__ ("cxx11"))) { } } # define _GLIBCXX_NAMESPACE_CXX11 __cxx11:: +#ifdef __LCC__ // suppress warning +# define _GLIBCXX_BEGIN_NAMESPACE_CXX11 inline namespace __cxx11 { +#else // __LCC__ # define _GLIBCXX_BEGIN_NAMESPACE_CXX11 namespace __cxx11 { +#endif // __LCC__ # define _GLIBCXX_END_NAMESPACE_CXX11 } # define _GLIBCXX_DEFAULT_ABI_TAG _GLIBCXX_ABI_TAG_CXX11 #else diff --git a/libstdc++-v3/include/bits/random.tcc b/libstdc++-v3/include/bits/random.tcc index df05ebea6e9..ad5b19632de 100644 --- a/libstdc++-v3/include/bits/random.tcc +++ b/libstdc++-v3/include/bits/random.tcc @@ -1627,7 +1627,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION + __x * __param._M_lp1p; } +#ifdef __LCC__ // libstdc++/71434 + __reject = __reject || __x + __np >= __thr; +#else // __LCC__ __reject |= __x + __np >= __thr; +#endif // __LCC__ } while (__reject); diff --git a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h index 97370ed534e..b33a12bddce 100644 --- a/libstdc++-v3/include/bits/stl_algo.h +++ b/libstdc++-v3/include/bits/stl_algo.h @@ -112,6 +112,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __find_if(_RandomAccessIterator __first, _RandomAccessIterator __last, _Predicate __pred, random_access_iterator_tag) { +#ifdef __LCC__ + typename iterator_traits<_RandomAccessIterator>::difference_type + __trip_count = (__last - __first); + +#pragma loop count(9) + for (; __trip_count > 0; --__trip_count) + { + if (__pred(__first)) + return __first; + ++__first; + } + return __first; +#else // __LCC__ typename iterator_traits<_RandomAccessIterator>::difference_type __trip_count = (__last - __first) >> 2; @@ -152,6 +165,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION default: return __last; } +#endif // __LCC__ } template diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h index 37e000ad962..ddb8de69090 100644 --- a/libstdc++-v3/include/bits/stl_bvector.h +++ b/libstdc++-v3/include/bits/stl_bvector.h @@ -489,7 +489,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _Bit_pointer _M_allocate(size_t __n) +#if ! (defined (__e2k__) && defined (__ptr128__)) { return _Bit_alloc_traits::allocate(_M_impl, _S_nword(__n)); } +#else /* defined (__e2k__) && defined (__ptr128__) */ + { + /* To zero-out certain bits in the allocated _Bit_pointer they may very + well apply logical AND to its uninitalized value. Therefore, take + care of initializing it in PM. */ + _Bit_pointer res = _Bit_alloc_traits::allocate(_M_impl, _S_nword(__n)); + std::fill(res, res + _S_nword(__n), 0); + return res; + } +#endif /* defined (__e2k__) && defined (__ptr128__) */ void _M_deallocate() diff --git a/libstdc++-v3/include/bits/unique_ptr.h b/libstdc++-v3/include/bits/unique_ptr.h index a31cd67d6e3..755fe7fe04b 100644 --- a/libstdc++-v3/include/bits/unique_ptr.h +++ b/libstdc++-v3/include/bits/unique_ptr.h @@ -797,8 +797,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION size_t operator()(const unique_ptr<_Tp, _Dp>& __u) const noexcept { +#if defined(__LCC__) && defined(__QNXNTO__) // _UP is a macro in QNX-libc + typedef unique_ptr<_Tp, _Dp> _Up; + return std::hash()(__u.get()); +#else // defined(__LCC__) && defined(__QNXNTO__) typedef unique_ptr<_Tp, _Dp> _UP; return std::hash()(__u.get()); +#endif // defined(__LCC__) && defined(__QNXNTO__) } }; diff --git a/libstdc++-v3/include/bits/valarray_after.h b/libstdc++-v3/include/bits/valarray_after.h index 54ba7fed61d..e52cba342b8 100644 --- a/libstdc++-v3/include/bits/valarray_after.h +++ b/libstdc++-v3/include/bits/valarray_after.h @@ -439,6 +439,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return _Expr<_Closure, _Tp>(_Closure(__v)); \ } +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + _DEFINE_EXPR_UNARY_FUNCTION(abs, _Abs_) + _DEFINE_EXPR_UNARY_FUNCTION(cos, _Cos_) + _DEFINE_EXPR_UNARY_FUNCTION(acos, _Acos_) + _DEFINE_EXPR_UNARY_FUNCTION(cosh, _Cosh_) + _DEFINE_EXPR_UNARY_FUNCTION(sin, _Sin_) + _DEFINE_EXPR_UNARY_FUNCTION(asin, _Asin_) + _DEFINE_EXPR_UNARY_FUNCTION(sinh, _Sinh_) + _DEFINE_EXPR_UNARY_FUNCTION(tan, _Tan_) + _DEFINE_EXPR_UNARY_FUNCTION(tanh, _Tanh_) + _DEFINE_EXPR_UNARY_FUNCTION(atan, _Atan_) + _DEFINE_EXPR_UNARY_FUNCTION(exp, _Exp_) + _DEFINE_EXPR_UNARY_FUNCTION(log, _Log_) + _DEFINE_EXPR_UNARY_FUNCTION(log10, _Log10_) + _DEFINE_EXPR_UNARY_FUNCTION(sqrt, _Sqrt_) +#else // defined(__LCC__) && defined(__QNXNTO__) _DEFINE_EXPR_UNARY_FUNCTION(abs, _Abs) _DEFINE_EXPR_UNARY_FUNCTION(cos, _Cos) _DEFINE_EXPR_UNARY_FUNCTION(acos, _Acos) @@ -453,6 +469,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _DEFINE_EXPR_UNARY_FUNCTION(log, _Log) _DEFINE_EXPR_UNARY_FUNCTION(log10, _Log10) _DEFINE_EXPR_UNARY_FUNCTION(sqrt, _Sqrt) +#endif // defined(__LCC__) && defined(__QNXNTO__) #undef _DEFINE_EXPR_UNARY_FUNCTION diff --git a/libstdc++-v3/include/bits/valarray_before.h b/libstdc++-v3/include/bits/valarray_before.h index 67603e10993..6675eaae1f3 100644 --- a/libstdc++-v3/include/bits/valarray_before.h +++ b/libstdc++-v3/include/bits/valarray_before.h @@ -60,98 +60,154 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // 2) efficiency -- object functions can be easily inlined // 3) be Koenig-lookup-friendly +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + struct _Abs_ +#else // defined(__LCC__) && defined(__QNXNTO__) struct _Abs +#endif // defined(__LCC__) && defined(__QNXNTO__) { template _Tp operator()(const _Tp& __t) const { return abs(__t); } }; +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + struct _Cos_ +#else // defined(__LCC__) && defined(__QNXNTO__) struct _Cos +#endif // defined(__LCC__) && defined(__QNXNTO__) { template _Tp operator()(const _Tp& __t) const { return cos(__t); } }; +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + struct _Acos_ +#else // defined(__LCC__) && defined(__QNXNTO__) struct _Acos +#endif // defined(__LCC__) && defined(__QNXNTO__) { template _Tp operator()(const _Tp& __t) const { return acos(__t); } }; +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + struct _Cosh_ +#else // defined(__LCC__) && defined(__QNXNTO__) struct _Cosh +#endif // defined(__LCC__) && defined(__QNXNTO__) { template _Tp operator()(const _Tp& __t) const { return cosh(__t); } }; +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + struct _Sin_ +#else // defined(__LCC__) && defined(__QNXNTO__) struct _Sin +#endif // defined(__LCC__) && defined(__QNXNTO__) { template _Tp operator()(const _Tp& __t) const { return sin(__t); } }; +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + struct _Asin_ +#else // defined(__LCC__) && defined(__QNXNTO__) struct _Asin +#endif // defined(__LCC__) && defined(__QNXNTO__) { template _Tp operator()(const _Tp& __t) const { return asin(__t); } }; +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + struct _Sinh_ +#else // defined(__LCC__) && defined(__QNXNTO__) struct _Sinh +#endif // defined(__LCC__) && defined(__QNXNTO__) { template _Tp operator()(const _Tp& __t) const { return sinh(__t); } }; +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + struct _Tan_ +#else // defined(__LCC__) && defined(__QNXNTO__) struct _Tan +#endif // defined(__LCC__) && defined(__QNXNTO__) { template _Tp operator()(const _Tp& __t) const { return tan(__t); } }; +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + struct _Atan_ +#else // defined(__LCC__) && defined(__QNXNTO__) struct _Atan +#endif // defined(__LCC__) && defined(__QNXNTO__) { template _Tp operator()(const _Tp& __t) const { return atan(__t); } }; +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + struct _Tanh_ +#else // defined(__LCC__) && defined(__QNXNTO__) struct _Tanh +#endif // defined(__LCC__) && defined(__QNXNTO__) { template _Tp operator()(const _Tp& __t) const { return tanh(__t); } }; +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + struct _Exp_ +#else // defined(__LCC__) && defined(__QNXNTO__) struct _Exp +#endif // defined(__LCC__) && defined(__QNXNTO__) { template _Tp operator()(const _Tp& __t) const { return exp(__t); } }; +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + struct _Log_ +#else // defined(__LCC__) && defined(__QNXNTO__) struct _Log +#endif // defined(__LCC__) && defined(__QNXNTO__) { template _Tp operator()(const _Tp& __t) const { return log(__t); } }; +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + struct _Log10_ +#else // defined(__LCC__) && defined(__QNXNTO__) struct _Log10 +#endif // defined(__LCC__) && defined(__QNXNTO__) { template _Tp operator()(const _Tp& __t) const { return log10(__t); } }; +#if defined(__LCC__) && defined(__QNXNTO__) // libstdc++ names conflict with QNX-libc names + struct _Sqrt_ +#else // defined(__LCC__) && defined(__QNXNTO__) struct _Sqrt +#endif // defined(__LCC__) && defined(__QNXNTO__) { template _Tp operator()(const _Tp& __t) const diff --git a/libstdc++-v3/include/ext/array_allocator.h b/libstdc++-v3/include/ext/array_allocator.h index a2948e4c920..bdd4c836b38 100644 --- a/libstdc++-v3/include/ext/array_allocator.h +++ b/libstdc++-v3/include/ext/array_allocator.h @@ -39,8 +39,10 @@ #endif // Suppress deprecated warning for this file. +#ifndef __LCC__ // suppress unrecognized pragma warning #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif // __LCC__ namespace __gnu_cxx _GLIBCXX_VISIBILITY(default) { @@ -177,6 +179,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_END_NAMESPACE_VERSION } // namespace +#ifndef __LCC__ // suppress unrecognized pragma warning #pragma GCC diagnostic pop +#endif // __LCC__ #endif diff --git a/libstdc++-v3/include/ext/mt_allocator.h b/libstdc++-v3/include/ext/mt_allocator.h index effb13b7dbc..4180aba6a11 100644 --- a/libstdc++-v3/include/ext/mt_allocator.h +++ b/libstdc++-v3/include/ext/mt_allocator.h @@ -59,9 +59,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct _Tune { // Compile time constants for the default _Tune values. +#if ! (defined (__e2k__) && defined (__ptr128__)) enum { _S_align = 8 }; +#else /* defined (__e2k__) && defined (__ptr128__) */ + enum { _S_align = 16 }; +#endif /* defined (__e2k__) && defined (__ptr128__) */ enum { _S_max_bytes = 128 }; +#if ! (defined (__e2k__) && defined (__ptr128__)) enum { _S_min_bin = 8 }; +#else /* defined (__e2k__) && defined (__ptr128__) */ + enum { _S_min_bin = 16 }; +#endif /* defined (__e2k__) && defined (__ptr128__) */ enum { _S_chunk_size = 4096 - 4 * sizeof(void*) }; enum { _S_max_threads = 4096 }; enum { _S_freelist_headroom = 10 }; diff --git a/libstdc++-v3/include/ext/pool_allocator.h b/libstdc++-v3/include/ext/pool_allocator.h index 3e59fabf2cf..68cd063dcaf 100644 --- a/libstdc++-v3/include/ext/pool_allocator.h +++ b/libstdc++-v3/include/ext/pool_allocator.h @@ -79,7 +79,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { protected: +#if ! (defined (__e2k__) && defined (__ptr128__)) enum { _S_align = 8 }; +#else /* defined (__e2k__) && defined (__ptr128__) */ + enum { _S_align = 16 }; +#endif /* defined (__e2k__) && defined (__ptr128__) */ enum { _S_max_bytes = 128 }; enum { _S_free_list_size = (size_t)_S_max_bytes / (size_t)_S_align }; diff --git a/libstdc++-v3/include/std/codecvt b/libstdc++-v3/include/std/codecvt index 67e358fd035..4c5abc22284 100644 --- a/libstdc++-v3/include/std/codecvt +++ b/libstdc++-v3/include/std/codecvt @@ -58,10 +58,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION class codecvt_utf8 : public codecvt<_Elem, char, mbstate_t> { public: +#ifdef __LCC__ + explicit + codecvt_utf8(size_t __refs = 0) { __builtin_abort(); } // mcstbug #122111 + + ~codecvt_utf8() { __builtin_abort(); } // mcstbug #122111 +#else /* __LCC__ */ explicit codecvt_utf8(size_t __refs = 0); ~codecvt_utf8(); +#endif /* __LCC__ */ }; template(__aligned); +#endif } } diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread index 8e2cb68e295..e052cd3d71f 100644 --- a/libstdc++-v3/include/std/thread +++ b/libstdc++-v3/include/std/thread @@ -341,7 +341,11 @@ _GLIBCXX_END_NAMESPACE_VERSION // programs, because this_thread::get_id() != thread::id{} must be true. // We know that pthread_t is an integral type in the GNU C library. if (!__gthread_active_p()) +#if defined(__LCC__) && defined(__e2k__) && defined(__ptr128__) + return thread::id(reinterpret_cast(1)); +#else // defined(__LCC__) && defined(__e2k__) && defined(__ptr128__) return thread::id(1); +#endif // defined(__LCC__) && defined(__e2k__) && defined(__ptr128__) #endif return thread::id(__gthread_self()); } diff --git a/libstdc++-v3/libsupc++/Makefile.am b/libstdc++-v3/libsupc++/Makefile.am index 1b361876827..82126158afe 100644 --- a/libstdc++-v3/libsupc++/Makefile.am +++ b/libstdc++-v3/libsupc++/Makefile.am @@ -41,7 +41,7 @@ headers = $(std_HEADERS) $(bits_HEADERS) if GLIBCXX_HOSTED c_sources = \ - cp-demangle.c + cxa/decode.c endif sources = \ @@ -119,14 +119,22 @@ endif libsupc___la_SOURCES = $(sources) $(c_sources) $(vtv_sources) libsupc__convenience_la_SOURCES = $(sources) $(c_sources) $(vtv_sources) -cp-demangle.c: - rm -f $@ - $(LN_S) $(toplevel_srcdir)/libiberty/cp-demangle.c $@ -cp-demangle.lo: cp-demangle.c - $(LTCOMPILE) -DIN_GLIBCPP_V3 -Wno-error -c $< -cp-demangle.o: cp-demangle.c - $(C_COMPILE) -DIN_GLIBCPP_V3 -Wno-error -c $< +CXA_INCLUDES = -I$(srcdir)/cxa +CXA_CFLAGS = -fbuilding-runtime -std=c++17 -fno-strict-aliasing -Wno-error +CXA_DECODE_CFLAGS = \ + -DCOMPILE_DECODE_FOR_LIB_SRC=1 \ + -DDEFAULT_EMULATE_GNU_ABI_BUGS=1 \ + -DUSE_LONG_DOUBLE_FOR_HOST_FP_VALUE=1 +decode.lo: cxa/decode.c + $(LTCXXCOMPILE) $(CXA_INCLUDES) $(CXA_CFLAGS) $(CXA_DECODE_CFLAGS) -c -xc++ $< +decode.o: cxa/decode.c + $(CXXCOMPILE) $(CXA_INCLUDES) $(CXA_CFLAGS) $(CXA_DECODE_CFLAGS) -c -xc++ $< + +fundamental_type_info.lo: fundamental_type_info.cc + $(LTCXXCOMPILE) -fbuilding-runtime -std=gnu++14 -c $< +fundamental_type_info.o: fundamental_type_info.cc + $(CXXCOMPILE) -fbuilding-runtime -std=gnu++14 -c $< # Use special rules for the C++11 sources so that the proper flags are passed. bad_array_length.lo: bad_array_length.cc diff --git a/libstdc++-v3/libsupc++/Makefile.in b/libstdc++-v3/libsupc++/Makefile.in index 4c46eeccb96..4c177dff2c2 100644 --- a/libstdc++-v3/libsupc++/Makefile.in +++ b/libstdc++-v3/libsupc++/Makefile.in @@ -131,7 +131,7 @@ am__objects_1 = array_type_info.lo atexit_arm.lo atexit_thread.lo \ del_opvsa.lo pbase_type_info.lo pmem_type_info.lo \ pointer_type_info.lo pure.lo si_class_type_info.lo tinfo.lo \ tinfo2.lo vec.lo vmi_class_type_info.lo vterminate.lo -@GLIBCXX_HOSTED_TRUE@am__objects_2 = cp-demangle.lo +@GLIBCXX_HOSTED_TRUE@am__objects_2 = decode.lo @ENABLE_VTABLE_VERIFY_TRUE@@VTV_CYGMIN_FALSE@am__objects_3 = \ @ENABLE_VTABLE_VERIFY_TRUE@@VTV_CYGMIN_FALSE@ vtv_stubs.lo am_libsupc___la_OBJECTS = $(am__objects_1) $(am__objects_2) \ @@ -404,7 +404,7 @@ bits_HEADERS = \ headers = $(std_HEADERS) $(bits_HEADERS) @GLIBCXX_HOSTED_TRUE@c_sources = \ -@GLIBCXX_HOSTED_TRUE@ cp-demangle.c +@GLIBCXX_HOSTED_TRUE@ cxa/decode.c sources = \ array_type_info.cc \ @@ -476,6 +476,13 @@ sources = \ libsupc___la_SOURCES = $(sources) $(c_sources) $(vtv_sources) libsupc__convenience_la_SOURCES = $(sources) $(c_sources) $(vtv_sources) +CXA_INCLUDES = -I$(srcdir)/cxa +CXA_CFLAGS = -fbuilding-runtime -std=c++17 -fno-strict-aliasing -Wno-error +CXA_DECODE_CFLAGS = \ + -DCOMPILE_DECODE_FOR_LIB_SRC=1 \ + -DDEFAULT_EMULATE_GNU_ABI_BUGS=1 \ + -DUSE_LONG_DOUBLE_FOR_HOST_FP_VALUE=1 + # AM_CXXFLAGS needs to be in each subdirectory so that it can be # modified in a per-library or per-sub-library way. Need to manually @@ -852,13 +859,15 @@ uninstall-am: uninstall-bitsHEADERS uninstall-stdHEADERS \ uninstall-toolexeclibLTLIBRARIES -cp-demangle.c: - rm -f $@ - $(LN_S) $(toplevel_srcdir)/libiberty/cp-demangle.c $@ -cp-demangle.lo: cp-demangle.c - $(LTCOMPILE) -DIN_GLIBCPP_V3 -Wno-error -c $< -cp-demangle.o: cp-demangle.c - $(C_COMPILE) -DIN_GLIBCPP_V3 -Wno-error -c $< +decode.lo: cxa/decode.c + $(LTCXXCOMPILE) $(CXA_INCLUDES) $(CXA_CFLAGS) $(CXA_DECODE_CFLAGS) -c -xc++ $< +decode.o: cxa/decode.c + $(CXXCOMPILE) $(CXA_INCLUDES) $(CXA_CFLAGS) $(CXA_DECODE_CFLAGS) -c -xc++ $< + +fundamental_type_info.lo: fundamental_type_info.cc + $(LTCXXCOMPILE) -fbuilding-runtime -std=gnu++14 -c $< +fundamental_type_info.o: fundamental_type_info.cc + $(CXXCOMPILE) -fbuilding-runtime -std=gnu++14 -c $< # Use special rules for the C++11 sources so that the proper flags are passed. bad_array_length.lo: bad_array_length.cc diff --git a/libstdc++-v3/libsupc++/cxa/basics.h b/libstdc++-v3/libsupc++/cxa/basics.h new file mode 100644 index 00000000000..1dc1ddb7225 --- /dev/null +++ b/libstdc++-v3/libsupc++/cxa/basics.h @@ -0,0 +1,98 @@ +/****************************************************************************** +* \ ___ / * +* / \ * +* Edison Design Group C++ Runtime - | \^/ | - * +* \ / * +* / | | \ * +* Copyright 1992-2018 Edison Design Group Inc. [_] * +* * +******************************************************************************/ +/* +Redistribution and use in source and binary forms are permitted +provided that the above copyright notice and this paragraph are +duplicated in all source code forms. The name of Edison Design +Group, Inc. may not be used to endorse or promote products derived +from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +Any use of this software is at the user's own risk. +*/ +/* + +Declarations for all runtime routines. + +*/ + +#ifndef BASICS_H +#define BASICS_H 1 + +/* +Include the header file that supplies the default configuration +parameters for this version. +*/ + +#include "defines.h" + +/* +EXTERN is defined usually as "extern"; in the translation unit that +actually defines storage for external variables, it is defined as an +empty string. EXTERN is used on the declarations of external variables +in .h files. This scheme makes it easy to define them in only one +place while using the same source in all places. Likewise, +VAR_INITIALIZERS is defined to cause inclusion of initializers for those +variables. +*/ +#ifndef EXTERN +#define EXTERN extern +#endif /* ifndef EXTERN */ +#ifndef VAR_INITIALIZERS +#define VAR_INITIALIZERS 0 +#endif /* ifndef VAR_INITIALIZERS */ + +/* Macro used to provide an initial value for variables declared EXTERN. */ +#if VAR_INITIALIZERS +#define initial_value(value) = value +#else /* VAR_INITIALIZERS */ +#define initial_value(value) /* nothing */ +#endif /* VAR_INITIALIZERS */ + +#ifndef CHECKING +/* Include consistency-checking code. */ +#define CHECKING 1 +#endif /* ifndef CHECKING */ + +/* +EXTERN_C is used to declare an external function with C linkage. When +compiling with a C compiler this is just set to ``extern'', but when +compiling with a C++ compiler it is set to ``extern "C"''. +*/ +#ifdef __cplusplus +#define EXTERN_C extern "C" +#else /* !defined(__cplusplus) */ +#define EXTERN_C extern +#endif /* __cplusplus */ + +/* +Definition of a generic byte. Always "unsigned char". +*/ +typedef unsigned char a_byte; + +/* Simple boolean type: */ +typedef int a_boolean; +typedef a_byte a_byte_boolean; +#define FALSE 0 +#define TRUE 1 + +#endif /* BASICS_H */ + + +/****************************************************************************** +* \ ___ / * +* / \ * +* Edison Design Group C++ Runtime - | \^/ | - * +* \ / * +* / | | \ * +* Copyright 1992-2018 Edison Design Group Inc. [_] * +* * +******************************************************************************/ diff --git a/libstdc++-v3/libsupc++/cxa/decode.c b/libstdc++-v3/libsupc++/cxa/decode.c new file mode 100644 index 00000000000..c94acd74f16 --- /dev/null +++ b/libstdc++-v3/libsupc++/cxa/decode.c @@ -0,0 +1,8428 @@ +/****************************************************************************** +* \ ___ / * +* / \ * +* Edison Design Group C++/C Front End - | \^/ | - * +* \ / * +* / | | \ * +* Copyright 1996-2018 Edison Design Group Inc. [_] * +* * +******************************************************************************/ +/* +Redistribution and use in source and binary forms are permitted +provided that the above copyright notice and this paragraph are +duplicated in all source code forms. The name of Edison Design +Group, Inc. may not be used to endorse or promote products derived +from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +Any use of this software is at the user's own risk. +*/ +/* + +decode.c -- Name demangler for C++. + +The demangling is intended to work only on names of external entities. +There is some name mangling done for internal entities, or by the +C-generating back end, that this program does not try to decode. + +When IA64_ABI is defined as 1, the demangling matches the IA-64 ABI, +which in addition to its use on Itanium is used on a lot of versions of +gcc. +*/ + +#if COMPILE_DECODE_FOR_LIB_SRC +/* +When COMPILE_DECODE_FOR_LIB_SRC is TRUE, this file is being cross compiled for +inclusion in the runtime library (so that __cxa_demangle is available). +Compiling in this mode implies that IA64_ABI is TRUE (no externally visible +symbols are defined in Cfront mode). When cross compiling, don't include any +header files from the front end. Since we don't have access to the settings of +the front end configuration macros, make sure the ones we use have been +defined. +*/ + +#ifndef IA64_ABI +#define IA64_ABI 1 +#else /* defined(IA64_ABI) */ +#if !IA64_ABI + #error IA64_ABI macro must be TRUE when COMPILE_DECODE_FOR_LIB_SRC is TRUE +#endif /* !IA64_ABI */ +#endif /* ifndef IA64_ABI */ + +#ifndef DEFAULT_EMULATE_GNU_ABI_BUGS + #error DEFAULT_EMULATE_GNU_ABI_BUGS macro must be set when \ + COMPILE_DECODE_FOR_LIB_SRC is TRUE +#endif /* ifndef DEFAULT_EMULATE_GNU_ABI_BUGS */ + +#ifndef USE_LONG_DOUBLE_FOR_HOST_FP_VALUE + #error USE_LONG_DOUBLE_FOR_HOST_FP_VALUE macro must be set when \ + COMPILE_DECODE_FOR_LIB_SRC is TRUE +#endif /* ifndef USE_LONG_DOUBLE_FOR_HOST_FP_VALUE */ + +#include "basics.h" /* Includes version in lib_src directory. */ +#include +#include +#include +#include +#ifndef sizeof_t +typedef size_t sizeof_t; +#endif /* ifndef sizeof_t */ +#ifndef true_size_t +typedef size_t true_size_t; +#endif /* ifndef true_size_t */ + +#else /* !COMPILE_DECODE_FOR_LIB_SRC */ + +#include "basics.h" /* Includes version in src directory. */ +#include "host_envir.h" +#if IA64_ABI +#include "targ_def.h" /* For DEFAULT_EMULATE_GNU_ABI_BUGS and others. */ +#endif /* IA64_ABI */ +#include "decode.h" + +#endif /* COMPILE_DECODE_FOR_LIB_SRC */ + +/* +Block used to hold state variables. A block is used so that these routines +will be reentrant. +*/ +typedef struct a_decode_control_block *a_decode_control_block_ptr; +typedef struct a_decode_control_block { + char *output_id; + /* Pointer to buffer for demangled version of + the current identifier. */ + sizeof_t output_id_len; + /* Length of output_id, not counting the final + null. */ + sizeof_t output_id_size; + /* Allocated size of output_id. */ + a_boolean err_in_id; + /* TRUE if any error was encountered in the current + identifier. */ + a_boolean output_overflow_err; + /* TRUE if the demangled output overflowed the + output buffer. */ + unsigned long suppress_id_output; + /* If > 0, demangled id output is suppressed. This + might be because of an error or just as a way + of avoiding output during some processing. */ + sizeof_t uncompressed_length; + /* If non-zero, the original name was compressed, + and this indicates the length of the uncompressed + (but still mangled) name. */ +#if !IA64_ABI + a_const_char *end_of_name; + /* Set to the character position just after the end of + the mangled name. When sections with indicated + lengths are scanned, set temporarily to just after + that section of the name. */ + unsigned long mangling_nesting_level; + /* The nesting level of calls to + full_demangle_identifier. Used to ensure that + template parameter names for recursive calls are + given different nesting_levels (and therefore + different template parameter names). */ +#else /* IA64_ABI */ + unsigned long suppress_substitution_recording; + /* If > 0, suppress recording of substitutions. */ + a_boolean contains_conversion_operator; + /* TRUE if the name being demangled contains a + conversion operator (i.e., "cv "). Such + names may require a second pass at demangling + if the first pass ends in failure. */ + a_boolean parse_template_args_after_conversion_operator; + /* TRUE if template arguments should be parsed + as part of the type following a templated conversion + operator. The initial attempt at demangling uses + a value of FALSE, but a subsequent attempt will + have this field set to TRUE. */ +#endif /* IA64_ABI */ +} a_decode_control_block; + + +static void clear_control_block(a_decode_control_block_ptr dctl) +/* +Clear a decoding control block. +*/ +{ + dctl->output_id = NULL; + dctl->output_id_len = 0; + dctl->output_id_size = 0; + dctl->err_in_id = FALSE; + dctl->output_overflow_err = FALSE; + dctl->suppress_id_output = 0; + dctl->uncompressed_length = 0; +#if !IA64_ABI + dctl->end_of_name = NULL; + dctl->mangling_nesting_level = 0; +#else /* IA64_ABI */ + dctl->suppress_substitution_recording = 0; + dctl->contains_conversion_operator = FALSE; + dctl->parse_template_args_after_conversion_operator = FALSE; +#endif /* IA64_ABI */ +} /* clear_control_block */ + +#if !IA64_ABI + +/* +Block that contains information used to control the output of template +parameter lists. +*/ +typedef struct a_template_param_block *a_template_param_block_ptr; +typedef struct a_template_param_block { + unsigned long nesting_level; + /* Number of levels of template nesting at this + point (1 == top level). */ + a_const_char *final_specialization; + /* Set to point to the mangled encoding for the final + specialization encountered while working from + outermost template to innermost. NULL if + no specialization has been found yet. */ + a_boolean set_final_specialization; + /* TRUE if final_specialization should be set while + scanning. */ + a_boolean actual_template_args_until_final_specialization; + /* TRUE if template parameter names should not be + put out. Reset when the final_specialization + position is reached. */ + a_boolean output_only_correspondences; + /* TRUE if doing a post-pass to output only template + parameter/argument correspondences and not + anything else. suppress_id_output will have been + incremented to suppress everything else, and + gets decremented temporarily when correspondences + are output. */ + a_boolean first_correspondence; + /* TRUE until the first template parameter/argument + correspondence is put out. */ + a_boolean use_old_form_for_template_output; + /* TRUE if templates should be output in the old + form that always puts actual argument values + in template argument lists. */ +} a_template_param_block; + + +/* +Declarations needed because of forward references: +*/ +static a_const_char *demangle_identifier_with_preceding_length( + a_const_char *ptr, + a_boolean suppress_parent_and_local_info, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_operation(a_const_char *ptr, + a_boolean need_parens, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_operator(a_const_char *ptr, + int *mangled_length, + a_boolean *takes_type, + a_boolean *is_new_style_cast, + a_boolean *is_postfix, + a_boolean *need_adl_parens, + a_boolean *is_initializer_list, + a_boolean *ud_suffix_follows, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_type(a_const_char *ptr, + a_decode_control_block_ptr dctl); +static a_const_char *full_demangle_type_name( + a_const_char *ptr, + a_boolean base_name_only, + a_template_param_block_ptr temp_par_info, + a_boolean is_destructor_name, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_template_arguments( + a_const_char *ptr, + a_boolean emit_arg_values, + a_boolean suppress_angle_brackets, + a_template_param_block_ptr temp_par_info, + a_decode_control_block_ptr dctl); +static a_boolean is_mangled_type_name(a_const_char *ptr, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_name( + a_const_char *ptr, + unsigned long nchars, + a_boolean stop_on_underscores, + unsigned long *nchars_left, + a_const_char *mclass, + a_template_param_block_ptr temp_par_info, + a_boolean *instance_emitted, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_name_with_preceding_length( + a_const_char *ptr, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_ref_qualifiers( + a_const_char *p, + a_const_char **ref_qual, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_type_qualifiers( + a_const_char *ptr, + a_boolean trailing_space, + a_decode_control_block_ptr dctl); + +/* +Interface to full_demangle_type_name for the simple case. +*/ +#define demangle_type_name(ptr, dctl) \ + full_demangle_type_name((ptr), /*base_name_only=*/FALSE, \ + /*temp_par_info=*/(a_template_param_block_ptr)NULL, \ + /*is_destructor_name=*/FALSE, \ + (dctl)) +static a_const_char *full_demangle_identifier( + a_const_char *ptr, + unsigned long nchars, + a_boolean suppress_parent_and_local_info, + a_decode_control_block_ptr dctl); +/* Interface to full_demangle_identifier for the simple case. */ +#define demangle_identifier(ptr, dctl) \ + full_demangle_identifier((ptr), (unsigned long)0, FALSE, (dctl)) + +#endif /* !IA64_ABI */ + +static void write_id_ch(char ch, + a_decode_control_block_ptr dctl) +/* +Add the indicated character to the demangled version of the current identifier. +*/ +{ + if (!dctl->suppress_id_output) { + if (!dctl->output_overflow_err) { + /* Test for buffer overflow, leaving room for a terminating null. */ + if (dctl->output_id_len+1 >= dctl->output_id_size) { + /* There's no room for the character in the buffer. */ + dctl->output_overflow_err = TRUE; + /* Make sure the (truncated) output is null-terminated. */ + if (dctl->output_id_size != 0) { + dctl->output_id[dctl->output_id_size-1] = '\0'; + } /* if */ + } else { + /* No overflow; put the character in the buffer. */ + dctl->output_id[dctl->output_id_len] = ch; + } /* if */ + } /* if */ + /* Keep track of the number of characters (even if output has overflowed + the buffer). */ + dctl->output_id_len++; + } /* if */ +} /* write_id_ch */ + + +static void write_id_str(a_const_char *str, + a_decode_control_block_ptr dctl) +/* +Add the indicated string to the demangled version of the current identifier. +*/ +{ + a_const_char *p = str; + + if (!dctl->suppress_id_output) { + for (; *p != '\0'; p++) write_id_ch(*p, dctl); + } /* if */ +} /* write_id_str */ + + +static void write_id_number(unsigned long num, + a_decode_control_block_ptr dctl) +/* +Utility to write the specified non-negative number to the demangled version +of the current identifier. +*/ +{ + char buffer[50]; + + (void)sprintf(buffer, "%lu", num); + write_id_str(buffer, dctl); +} /* write_id_number */ + +#if IA64_ABI + +static void write_id_signed_number(long num, + a_decode_control_block_ptr dctl) +/* +Utility to write the specified signed number to the demangled version +of the current identifier. +*/ +{ + char buffer[50]; + + (void)sprintf(buffer, "%ld", num); + write_id_str(buffer, dctl); +} /* write_id_signed_number */ + +#endif /* IA64_ABI */ + +static void bad_mangled_name(a_decode_control_block_ptr dctl) +/* +A bad name mangling has been encountered. Record an error. +*/ +{ + if (!dctl->err_in_id) { + dctl->err_in_id = TRUE; + dctl->suppress_id_output++; +#if IA64_ABI + dctl->suppress_substitution_recording++; +#endif /* IA64_ABI */ + } /* if */ +} /* bad_mangled_name */ + +#if IA64_ABI + +/*ARGSUSED*/ +static a_const_char get_char(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Get and return the character pointed to by ptr. Stub version; this +does nothing in the IA-64 ABI, but it's called from some low-level routines. +*/ +{ + return *ptr; +} /* get_char */ + + +static a_boolean start_of_id_is(a_const_char *str, + a_const_char *id) +/* +Return TRUE if the part of the mangled name at id begins with the string str. +*/ +{ + a_boolean is_start = FALSE; + + for (;;) { + char chs = *str++; + if (chs == '\0') { + is_start = TRUE; + break; + } /* if */ + if (chs != *id++) break; + } /* for */ + return is_start; +} /* start_of_id_is */ + +#else /* !IA64_ABI */ + +static a_const_char get_char(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Get and return the character pointed to by ptr. However, if that +position is at or beyond dctl->end_of_name, return a null character +instead. +*/ +{ + char ch; + + if (ptr >= dctl->end_of_name) { + ch = '\0'; + } else { + ch = *ptr; + } /* if */ + return ch; +} /* get_char */ + + +static a_boolean start_of_id_is(a_const_char *str, + a_const_char *id, + a_decode_control_block_ptr dctl) +/* +Return TRUE if the part of the mangled name at id begins with the string str. +*/ +{ + a_boolean is_start = FALSE; + + for (;;) { + char chs = *str++; + if (chs == '\0') { + is_start = TRUE; + break; + } /* if */ + if (chs != get_char(id++, dctl)) break; + } /* for */ + return is_start; +} /* start_of_id_is */ + +#endif /* IA64_ABI */ + +static a_const_char *advance_past(a_const_char ch, + a_const_char *p, + a_decode_control_block_ptr dctl) +/* +The character ch is expected at *p. If it's there, advance past it. If +not, call bad_mangled_name. In either case, return the updated value of p. +*/ +{ + if (get_char(p, dctl) == ch) { + p++; + } else { + bad_mangled_name(dctl); + } /* if */ + return p; +} /* advance_past */ + + +static a_const_char *advance_past_underscore(a_const_char *p, + a_decode_control_block_ptr dctl) +/* +An underscore is expected at *p. If it's there, advance past it. If +not, call bad_mangled_name. In either case, return the updated value of p. +*/ +{ + return advance_past('_', p, dctl); +} /* advance_past_underscore */ + +#if IA64_ABI +static a_const_char *get_number(a_const_char *p, + long *num, + a_decode_control_block_ptr dctl); +#else /* !IA64_ABI */ +static a_const_char *get_number(a_const_char *p, + unsigned long *num, + a_decode_control_block_ptr dctl); +#endif /* IA64_ABI */ + +static a_const_char *demangle_module_id(a_const_char *ptr, + unsigned long num, + a_const_char *prefix, + a_decode_control_block_ptr dctl) +/* +Demangle a module id name (an EDG extension), which has the form + + _ _ _ [ _ ] + +Only the file name part is parsed and put out. num specifies the number of +characters in the entire module id. prefix points earlier in the mangled +name to a prefix that may precede the module id (if no such prefix is used, +prefix == NULL). Return a pointer to the character position following the +entire module id. +*/ +{ +#if IA64_ABI + long num_chars_to_output; +#else /* !IA64_ABI */ + unsigned long num_chars_to_output; +#endif /* IA64_ABI */ + a_const_char *start; + + if (*ptr != '_' || !isdigit((unsigned char)ptr[1])) { + /* May not be an EDG module_id, in which case, emit the entire string + (including any prefix that may have been parsed by the caller). */ + if (prefix != NULL) { + while (prefix != ptr) write_id_ch(*prefix++, dctl); + } /* if */ + num_chars_to_output = num; + start = ptr; + } else { + start = get_number(ptr+1, &num_chars_to_output, dctl); + if (!dctl->err_in_id) { + uint32_t prefix_len = (uint32_t)((start-ptr)+1); + if (*start != '_' || +#if IA64_ABI + num_chars_to_output <= 0 || +#endif /* IA64_ABI */ + num < ((unsigned long)num_chars_to_output + prefix_len)) { + bad_mangled_name(dctl); + } else { + /* Skip the underscore. */ + start++; + } /* if */ + } /* if */ + } /* if */ + if (!dctl->err_in_id) { + /* Write the filename (or entire module id). */ + while (num_chars_to_output-- > 0) write_id_ch(*start++, dctl); + } /* if */ + return ptr+num; +} /* demangle_module_id */ + +#if !IA64_ABI + +static a_const_char *get_length(a_const_char *p, + unsigned long *num, + a_const_char **prev_end, + a_decode_control_block_ptr dctl) +/* +Accumulate a number indicating a length, starting at position p, and +return its value in *num. Return a pointer to the character position +following the number. dctl->end_of_name is updated to reflect the location +after the end of the entity with the length, and *prev_end is set to the +previous value of dctl->end_of_name for later restoration. +*/ +{ + unsigned long n = 0; + char ch; + + *prev_end = dctl->end_of_name; + ch = get_char(p, dctl); + if (!isdigit((unsigned char)ch)) { + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + do { + n = n*10 + (ch - '0'); + if (n > (unsigned long)((dctl->end_of_name - p) - 1)) { + /* Bad number (bigger than the amount of text remaining). */ + bad_mangled_name(dctl); + n = (unsigned long)((dctl->end_of_name - p) - 1); + goto end_of_routine; + } /* if */ + p++; + ch = get_char(p, dctl); + } while (isdigit((unsigned char)ch)); + dctl->end_of_name = p + n; +end_of_routine: + *num = n; + return p; +} /* get_length */ + + +static a_const_char *get_number(a_const_char *p, + unsigned long *num, + a_decode_control_block_ptr dctl) +/* +Accumulate a number starting at position p and return its value in *num. +Return a pointer to the character position following the number. +*/ +{ + unsigned long n = 0; + char ch; + + ch = get_char(p, dctl); + if (!isdigit((unsigned char)ch)) { + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + do { + n = n*10 + (ch - '0'); + p++; + ch = get_char(p, dctl); + } while (isdigit((unsigned char)ch)); +end_of_routine: + *num = n; + return p; +} /* get_number */ + + +static a_const_char *get_single_digit_number(a_const_char *p, + unsigned long *num, + a_decode_control_block_ptr dctl) +/* +Accumulate a number starting at position p and return its value in *num. +The number is a single digit. Return a pointer to the character position +following the number. +*/ +{ + char ch; + + *num = 0; + ch = get_char(p, dctl); + if (!isdigit((unsigned char)ch)) { + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + *num = (ch - '0'); + p++; +end_of_routine: + return p; +} /* get_single_digit_number */ + + +static a_const_char *get_single_digit_length( + a_const_char *p, + unsigned long *num, + a_const_char **prev_end, + a_decode_control_block_ptr dctl) +/* +Accumulate a length starting at position p and return its value in *num. +The length is a single digit. Return a pointer to the character position +following the length. dctl->end_of_name is updated to reflect the location +after the end of the entity with the length, and *prev_end is set to the +previous value of dctl->end_of_name for later restoration. +*/ +{ + p = get_single_digit_number(p, num, dctl); + *prev_end = dctl->end_of_name; + if (*num > (unsigned long)(dctl->end_of_name - p)) { + /* Bad length (too large). */ + bad_mangled_name(dctl); + } else { + dctl->end_of_name = p + *num; + } /* if */ + return p; +} /* get_single_digit_length */ + + +static a_const_char *get_length_with_optional_underscore( + a_const_char *p, + unsigned long *num, + a_const_char **prev_end, + a_decode_control_block_ptr dctl) +/* +Accumulate a number starting at position p and return its value in *num. +If the number has more than one digit, it is followed by an underscore. +(Or, in a newer representation, surrounded by underscores.) +Return a pointer to the character position following the number. +dctl->end_of_name is updated to reflect the location after the end +of the entity with the length, and *prev_end is set to the previous value +of dctl->end_of_name for later restoration. +*/ +{ + if (get_char(p, dctl) == '_') { + /* New encoding (not from cfront) -- the length is surrounded by + underscores whether it's a single digit or several digits, + e.g., "L_10_1234567890". */ + p++; + /* Multi-digit number followed by underscore. */ + p = get_length(p, num, prev_end, dctl); + p = advance_past_underscore(p, dctl); + dctl->end_of_name++; /* Adjust for underscore. */ + } else if (isdigit((unsigned char)get_char(p, dctl)) && + isdigit((unsigned char)get_char(p+1, dctl)) && + get_char(p+2, dctl) == '_') { + /* The cfront version -- a multi-digit length is followed by an + underscore, e.g., "L10_1234567890". This doesn't work well because + something like "L11", intended to have a one-digit length, can + be made ambiguous by following it by a "_" for some other reason. + So this form is not used in new cases where that can come up, e.g., + nontype template arguments for functions. In any case, interpret + "multi-digit" as "2-digit" and don't look further for the underscore. */ + /* Multi-digit number followed by underscore. */ + p = get_length(p, num, prev_end, dctl); + p = advance_past_underscore(p, dctl); + dctl->end_of_name++; /* Adjust for underscore. */ + } else { + /* Single-digit number not followed by underscore. */ + p = get_single_digit_length(p, num, prev_end, dctl); + } /* if */ + return p; +} /* get_length_with_optional_underscore */ + + +static a_const_char *get_number_with_optional_underscore( + a_const_char *p, + unsigned long *num, + a_decode_control_block_ptr dctl) +/* +Accumulate a number starting at position p and return its value in *num. +If the number has more than one digit, it is followed by an underscore. +(Or, in a newer representation, surrounded by underscores.) +Return a pointer to the character position following the number. +Parses the same string as get_length_with_optional_underscore, except that +dctl->end_of_name is not altered (meaning that this routine can be used to +retrieve a count of something other than the number of characters that +immediately follow the number). +*/ +{ + if (get_char(p, dctl) == '_') { + /* New encoding (not from cfront) -- the number is surrounded by + underscores whether it's a single digit or several digits, + e.g., "L_10_". */ + p++; + /* Multi-digit number followed by underscore. */ + p = get_number(p, num, dctl); + p = advance_past_underscore(p, dctl); + } else if (isdigit((unsigned char)get_char(p, dctl)) && + isdigit((unsigned char)get_char(p+1, dctl)) && + get_char(p+2, dctl) == '_') { + /* The cfront version -- a multi-digit number is followed by an + underscore, e.g., "L10_". This doesn't work well because + something like "L11", intended to have a one-digit length, can + be made ambiguous by following it by a "_" for some other reason. + So this form is not used in new cases where that can come up, e.g., + nontype template arguments for functions. In any case, interpret + "multi-digit" as "2-digit" and don't look further for the underscore. */ + /* Multi-digit number followed by underscore. */ + p = get_number(p, num, dctl); + p = advance_past_underscore(p, dctl); + } else { + /* Single-digit number not followed by underscore. */ + p = get_single_digit_number(p, num, dctl); + } /* if */ + return p; +} /* get_number_with_optional_underscore */ + + +static a_boolean is_immediate_type_qualifier(a_const_char *p, + a_decode_control_block_ptr dctl) +/* +Return TRUE if the encoding pointed to is one that indicates type +qualification. +*/ +{ + a_boolean is_type_qual = FALSE; + char ch; + + ch = get_char(p, dctl); + if (ch == 'C' || ch == 'V' || (ch == 'D' && get_char(p+1, dctl) == 'r')) { + /* This is a type qualifier. */ + is_type_qual = TRUE; + } /* if */ + return is_type_qual; +} /* is_immediate_type_qualifier */ + + +static a_const_char *remove_immediate_type_qualifiers( + a_const_char *p, + a_decode_control_block_ptr dctl) +/* +Return a pointer to the mangled name after removing any type qualifiers +that might be present. +*/ +{ + while (is_immediate_type_qualifier(p, dctl)) { + if (get_char(p, dctl) == 'D' && get_char(p+1, dctl) == 'r') { + /* Two-character qualifier. */ + p+=2; + } else { + /* One-character qualifier. */ + p++; + } /* if */ + } /* while */ + return p; +} /* remove_immediate_type_qualifiers */ + + +static void write_template_parameter_name(unsigned long depth, + unsigned long position, + a_boolean nontype, + a_decode_control_block_ptr dctl) +/* +Output a representation of a template parameter with depth and position +as indicated. It's a nontype parameter if nontype is TRUE. +*/ +{ + char buffer[100]; + char letter = '\0'; + + if (nontype) { + /* Nontype parameter. */ + /* Use a code letter for the first few levels, then the depth number. */ + if (depth == 1) { + letter = 'N'; + } else if (depth == 2) { + letter = 'O'; + } else if (depth == 3) { + letter = 'P'; + } /* if */ + if (letter != '\0') { + (void)sprintf(buffer, "%c%lu", letter, position); + } else { + (void)sprintf(buffer, "N_%lu_%lu", depth, position); + } /* if */ + } else { + /* Normal type parameter. */ + /* Use a code letter for the first few levels, then the depth number. */ + if (depth == 1) { + letter = 'T'; + } else if (depth == 2) { + letter = 'U'; + } else if (depth == 3) { + letter = 'V'; + } /* if */ + if (letter != '\0') { + (void)sprintf(buffer, "%c%lu", letter, position); + } else { + (void)sprintf(buffer, "T_%lu_%lu", depth, position); + } /* if */ + } /* if */ + write_id_str(buffer, dctl); +} /* write_template_parameter_name */ + + +static a_const_char *demangle_template_parameter_name( + a_const_char *ptr, + a_boolean nontype, + a_decode_control_block_ptr dctl) +/* +Demangle a template parameter name at the indicated location. The parameter +is a nontype parameter if nontype is TRUE. Return a pointer to the character +position following what was demangled. +*/ +{ + a_const_char *p = ptr; + unsigned long position, depth = 1; + + /* This comes up with the modern mangling for template functions. + Form is "ZnZ" or "Zn_mZ", where n is the parameter number and m + is the depth number (1 if not specified). */ + p++; /* Advance past the "Z". */ + /* Get the position number. */ + p = get_number(p, &position, dctl); + if (get_char(p, dctl) == '_' && get_char(p+1, dctl) != '_') { + /* Form including depth ("Zn_mZ"). */ + p++; + p = get_number(p, &depth, dctl); + } /* if */ + /* Output the template parameter name. */ + write_template_parameter_name(depth, position, nontype, dctl); + if (get_char(p , dctl) == '_' && + get_char(p+1, dctl) == '_' && + get_char(p+2, dctl) == 't' && + get_char(p+3, dctl) == 'm' && + get_char(p+4, dctl) == '_' && + get_char(p+5, dctl) == '_') { + /* A template template parameter followed by a template + argument list. */ + p = demangle_template_arguments(p+6, /*emit_arg_values=*/FALSE, + /*suppress_angle_brackets=*/FALSE, + (a_template_param_block_ptr)NULL, dctl); + } /* if */ + /* Check for the final "Z". This appears in the mangling to avoid + ambiguities when the template parameter is followed by something whose + encoding begins with a digit, e.g., a class name. */ + if (get_char(p, dctl) != 'Z') { + bad_mangled_name(dctl); + } else { + p++; + } /* if */ + return p; +} /* demangle_template_parameter_name */ + + +static a_const_char *demangle_constant_value( + a_const_char *ptr, + a_boolean is_bool, + a_boolean is_nullptr, + a_decode_control_block_ptr dctl) +/* +Demangle a constant value that is part of a literal. The form of the +constant has an initial length (which may or may not use the new underscore +for specifying the length), followed by some number of characters, for example: + + 3n12 encoding for -12 + ^^^---- Characters of constant. Some characters get remapped: + n --> - + p --> + + d --> . + ^------- Length of constant (may or may not include underscores). + +When is_bool is TRUE, emit "true"/"false" instead of 1/0. Likewise when +is_nullptr is TRUE (emits "nullptr" rather than 0). +*/ +{ + a_const_char *p = ptr, *prev_end; + char ch; + unsigned long nchars; + a_boolean is_nonzero = FALSE; + + /* Get the length of the constant. */ + p = get_length_with_optional_underscore(p, &nchars, &prev_end, dctl); + /* Process the characters of the literal constant. */ + for (; nchars > 0; nchars--, p++) { + /* Remap characters where necessary. */ + ch = get_char(p, dctl); + switch (ch) { + case '\0': + case '_': + /* Ran off end of string. */ + bad_mangled_name(dctl); + goto end_of_routine; + case 'p': + ch = '+'; + break; + case 'n': + ch = '-'; + break; + case 'd': + ch = '.'; + break; + } /* switch */ + if (is_bool) { + /* For the bool case, just keep track of whether the constant is + non-zero; true or false will be output later. */ + if (ch != '0') is_nonzero = TRUE; + } else if (is_nullptr) { + /* Constant should only ever be zero. Suppress it. */ + } else { + /* Normal (non-bool, non-nullptr) case. Output the character of the + constant. */ + write_id_ch(ch, dctl); + } /* if */ + } /* for */ + dctl->end_of_name = prev_end; + if (is_bool) { + /* For bool, output true or false. */ + write_id_str((char *)(is_nonzero ? "true" : "false"), dctl); + } /* if */ + if (is_nullptr) write_id_str("nullptr", dctl); +end_of_routine: + return p; +} /* demangle_constant_value */ + + +static a_const_char *demangle_constant( + a_const_char *ptr, + a_boolean suppress_address_of, + a_boolean need_parens, + a_decode_control_block_ptr dctl) +/* +Demangle a constant (e.g., a nontype template class argument) beginning at +ptr, and output the demangled form. When suppress_address_of is TRUE, the +ampersand that is normally emitted before an address constant is suppressed +(this is used when demangling expressions where any "address of" operation is +explicit). When need_parens is TRUE, parentheses are emitted around literals +and expressions (but not addresses or template parameters). Return a pointer +to the character position following what was demangled. +*/ +{ + a_const_char *p = ptr, *type = NULL, *index, *prev_end, *quals; + unsigned long nchars; + char ch; + + /* A constant has a form like + CiL15 <-- integer constant 5 + ^-- Literal constant representation. + ^--- Length of literal constant. + ^---- L indicates literal constant; c indicates address + of variable, etc. + ^^----- Type of template argument, with "const" added. + A template parameter constant or a constant expression does not have + the initial "C" and type. + */ + if (get_char(p, dctl) == 'C') { + /* Advance past the type. */ + type = p; + dctl->suppress_id_output++; + p = demangle_type(p, dctl); + dctl->suppress_id_output--; + } /* if */ + /* The next thing has one of the following forms: + 3abc Address of "abc". + L211 Literal constant; length ("2") followed by the characters of + the constant ("11"). + LM0_L2n1_1j Pointer-to-member-function constant; the three parts + correspond to the triplet of values in the __mptr + data structure. + Z1Z Template parameter. + Opl2Z1ZZ2ZO Expression. + */ + ch = get_char(p, dctl); + if (isdigit((unsigned char)ch)) { + /* A name preceded by its length, e.g., "3abc". Put out "&name". */ + if (!suppress_address_of) write_id_ch('&', dctl); + /* Process the length and name. */ + p = demangle_identifier_with_preceding_length( + p, + /*suppress_parent_and_local_info=*/FALSE, + dctl); + } else if (ch == 'L') { + /* Emit parentheses around the literal if requested. */ + if (need_parens) write_id_ch('(', dctl); + if (type == NULL) { + bad_mangled_name(dctl); + } else if (get_char(p+1, dctl) == 'M') { + /* Pointer-to-member-function. The form of the constant is + LM0_L2n1_1j Non-virtual function + LM0_L11_0 Virtual function + LM0_L10_0 Null pointer + The three parts match the three components of the __mptr structure: + (delta, index, function or offset). The index is -1 for a non-virtual + function, 0 for a null pointer, and greater than 0 for a virtual + function. The index is represented like an integer constant (see + above). For virtual functions, the last component is always "0" + even if the offset is not zero. */ + /* Advance past the "LM". */ + p += 2; + /* Advance over the first component, ignoring it. */ + while (isdigit((unsigned char)get_char(p, dctl))) p++; + p = advance_past_underscore(p, dctl); + /* The index component should be next. */ + if (get_char(p, dctl) != 'L') { + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + p++; + /* Get the index length. */ + /* Note that get_length_with_optional_underscore is not used because + this is an ambiguous situation: an underscore follows the index + value, and there's no way to tell if it's the multi-digit + indicator for the length or the separator between fields. */ + if (get_char(p, dctl) == '_') { + /* New-form encoding, no ambiguity. */ + p = get_length_with_optional_underscore(p, &nchars, &prev_end, dctl); + } else { + p = get_single_digit_length(p, &nchars, &prev_end, dctl); + } /* if */ + /* Remember the start of the index. */ + index = p; + /* Skip the rest of the index. */ + while (isdigit((unsigned char)get_char(p, dctl)) || + (get_char(p, dctl) == 'n')) p++; + dctl->end_of_name = prev_end; + p = advance_past_underscore(p, dctl); + /* If the index number starts with 'n', this is a non-virtual + function. */ + if (*index == 'n') { + /* Non-virtual function. */ + /* The third component is a name preceded by its length, e.g., + "1f". Put out "&A::f", where "A" is the class type retrieved + from the type. */ + write_id_ch('&', dctl); + /* Start at type+2 to skip the "C" for const and the "M" for + pointer-to-member. */ + quals = demangle_type_name(type+2, dctl); + write_id_str("::", dctl); + /* Demangle the length and name. */ + p = demangle_identifier_with_preceding_length( + p, + /*suppress_parent_and_local_info=*/TRUE, + dctl); + if (is_immediate_type_qualifier(quals, dctl)) { + /* Get any optional cv-qualifiers. */ + write_id_ch(' ', dctl); + quals = demangle_type_qualifiers(quals, /*trailing_space=*/FALSE, + dctl); + } /* if */ + if (get_char(quals, dctl) == 'F') { + /* See if there is a ref-qualifier. */ + a_const_char *ref_qual; + (void)demangle_ref_qualifiers(quals+1, &ref_qual, dctl); + if (ref_qual != NULL) { + write_id_str(ref_qual, dctl); + } /* if */ + } /* if */ + } else { + /* Not a non-virtual function. The encoding for the third component + should be simply "0". */ + if (get_char(p, dctl) != '0') { + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + p++; + if (nchars == 1 && *index == '0') { + /* Null pointer constant. Output "(type)0", that is, a zero cast + to the pointer-to-member type. */ + write_id_ch('(', dctl); + (void)demangle_type(type, dctl); + write_id_str(")0", dctl); + } else { + /* Virtual function. This case can't really be demangled properly, + because the mangled name doesn't have enough information. + Output "&A::virtual-function-n". */ + write_id_ch('&', dctl); + /* Start at type+2 to skip the "C" for const and the "M" for + pointer-to-member. */ + (void)demangle_type_name(type+2, dctl); + write_id_str("::", dctl); + write_id_str("virtual-function-", dctl); + /* Write the index number. */ + for (; nchars > 0; nchars--, index++) write_id_ch(*index, dctl); + } /* if */ + } /* if */ + } else if (get_char(p+1, dctl) == 'S') { + /* String literal constant. */ + p+=2; + if (type == NULL) { + bad_mangled_name(dctl); + } else { + /* The type is the type of the string. Emit "..." cast to the + proper type. */ + write_id_ch('(', dctl); + (void)demangle_type(type+1, dctl); + write_id_str(")\"...\"", dctl); + } /* if */ + } else { + /* Normal literal constant. Form is something like + L3n12 encoding for -12 + ^^^---- Characters of constant. Some characters get remapped: + n --> - + p --> + + d --> . + ^------- Length of constant. + Output is + (type)constant + That is, the literal constant preceded by a cast to the right type. + */ + /* See if the type is bool. */ + a_boolean is_bool = (type+2 == p && *(type+1) == 'b'); + a_boolean is_managed_nullptr = (type+2 == p && *(type+1) == 'j'); + a_boolean is_nullptr = (type+2 == p && *(type+1) == 'n') || + is_managed_nullptr; + a_boolean is_complex = (type+3 == p && *(type+1) == 'x'); + /* If the type is bool or nullptr, don't put out the cast. */ + if (!(is_bool || is_nullptr)) { + write_id_ch('(', dctl); + /* Start at type+1 to avoid the "C" for const. */ + (void)demangle_type(type+1, dctl); + write_id_ch(')', dctl); + } /* if */ + if (is_complex) write_id_ch('(', dctl); + p++; /* Advance past the "L". */ + if (is_managed_nullptr) { + /* If this is a managed C++/CLI __nullptr, emit the underscores to + distinguish it from the standard nullptr. */ + write_id_str("__", dctl); + } /* if */ + p = demangle_constant_value(p, is_bool, is_nullptr, dctl); + if (!dctl->err_in_id && is_complex) { + /* Now emit the imaginary portion of the complex number. */ + write_id_ch('+', dctl); + p = demangle_constant_value(p, /*is_bool=*/FALSE, /*is_nullptr=*/FALSE, + dctl); + write_id_str("i)", dctl); + } /* if */ + } /* if */ + if (need_parens) write_id_ch(')', dctl); + } else if (ch == 'Z') { + /* A template parameter. */ + p = demangle_template_parameter_name(p, /*nontype=*/TRUE, dctl); + } else if (ch == 'O') { + /* An operation. */ + p = demangle_operation(p, need_parens, dctl); + } else { + /* The constant starts with something unexpected. */ + bad_mangled_name(dctl); + } /* if */ +end_of_routine: + return p; +} /* demangle_constant */ + +static a_const_char *demangle_parameter_reference( + a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle a function parameter reference (e.g., in a late specified return +type) as pointed to by ptr: + + v-vv----- These are optional. + IC1_2I <-- "const param#1 two levels up" + ^---- Terminating non-digit character so parameter number won't run + into an entity with an initial length. + ^^----- Number of "levels up" for this parameter (0-based). Omitted + if zero. + ^------- Parameter number (1-based) or 0 for "this". + ^-------- Optional cv-qualifiers. + ^--------- "I" indicates parameter reference. +*/ +{ + a_const_char *p = ptr; + unsigned long num, level = 0; + char buffer[60]; + + /* Advance past the initial "I" (verified by caller). */ + p++; + if (is_immediate_type_qualifier(p, dctl)) { + /* Get any optional cv-qualifiers. */ + p = demangle_type_qualifiers(p, /*trailing_space=*/TRUE, dctl); + } /* if */ + p = get_number(p, &num, dctl); + if (!dctl->err_in_id) { + if (get_char(p, dctl) != 'I') { + p = advance_past_underscore(p, dctl); + if (!dctl->err_in_id) { + p = get_number(p, &level, dctl); + } /* if */ + } /* if */ + } /* if */ + if (!dctl->err_in_id) { + if (num == 0) { + /* An explicit "this" in a trailing return type. */ + write_id_str("this", dctl); + } else { + if (level == 0) { + (void)sprintf(buffer, "param#%ld", num); + } else { + (void)sprintf(buffer, "param#%ld[up %ld level%s]", num, level, + level > 1 ? "s" : ""); + } /* if */ + write_id_str(buffer, dctl); + } /* if */ + p = advance_past('I', p, dctl); + } /* if */ + return p; +} /* demangle_parameter_reference */ + + +static a_const_char *demangle_expression( + a_const_char *ptr, + a_boolean need_parens, + a_decode_control_block_ptr dctl) +/* +Demangle an expression; ensure that the expression is enclosed in +parentheses when necessary if need_parens is TRUE (names aren't parenthesized +even when need_parens is TRUE). +*/ +{ + a_const_char *p = ptr; + + if (get_char(p, dctl) == 'I') { + /* A function parameter reference. */ + p = demangle_parameter_reference(p, dctl); + } else if (get_char(p, dctl) == '_' && get_char(p+1, dctl) == '_') { + /* Certain special names can occur here, for example, an operator name + that appears as the first operand of a call. */ + p = demangle_name(p, (unsigned long)0, /*stop_on_underscores=*/TRUE, + (unsigned long *)NULL, (char *)NULL, + (a_template_param_block_ptr)NULL, (a_boolean *)NULL, + dctl); + if (get_char(p, dctl) == '_' && get_char(p+1, dctl) == '_') { + p += 2; + } else { + bad_mangled_name(dctl); + } /* if */ + } else { + /* Used to demangle literals as well as template parameters, operations. + Within an expression, suppress implicit "&"s during the demangling. */ + p = demangle_constant(p, /*suppress_address_of=*/TRUE, need_parens, dctl); + } /* if */ + return p; +} /* demangle_expression */ + + +static a_const_char *demangle_operation(a_const_char *ptr, + a_boolean need_parens, + a_decode_control_block_ptr dctl) +/* +Demangle an operation in a constant expression (these come up in template +arguments and array sizes, in template function parameter lists) beginning +at ptr, and output the demangled form. When need_parens is TRUE, parentheses +are emitted around the operation. Return a pointer to the character +position following what was demangled. +*/ +{ + a_const_char *p = ptr, *operator_str, *close_str = ""; + int op_length; + unsigned long num_operands, i, num_dimensions; + a_boolean takes_type, is_new_style_cast, is_postfix, need_adl_parens; + a_boolean has_variable_number_of_operands = FALSE, is_initializer_list; + a_boolean is_call = FALSE, is_cli_subscript = FALSE; + a_boolean ud_suffix_follows; + + /* An operation has the form + Opl2Z1ZZ2ZO <-- "Z1 + Z2", Z1/Z2 indicating nontype template parameters. + ^---- "O" to end the operation encoding. + ^^^----- Second operand. + ^^^-------- First operand. + ^----------- Count of operands (which may or may not have an + initial "_" indicating possibly more than 9 operands). + ^^------------ Operation, using same encoding as for operator + function names. + ^-------------- "O" for operation. + */ + p++; /* Advance past the "O". */ + /* Decode the operator name, e.g., "pl" is "+". */ + operator_str = demangle_operator(p, &op_length, &takes_type, + &is_new_style_cast, &is_postfix, + &need_adl_parens, &is_initializer_list, + &ud_suffix_follows, dctl); + if (operator_str == NULL) { + bad_mangled_name(dctl); + } else { + /* Put parentheses around the operation if necessary. */ + if (need_parens) write_id_ch('(', dctl); + if (*operator_str == 'f' && strcmp(operator_str, "fold-ex") == 0) { + /* A fold expression; handle it here (since it's significantly + different than a standard operation). */ + a_boolean unary, left; + p++; + switch (get_char(p, dctl)) { + case 'l': unary = TRUE; left = TRUE; break; + case 'L': unary = FALSE; left = TRUE; break; + case 'r': unary = TRUE; left = FALSE; break; + case 'R': unary = FALSE; left = FALSE; break; + default: + bad_mangled_name(dctl); + break; + } /* switch */ + p++; + operator_str = demangle_operator(p, &op_length, &takes_type, + &is_new_style_cast, &is_postfix, + &need_adl_parens, &is_initializer_list, + &ud_suffix_follows, dctl); + if (operator_str == NULL) { + bad_mangled_name(dctl); + } else { + p += op_length; + write_id_ch('(', dctl); + if (unary) { + if (left) { + write_id_str("...", dctl); + write_id_str(operator_str, dctl); + p = demangle_expression(p, /*need_parens=*/FALSE, dctl); + } else { + p = demangle_expression(p, /*need_parens=*/FALSE, dctl); + write_id_str(operator_str, dctl); + write_id_str("...", dctl); + } /* if */ + } else { + p = demangle_expression(p, /*need_parens=*/FALSE, dctl); + write_id_str(operator_str, dctl); + write_id_str("...", dctl); + write_id_str(operator_str, dctl); + p = demangle_expression(p, /*need_parens=*/FALSE, dctl); + } /* if */ + write_id_ch(')', dctl); + } /* if */ + goto skip_operand_loop; + } /* if */ + p += op_length; + if (is_initializer_list) { + /* An initializer list (with an optional type). */ + if (takes_type) { + p = demangle_type(p, dctl); + } /* if */ + write_id_str(operator_str, dctl); + has_variable_number_of_operands = TRUE; + close_str = "}"; + } else if (takes_type) { + /* For casts, sizeof, __alignof__, __uuidof__, typeid, new, or sizeof... + get the type. */ + if (strcmp(operator_str, "cast") == 0) { + a_const_char *num_args_ptr; + /* A "cast" can have zero or more operands (aside from the type). + For casts with exactly one operand, emit "(type)arg", but for + other cases, emit the functional-notation type conversion syntax: + "type(args)". Look ahead at the number of arguments to determine + which case we have. */ + dctl->suppress_id_output++; + num_args_ptr = demangle_type(p, dctl); + dctl->suppress_id_output--; + (void)get_number_with_optional_underscore(num_args_ptr, + &num_operands, dctl); + if (!dctl->err_in_id) { + operator_str = ""; + if (num_operands == 1) { + /* Output as "(type)arg". */ + write_id_ch('(', dctl); + p = demangle_type(p, dctl); + write_id_ch(')', dctl); + } else { + /* Output as "type(args)". */ + p = demangle_type(p, dctl); + write_id_ch('(', dctl); + has_variable_number_of_operands = TRUE; + close_str = ")"; + } /* if */ + } /* if */ + } else if (strcmp(operator_str, "sizeof(") == 0 || + strcmp(operator_str, "__alignof__(") == 0 || + strcmp(operator_str, "__uuidof(") == 0 || + strcmp(operator_str, "typeid(") == 0 || + strcmp(operator_str, "sizeof...(") == 0) { + /* These manglings have three forms, dependent on the next character + in the mangled name. They're sufficiently different that they + are handled (mostly separately) here. */ + write_id_str(operator_str, dctl); + operator_str = ""; + if (get_char(p, dctl) == 'e') { + /* An "old style expression" where the expression was not + encoded (and the operand count is zero). Just note that there + was an expression and we're done. */ + write_id_str("expr)", dctl); + p++; + } else if (get_char(p, dctl) == 'X') { + /* A "new style expression" where the expression is + included in the mangled name and will be demangled below. */ + close_str = ")"; + p++; + } else { + /* The "type" case; simply decode the type (the mangled + encoding specifies zero operands -- which are ignored below). */ + p = demangle_type(p, dctl); + write_id_ch(')', dctl); + } /* if */ + } else if (strcmp(operator_str, "::typeid") == 0) { + /* C++/CLI T::typeid. */ + p = demangle_type(p, dctl); + write_id_str(operator_str, dctl); + } else { + /* Generic processing of items that take a type (e.g., static_cast). */ + write_id_str(operator_str, dctl); + p = demangle_type(p, dctl); + if (is_new_style_cast) { + /* Something like static_cast(expression). The operator and + type have been emitted, close the type with a right angle + bracket and parse the expression below. */ + operator_str = ""; + write_id_str(">(", dctl); + close_str = ")"; + } else { + write_id_ch(')', dctl); + } /* if */ + } /* if */ + } else if (strcmp(operator_str, "builtin-operation") == 0) { + unsigned long kind; + /* A builtin operation. */ + has_variable_number_of_operands = TRUE; + write_id_str("builtin-operation-", dctl); + /* Extract the operation number following the "bi". */ + p = advance_past_underscore(p, dctl); + p = get_number(p, &kind, dctl); + if (kind > 99) { + bad_mangled_name(dctl); + } else { + write_id_number(kind, dctl); + } /* if */ + p = advance_past_underscore(p, dctl); + write_id_ch('(', dctl); + close_str = ")"; + } else if (strcmp(operator_str, "__real(") == 0 || + strcmp(operator_str, "__imag(") == 0 || + strcmp(operator_str, "noexcept(") == 0) { + /* These need a closing paren after their operand. */ + close_str = ")"; + } else if (strcmp(operator_str, "()") == 0) { + /* A call operation. The first operand is the target of the call, + the rest are arguments. */ + operator_str = ""; + is_call = TRUE; + has_variable_number_of_operands = TRUE; + } else if (strcmp(operator_str, "new") == 0 || + strcmp(operator_str, "new[]") == 0) { + /* new has an optional "g" (indicating that ::new was used), followed by + an optional initial list of placement expressions, followed + by a type and then another optional list of initializer + expressions. Handle the first expression list and the type here, + then let the generic loop below handle the initializer list. */ + /* new may have an optional "g" indicating a global scope new. */ + if (get_char(p, dctl) == 'g') { + p++; + write_id_str("::", dctl); + } /* if */ + write_id_str(operator_str, dctl); + write_id_ch(' ', dctl); + operator_str = ""; + has_variable_number_of_operands = TRUE; + /* Get the count of operands. */ + p = get_number_with_optional_underscore(p, &num_operands, dctl); + if (num_operands != 0) { + write_id_ch('(', dctl); + for (i = 1; i <= num_operands; i++) { + p = demangle_expression(p, /*need_parens=*/FALSE, dctl); + if (i != num_operands) write_id_str(", ", dctl); + } /* for */ + write_id_str(") ", dctl); + } /* if */ + p = demangle_type(p, dctl); +handle_new_operands: + if (get_char(p, dctl) == 'O') { + /* There are no initializers; skip the loop below. */ + goto skip_operand_loop; + } /* if */ + if (get_char(p, dctl) == 'b' && get_char(p+1, dctl) == 'i') { + /* A brace-enclosed initializer list. */ + p += 2; + write_id_ch('{', dctl); + close_str = "}"; + } else { + /* A parenthesized initializer list. */ + write_id_ch('(', dctl); + close_str = ")"; + } /* if */ + } else if (strcmp(operator_str, "gcnew") == 0) { + /* C++/CLI gcnew. */ + write_id_str(operator_str, dctl); + write_id_ch(' ', dctl); + operator_str = ""; + has_variable_number_of_operands = TRUE; + /* Get the count of dimensions. */ + p = get_number_with_optional_underscore(p, &num_dimensions, dctl); + if (num_dimensions == 0) { + /* Non-array case. */ + p = demangle_type(p, dctl); + } else { + a_const_char *dim_p = p; + /* Array case; emit the dimensions (but emit the type first). */ + dctl->suppress_id_output++; + for (i = 1; i <= num_dimensions; i++) { + p = demangle_expression(p, /*need_parens=*/FALSE, dctl); + } /* for */ + dctl->suppress_id_output--; + p = demangle_type(p, dctl); + write_id_ch('(', dctl); + for (i = 1; i <= num_dimensions; i++) { + dim_p = demangle_expression(dim_p, /*need_parens=*/FALSE, dctl); + if (i != num_dimensions) write_id_str(", ", dctl); + } /* for */ + write_id_str(") ", dctl); + } /* if */ + goto handle_new_operands; + } else if (strcmp(operator_str, "delete") == 0 || + strcmp(operator_str, "delete[]") == 0) { + /* delete may have an optional "g" indicating a global scope delete. */ + if (get_char(p, dctl) == 'g') { + p++; + write_id_str("::", dctl); + } /* if */ + } else if (strcmp(operator_str, "subscript") == 0) { + /* A C++/CLI subscript operation (with a variable number of operands). */ + has_variable_number_of_operands = TRUE; + is_cli_subscript = TRUE; + } /* if */ + /* Get the count of operands. */ + p = get_number_with_optional_underscore(p, &num_operands, dctl); + /* Some operations (e.g., sizeof(type), __alignof__(type), etc.) take + zero operands. */ + if (num_operands != 0) { + if (has_variable_number_of_operands) { + /* Operation has a variable number of operations, and + they may be type operands (i.e., builtin-operation). */ + for (i = 1; i <= num_operands; i++) { + if (get_char(p, dctl) == 'T') { + /* Type operand. */ + p = demangle_type(p+1, dctl); + } else { + p = demangle_expression(p, need_adl_parens, dctl); + } /* if */ + if (is_call) { + /* This is a call to the target just emitted; the rest are + arguments. */ + write_id_str("(", dctl); + close_str = ")"; + is_call = FALSE; + } else if (is_cli_subscript) { + /* This is a C++/CLI subscript operation, we've just emitted + the array, the remaining operands are subscripts. */ + write_id_str("[", dctl); + close_str = "]"; + is_cli_subscript = FALSE; + } else if (i != num_operands) { + write_id_str(", ", dctl); + } /* if */ + } /* for */ + } else { + /* Normal case, i.e., the operation has one, two, or three operands + (and isn't an operation that has a variable number of operands -- + some of which may be types -- like a builtin-operation). */ + if (num_operands == 1 && !is_postfix) { + /* Prefix unary operator -- operator comes first. */ + write_id_str(operator_str, dctl); + if (strcmp(operator_str, "delete") == 0 || + strcmp(operator_str, "delete[]") == 0) { + /* Add a space to separate from expression. */ + write_id_ch(' ', dctl); + } /* if */ + } /* if */ + /* Process the first operand. */ + p = demangle_expression(p, /*need_parens=*/TRUE, dctl); + if (num_operands == 1 && is_postfix) { + /* Postfix unary operator -- operator comes last. */ + write_id_str(operator_str, dctl); + } /* if */ + if (num_operands > 1) { + /* Binary and ternary operators -- operator comes after first + operand. */ + if (strcmp(operator_str, "[]") == 0) { + /* For subscripting, put one "[" between the operands and one + at the end. */ + operator_str = "["; + close_str = "]"; + } /* if */ + write_id_str(operator_str, dctl); + /* Process the second operand. */ + p = demangle_expression(p, /*need_parens=*/TRUE, dctl); + if (num_operands > 2) { + /* Ternary operand -- "?". */ + write_id_ch(':', dctl); + /* Process the third operand. */ + p = demangle_expression(p, /*need_parens=*/TRUE, dctl); + } /* if */ + } /* if */ + } /* if */ + } else if (strcmp(operator_str, "throw ") == 0) { + /* A rethrow has no operands, just put out the string. */ + write_id_str(operator_str, dctl); + } /* if */ + write_id_str(close_str, dctl); +skip_operand_loop: + if (need_parens) write_id_ch(')', dctl); + /* Check for the final "O". */ + if (get_char(p, dctl) != 'O') { + bad_mangled_name(dctl); + } else { + p++; + } /* if */ + } /* if */ + return p; +} /* demangle_operation */ + + +static void clear_template_param_block(a_template_param_block_ptr tpbp) +/* +Clear the fields of the indicated template parameter block. +*/ +{ + tpbp->nesting_level = 0; + tpbp->final_specialization = NULL; + tpbp->set_final_specialization = FALSE; + tpbp->actual_template_args_until_final_specialization = FALSE; + tpbp->output_only_correspondences = FALSE; + tpbp->first_correspondence = FALSE; + tpbp->use_old_form_for_template_output = FALSE; +} /* clear_template_param_block */ + + +static a_const_char *demangle_template_arguments( + a_const_char *ptr, + a_boolean emit_arg_values, + a_boolean suppress_angle_brackets, + a_template_param_block_ptr temp_par_info, + a_decode_control_block_ptr dctl) +/* +Demangle the template class arguments or template parameter pack beginning at +ptr and output the demangled form. Return a pointer to the character position +following what was demangled. ptr points to just past the "__tm__", "__ps__", +"__pt__", or "__pk__" string. emit_arg_values is TRUE if the template +argument "values" (i.e., type or nontype value) should be emitted rather than +the template parameter name. This is used for a partial-specialization +parameter list ("__ps__") or parameter pack ("__pk__"). Angle brackets are +suppressed when suppress_angle_brackets is TRUE (when called to demangle +a parameter pack). When temp_par_info != NULL, it points to a block that +controls output of extra information on template parameters. +*/ +{ + a_const_char *p = ptr, *arg_base, *prev_end; + char ch; + unsigned long nchars, position; + a_boolean nontype, skipped, unskipped, is_pack; + + if (temp_par_info != NULL && !emit_arg_values) { + temp_par_info->nesting_level++; + } /* if */ + /* A template argument list looks like + __tm__3_ii + ^^---- Argument types. + ^------- Size of argument types, including the underscore. + ^------- ptr points here. + For the first argument list of a partial specialization, "__tm__" is + replaced by "__ps__". For old-form mangling of templates, "__tm__" + is replaced by "__pt__". Template arguments can be either nontype + (as identified by an "X"), a template argument pack (as identified + by "__pk__"), or types (otherwise). + */ + if (!suppress_angle_brackets) write_id_ch('<', dctl); + /* Scan the size. */ + p = get_length(p, &nchars, &prev_end, dctl); + arg_base = p; + p = advance_past_underscore(p, dctl); + /* Loop to process the arguments. */ + for (position = 1;; position++) { + /* Check for zero arguments case. */ + if ((unsigned long)(p - arg_base) >= nchars) break; + if (dctl->err_in_id) break; /* Avoid infinite loops on errors. */ + if (start_of_id_is("__pk__", p, dctl)) { + /* Template argument packs are encoded much like a template argument + list, except "__pk__" is used to introduce them: + __pk__3_sc + ^^---- Argument types. + ^------- Size of argument types, including the underscore. + */ + is_pack = TRUE; + p+=6; /* Advance past the "__pk__". */ + } else { + is_pack = FALSE; + } /* if */ + ch = get_char(p, dctl); + if (ch == '\0' || (ch == '_' && !is_pack)) { + /* We ran off the end of the string. */ + bad_mangled_name(dctl); + break; + } /* if */ + /* "X" identifies the beginning of a nontype argument. */ + nontype = (ch == 'X'); + skipped = unskipped = FALSE; + if (!emit_arg_values && temp_par_info != NULL && + !temp_par_info->use_old_form_for_template_output && + !temp_par_info->actual_template_args_until_final_specialization) { + /* Doing something special: writing out the template parameter name. */ + if (temp_par_info->output_only_correspondences) { + /* This is the second pass, which writes out parameter/argument + correspondences, e.g., "T1=int". Output has been suppressed + in general, and is turned on briefly here. */ + dctl->suppress_id_output--; + unskipped = TRUE; + /* Put out a comma between entries and a left bracket preceding the + first entry. */ + if (temp_par_info->first_correspondence) { + write_id_str(" [with ", dctl); + temp_par_info->first_correspondence = FALSE; + } else { + write_id_str(", ", dctl); + } /* if */ + } /* if */ + /* Write the template parameter name. */ + write_template_parameter_name(temp_par_info->nesting_level + + dctl->mangling_nesting_level, + position, + nontype, dctl); + if (temp_par_info->output_only_correspondences) { + /* This is the second pass, to write out correspondences, so put the + argument value out after the parameter name. */ + if (is_pack) { + /* Indicate this is a pack (only in the correspondences). */ + write_id_str("...", dctl); + } /* if */ + write_id_ch('=', dctl); + } else { + /* This is the first pass. The argument value is skipped. In + the second pass, its value will be written out. */ + /* We still have to scan over the argument value, but suppress + output. */ + dctl->suppress_id_output++; + skipped = TRUE; + } /* if */ + } /* if */ + /* Write the argument value. */ + if (nontype) { + /* Nontype argument. */ + p++; /* Advance past the "X". */ + p = demangle_constant(p, /*suppress_address_of=*/FALSE, + /*need_parens=*/FALSE, dctl); + } else if (is_pack) { + /* A template argument pack. */ + a_template_param_block pack_temp_par_info; + clear_template_param_block(&pack_temp_par_info); + /* Recurse to handle the template argument pack. */ + p = demangle_template_arguments(p, /*emit_arg_values=*/TRUE, + /*suppress_angle_brackets=*/TRUE, + &pack_temp_par_info, dctl); + } else { + /* Type argument. */ + p = demangle_type(p, dctl); + } /* if */ + if (skipped) dctl->suppress_id_output--; + if (unskipped) dctl->suppress_id_output++; + /* Stop after the last argument. */ + if ((unsigned long)(p - arg_base) >= nchars) break; + write_id_str(", ", dctl); + } /* for */ + dctl->end_of_name = prev_end; + if (!suppress_angle_brackets) write_id_ch('>', dctl); + return p; +} /* demangle_template_arguments */ + + +static a_const_char *demangle_operator( + a_const_char *ptr, + int *mangled_length, + a_boolean *takes_type, + a_boolean *is_new_style_cast, + a_boolean *is_postfix, + a_boolean *need_adl_parens, + a_boolean *is_initializer_list, + a_boolean *ud_suffix_follows, + a_decode_control_block_ptr dctl) +/* +Examine the first few characters at ptr to see if they are an encoding for +an operator (e.g., "pl" for plus). If so, return a pointer to a string for +the operator (e.g., "+"), set *mangled_length to the number of characters +in the encoding, and *takes_type to TRUE if the operator takes a type +modifier (e.g., cast). *is_new_style_cast is set to TRUE if the operator +is a new style cast (and needs a closing '>' and expression emitted). +*is_postfix is set to TRUE if the operator is a postfix operator (unary +operators are typically emitted as prefix). *need_adl_parens is set to TRUE +if the operator is a call that requires parentheses to suppress ADL. +*is_initializer_list is set to TRUE if the operator is an initializer list. +*ud_suffix_follows is set to TRUE if a literal operator (i.e., operator "") +is scanned and the ud-suffix follows the decoded operator name (which +must then be demangled by the caller). +If the first few characters are not an operator encoding, return NULL. +*/ +{ + a_const_char *s; + int len = 2; + + *takes_type = FALSE; + *is_new_style_cast = FALSE; + *is_postfix = FALSE; + *need_adl_parens = FALSE; + *is_initializer_list = FALSE; + *ud_suffix_follows = FALSE; + /* The length-3 codes are tested first to avoid taking their first two + letters as one of the length-2 codes. */ + if (start_of_id_is("apl", ptr, dctl)) { + s = "+="; + len = 3; + } else if (start_of_id_is("ami", ptr, dctl)) { + s = "-="; + len = 3; + } else if (start_of_id_is("amu", ptr, dctl)) { + s = "*="; + len = 3; + } else if (start_of_id_is("adv", ptr, dctl)) { + s = "/="; + len = 3; + } else if (start_of_id_is("amd", ptr, dctl)) { + s = "%="; + len = 3; + } else if (start_of_id_is("aer", ptr, dctl)) { + s = "^="; + len = 3; + } else if (start_of_id_is("aad", ptr, dctl)) { + s = "&="; + len = 3; + } else if (start_of_id_is("aor", ptr, dctl)) { + s = "|="; + len = 3; + } else if (start_of_id_is("ars", ptr, dctl)) { + s = ">>="; + len = 3; + } else if (start_of_id_is("als", ptr, dctl)) { + s = "<<="; + len = 3; + } else if (start_of_id_is("ppe", ptr, dctl)) { + s = "++"; + len = 3; + } else if (start_of_id_is("mme", ptr, dctl)) { + s = "--"; + len = 3; + } else if (start_of_id_is("nwa", ptr, dctl)) { + s = "new[]"; + len = 3; + } else if (start_of_id_is("dla", ptr, dctl)) { + s = "delete[]"; + len = 3; + } else if (start_of_id_is("nw", ptr, dctl)) { + s = "new"; + } else if (start_of_id_is("gc", ptr, dctl)) { + s = "gcnew"; + } else if (start_of_id_is("dl", ptr, dctl)) { + s = "delete"; + } else if (start_of_id_is("pl", ptr, dctl)) { + s = "+"; + } else if (start_of_id_is("mi", ptr, dctl)) { + s = "-"; + } else if (start_of_id_is("ml", ptr, dctl)) { + s = "*"; + } else if (start_of_id_is("dv", ptr, dctl)) { + s = "/"; + } else if (start_of_id_is("md", ptr, dctl)) { + s = "%"; + } else if (start_of_id_is("er", ptr, dctl)) { + s = "^"; + } else if (start_of_id_is("ad", ptr, dctl)) { + s = "&"; + } else if (start_of_id_is("or", ptr, dctl)) { + s = "|"; + } else if (start_of_id_is("co", ptr, dctl)) { + s = "~"; + } else if (start_of_id_is("nt", ptr, dctl)) { + s = "!"; + } else if (start_of_id_is("as", ptr, dctl)) { + s = "="; + } else if (start_of_id_is("lt", ptr, dctl)) { + s = "<"; + } else if (start_of_id_is("gt", ptr, dctl)) { + s = ">"; + } else if (start_of_id_is("ls", ptr, dctl)) { + s = "<<"; + } else if (start_of_id_is("rs", ptr, dctl)) { + s = ">>"; + } else if (start_of_id_is("eq", ptr, dctl)) { + s = "=="; + } else if (start_of_id_is("ne", ptr, dctl)) { + s = "!="; + } else if (start_of_id_is("le", ptr, dctl)) { + s = "<="; + } else if (start_of_id_is("ge", ptr, dctl)) { + s = ">="; + } else if (start_of_id_is("aa", ptr, dctl)) { + s = "&&"; + } else if (start_of_id_is("oo", ptr, dctl)) { + s = "||"; + } else if (start_of_id_is("pp", ptr, dctl)) { + s = "++"; + *is_postfix = TRUE; + } else if (start_of_id_is("mm", ptr, dctl)) { + s = "--"; + *is_postfix = TRUE; + } else if (start_of_id_is("cm", ptr, dctl)) { + s = ","; + } else if (start_of_id_is("rm", ptr, dctl)) { + s = "->*"; + } else if (start_of_id_is("rf", ptr, dctl)) { + s = "->"; + } else if (start_of_id_is("cl", ptr, dctl)) { + s = "()"; + } else if (start_of_id_is("cp", ptr, dctl)) { + *need_adl_parens = TRUE; + s = "()"; + } else if (start_of_id_is("vc", ptr, dctl)) { + s = "[]"; + } else if (start_of_id_is("qs", ptr, dctl)) { + s = "?"; + } else if (start_of_id_is("mn", ptr, dctl)) { + s = "?"; + } else if (start_of_id_is("ds", ptr, dctl)) { + s = ".*"; + } else if (start_of_id_is("dt", ptr, dctl)) { + s = "."; + } else if (start_of_id_is("ps", ptr, dctl)) { + s = "+"; + } else if (start_of_id_is("ng", ptr, dctl)) { + s = "-"; + } else if (start_of_id_is("de", ptr, dctl)) { + s = "*"; + } else if (start_of_id_is("ao", ptr, dctl)) { + s = "&"; + } else if (start_of_id_is("rl", ptr, dctl)) { + s = "__real("; + } else if (start_of_id_is("im", ptr, dctl)) { + s = "__imag("; + } else if (start_of_id_is("dc", ptr, dctl)) { + s = "dynamic_cast<"; + *is_new_style_cast = TRUE; + *takes_type = TRUE; + } else if (start_of_id_is("sc", ptr, dctl)) { + s = "static_cast<"; + *is_new_style_cast = TRUE; + *takes_type = TRUE; + } else if (start_of_id_is("cc", ptr, dctl)) { + s = "const_cast<"; + *is_new_style_cast = TRUE; + *takes_type = TRUE; + } else if (start_of_id_is("rc", ptr, dctl)) { + s = "reinterpret_cast<"; + *is_new_style_cast = TRUE; + *takes_type = TRUE; + } else if (start_of_id_is("sf", ptr, dctl)) { + s = "safe_cast<"; + *is_new_style_cast = TRUE; + *takes_type = TRUE; + } else if (start_of_id_is("tw", ptr, dctl)) { + s = "throw "; + } else if (start_of_id_is("sz", ptr, dctl)) { + s = "sizeof("; + *takes_type = TRUE; + } else if (start_of_id_is("cs", ptr, dctl)) { + s = "cast"; + *takes_type = TRUE; + } else if (start_of_id_is("af", ptr, dctl)) { + s = "__alignof__("; + *takes_type = TRUE; + } else if (start_of_id_is("uu", ptr, dctl)) { + s = "__uuidof("; + *takes_type = TRUE; + } else if (start_of_id_is("ty", ptr, dctl)) { + s = "typeid("; + *takes_type = TRUE; + } else if (start_of_id_is("ct", ptr, dctl)) { + s = "::typeid"; + *takes_type = TRUE; + } else if (start_of_id_is("bi", ptr, dctl)) { + s = "builtin-operation"; + } else if (start_of_id_is("sp", ptr, dctl)) { + s = "..."; + *is_postfix = TRUE; + } else if (start_of_id_is("sk", ptr, dctl)) { + s = "sizeof...("; + *takes_type = TRUE; + } else if (start_of_id_is("ht", ptr, dctl)) { + s = "%"; + } else if (start_of_id_is("sb", ptr, dctl)) { + s = "subscript"; + } else if (start_of_id_is("il", ptr, dctl)) { + s = "{"; + *is_initializer_list = TRUE; + } else if (start_of_id_is("tl", ptr, dctl)) { + s = "{"; + *takes_type = TRUE; + *is_initializer_list = TRUE; + } else if (start_of_id_is("nx", ptr, dctl)) { + s = "noexcept("; + } else if (start_of_id_is("li", ptr, dctl)) { + /* Note that the ud-suffix follows the "li" and needs to be + demangled by the caller. */ + s = "\"\""; + *ud_suffix_follows = TRUE; + } else if (start_of_id_is("aw", ptr, dctl)) { + s = "co_await"; + } else if (start_of_id_is("fr", ptr, dctl) || + start_of_id_is("fl", ptr, dctl) || + start_of_id_is("fR", ptr, dctl) || + start_of_id_is("fL", ptr, dctl)) { + /* A fold expression. The actual length is not easily known and must be + determined by the caller. The returned value is simply a flag (and + not an actual operation string). */ + s = "fold-ex"; + } else { + s = NULL; + } /* if */ + *mangled_length = len; + return s; +} /* demangle_operator */ + + +static a_boolean is_operator_function_name( + a_const_char *ptr, + a_const_char **demangled_name, + int *mangled_length, + a_boolean *ud_suffix_follows, + a_decode_control_block_ptr dctl) +/* +Examine the string beginning at ptr to see if it is the mangled name for +an operator function. If so, return TRUE and set *demangled_name to +the demangled form, and *mangled_length to the length of the mangled form. +*ud_suffix_follows is set to TRUE if the encoding for the operator function +name is followed by a user-defined literal suffix (which must be demangled +by the caller). +*/ +{ + a_const_char *s, *end_ptr; + int len; + a_boolean takes_type, is_new_style_cast, is_postfix, need_adl_parens; + a_boolean is_initializer_list; + + /* Get the operator name. */ + s = demangle_operator(ptr, &len, &takes_type, &is_new_style_cast, + &is_postfix, &need_adl_parens, &is_initializer_list, + ud_suffix_follows, dctl); + if (s != NULL) { + /* Make sure we took the whole name and nothing more. */ + end_ptr = ptr + len; + if (*ud_suffix_follows && end_ptr != NULL && !dctl->err_in_id && + isdigit((unsigned char)get_char(end_ptr, dctl))) { + /* If a ud-suffix follows, make sure its length is accounted for. + Note that prev_end is restored here as this is just speculative + look ahead. */ + unsigned long num; + a_const_char *prev_end; + end_ptr = get_length(end_ptr, &num, &prev_end, dctl); + end_ptr = end_ptr + num; + /* Restore any fields that might have been changed by the speculative + lookahead. */ + dctl->end_of_name = prev_end; + dctl->err_in_id = FALSE; + } /* if */ + if (get_char(end_ptr, dctl) == '\0' || + (get_char(end_ptr, dctl) == '_' && get_char(end_ptr+1, dctl) == '_')) { + /* Okay. */ + } else { + s = NULL; + *ud_suffix_follows = FALSE; + } /* if */ + } /* if */ + *demangled_name = s; + *mangled_length = len; + return (s != NULL); +} /* is_operator_function_name */ + + +static void note_specialization(a_const_char *ptr, + a_template_param_block_ptr temp_par_info) +/* +Note the fact that a specialization indication has been encountered at ptr +while scanning a mangled name. temp_par_info, if non-NULL, points to +a block of information related to template parameter processing. +*/ +{ + if (temp_par_info != NULL) { + if (temp_par_info->set_final_specialization) { + /* Remember the location of the last specialization seen. */ + temp_par_info->final_specialization = ptr; + } else if (temp_par_info->actual_template_args_until_final_specialization&& + ptr == temp_par_info->final_specialization) { + /* Stop doing the special processing for specializations when the + final specialization is reached. */ + temp_par_info->actual_template_args_until_final_specialization = FALSE; + } /* if */ + } /* if */ +} /* note_specialization */ + + +static a_const_char *demangle_function_local_indication( + a_const_char *ptr, + unsigned long nchars, + unsigned long *instance, + a_decode_control_block_ptr dctl) +/* +Demangle the function name and id number in a function-local indication: + + __L2__f__Fv + ^-- returned pointer points here + ^------- mangled function name + ^---------- instance number within function (ptr points here on entry) + +ptr points to the character after the "__L". If nchars is non-zero, it +indicates the length of the string, starting from ptr. Return a pointer +to the character following the mangled function name. Output a function +indication like "f(void)::". The instance number is simply a way of +differentiating between similarly named entities in the same function and +may be a discriminator (for class/scoped enums), scope number, or block number +depending what is being mangled and is returned to the caller in *instance. +This allows the caller to emit it later (after the name of the entity) or +suppress it (in cases where it is duplicated). +*/ +{ + a_const_char *p = ptr, *prev_end = NULL; + + if (nchars != 0) { + prev_end = dctl->end_of_name; + dctl->end_of_name = ptr + nchars; + } /* if */ + /* Get the instance number. */ + p = get_number(ptr, instance, dctl); + /* Check for the two underscores following the instance number. For local + class names in some older versions of the mangling scheme, there is no + following function name. */ + if (get_char(p, dctl) == '_' && get_char(p+1, dctl) == '_') { + p += 2; + /* Put out the function name. */ + if (nchars != 0) nchars -= (unsigned long)(p - ptr); + p = full_demangle_identifier(p, nchars, + /*suppress_parent_and_local_info=*/FALSE, + dctl); + write_id_str("::", dctl); + } /* if */ + if (prev_end != NULL) dctl->end_of_name = prev_end; + return p; +} /* demangle_function_local_indication */ + + +static void emit_instance(unsigned long instance, + a_decode_control_block_ptr dctl) +/* +The instance number is part of a local function mangling (used to +differentiate between entities with the same name within the same function). +This could represent a discriminator, scope number or block number depending +on what has been mangled. Emit it as an instance number. +*/ +{ + if (!dctl->err_in_id) { + write_id_str(" (instance ", dctl); + write_id_number(instance, dctl); + write_id_str(")", dctl); + } /* if */ +} /* emit_instance */ + + +static a_const_char *demangle_name( + a_const_char *ptr, + unsigned long nchars, + a_boolean stop_on_underscores, + unsigned long *nchars_left, + a_const_char *mclass, + a_template_param_block_ptr temp_par_info, + a_boolean *instance_emitted, + a_decode_control_block_ptr dctl) +/* +Demangle the name at ptr and output the demangled form. Return a pointer +to the character position following what was demangled. A "name" is +usually just a string of alphanumeric characters. However, names of +constructors, destructors, and operator functions require special +handling, as do template entity names. A name at this level +does not include any associated parent or function-local information, +nor function-parameter information. nchars indicates the number +of characters in the name, or is zero if the name is open-ended +(it's ended by a null or double underscore). A double underscore +ends the name if stop_on_underscores is TRUE (though some sequences +beginning with two underscores and related to templates, e.g., "__pt", +are recognized and processed locally regardless of the setting of +stop_on_underscores). If nchars_left is non-NULL, no error is +issued if too few characters are taken to satisfy nchars; +the count of remaining characters is placed in *nchars_left. +mclass, when non-NULL, points to the mangled form of the class of +which this name is a member. When it's non-NULL, constructor and +destructor names will be put out in the proper form (otherwise, +they are left in their original forms). If instance_emitted is non-NULL, +it is set to TRUE if the name has an instance number (as is the case +with unnamed types and lambdas); this allows the caller to suppress +duplicate instance numbers when the type appears in a local environment. +instance_emitted is set to FALSE otherwise. When temp_par_info != NULL, +it points to a block that controls output of extra information on +template parameters. +*/ +{ + a_const_char *p, *end_ptr = NULL, *prev_end = NULL; + a_boolean is_special_name = FALSE, is_pt, is_partial_spec = FALSE; + a_boolean partial_spec_output_suppressed = FALSE, ud_suffix_follows; + a_const_char *demangled_name; + int mangled_length; + unsigned long discriminator; + + if (instance_emitted != NULL) *instance_emitted = FALSE; + if (nchars != 0) { + prev_end = dctl->end_of_name; + dctl->end_of_name = ptr + nchars; + } /* if */ + if (nchars_left != NULL) *nchars_left = 0; + /* See if the name is special in some way. */ + if (get_char(ptr, dctl) == '_' && get_char(ptr+1, dctl) == '_') { + /* Name beginning with two underscores. */ + p = ptr + 2; + if (start_of_id_is("ct__", p, dctl) || + start_of_id_is("st__", p, dctl)) { + /* Constructor or C++/CLI static constructor. */ + end_ptr = p + 2; + if (mclass == NULL) { + /* The mangled name for the class is not provided, so handle this as + a normal name. */ + } else { + /* Output the class name for the constructor name. */ + is_special_name = TRUE; + (void)full_demangle_type_name(mclass, /*base_name_only=*/TRUE, + /*temp_par_info=*/ + (a_template_param_block_ptr)NULL, + /*is_destructor_name=*/FALSE, + dctl); + if (start_of_id_is("st__", p, dctl)) { + /* Add an indication that this is a C++/CLI static constructor. */ + write_id_str("[static]", dctl); + } /* if */ + } /* if */ + } else if (start_of_id_is("dt__", p, dctl) || + start_of_id_is("df__", p, dctl)) { + /* Destructor or C++/CLI finalizer. */ + end_ptr = p + 2; + if (mclass == NULL) { + /* The mangled name for the class is not provided, so handle this as + a normal name. */ + } else { + /* Output ~class-name for the destructor name, or !class-name for + a C++/CLI finalizer. */ + is_special_name = TRUE; + if (start_of_id_is("df__", p, dctl)) { + write_id_ch('!', dctl); + } else { + write_id_ch('~', dctl); + } /* if */ + (void)full_demangle_type_name(mclass, /*base_name_only=*/TRUE, + /*temp_par_info=*/ + (a_template_param_block_ptr)NULL, + /*is_destructor_name=*/FALSE, + dctl); + } /* if */ + } else if (start_of_id_is("dn__", p, dctl)) { + /* Destructor name. */ + /* This differs from the dt__ case above in two ways: its demangling + doesn't always have a scope operator (i.e., ::), and it doesn't + require that the destructor name be the same as the qualifying type + (e.g., it can handle T::~X()). What follows (a "destructor name") + can be parsed as a nested type, but has an implied ~ before the + final qualifier. For example, Q4_1A1B1C1D would demangle as + A::B::C::~D and 1A would demangle as ~A (as in a.~A()). */ + is_special_name = TRUE; + if (get_char(p+4, dctl) == 'Q') { + /* Destructor is qualified. */ + end_ptr = full_demangle_type_name(p+4, /*base_name_only=*/FALSE, + /*temp_par_info=*/ + (a_template_param_block_ptr)NULL, + /*is_destructor_name=*/TRUE, + dctl); + } else { + /* An unqualified type. */ + write_id_ch('~', dctl); + end_ptr = demangle_type(p+4, dctl); + } /* if */ + } else if (start_of_id_is("op", p, dctl)) { + /* Conversion function. Name looks like __opi__... where the part + after "op" encodes the type (e.g., "opi" is "operator int"). */ + is_special_name = TRUE; + write_id_str("operator ", dctl); + end_ptr = demangle_type(p+2, dctl); + } else if (is_operator_function_name(p, &demangled_name, + &mangled_length, &ud_suffix_follows, + dctl)) { + /* Operator function. */ + is_special_name = TRUE; + write_id_str("operator ", dctl); + write_id_str(demangled_name, dctl); + end_ptr = p + mangled_length; + if (ud_suffix_follows) { + /* A literal operator (i.e., operator ""); the ud-suffix follows. */ + end_ptr = demangle_name_with_preceding_length(end_ptr, dctl); + } /* if */ + } else if (nchars != 0 && start_of_id_is("N", p, dctl)) { + /* __Nxxxx: unnamed namespace name. Put out "" and ignore + the characters after "__N". For nested unnamed namespaces there + is no number after the "__N". */ + is_special_name = TRUE; + write_id_str("", dctl); + end_ptr = p + nchars - 2; + } else if (nchars != 0 && start_of_id_is("INTERNAL", p, dctl)) { + /* __INTERNAL: An individuated namespace name. */ + is_special_name = TRUE; + write_id_str("[local to ", dctl); + end_ptr = demangle_module_id(p+8, nchars-(8+2), p-2, dctl); + write_id_str("]", dctl); + } else if (start_of_id_is("Ut", p, dctl)) { + /* __Utnn: An unnamed type. */ + write_id_str("[unnamed type", dctl); + p = get_number(p+2, &discriminator, dctl); + if (discriminator > 0) { + write_id_str(" (instance ", dctl); + write_id_number(discriminator, dctl); + write_id_str(")", dctl); + is_special_name = TRUE; + end_ptr = p; + if (instance_emitted != NULL) *instance_emitted = TRUE; + } else { + bad_mangled_name(dctl); + } /* if */ + write_id_str("]", dctl); + } else if (start_of_id_is("Ul", p, dctl) || + start_of_id_is("Um", p, dctl)) { + /* __Ulnn_ or __Umnn_: Lambda closure. + For demangling purposes, treat these the same; the member initializer + case will be preceded by the name of the member being initialized, + so no further words are necessary. */ + p = get_number(p+2, &discriminator, dctl); + if (get_char(p, dctl) == '_') { + write_id_str("[lambda", dctl); + p = demangle_type(p+1, dctl); + if (discriminator > 0) { + write_id_str(" (instance ", dctl); + write_id_number(discriminator, dctl); + write_id_str(")", dctl); + is_special_name = TRUE; + end_ptr = p; + if (instance_emitted != NULL) *instance_emitted = TRUE; + } else { + bad_mangled_name(dctl); + } /* if */ + write_id_str("]", dctl); + } else { + bad_mangled_name(dctl); + } /* if */ + } else if (start_of_id_is("Ud", p, dctl)) { + /* __Udnn_p_: Lambda closure in default argument. + Note that this will always appear in a local function context, but + that is handled at a higher level. */ + unsigned long param_num; + p = get_number(p+2, &discriminator, dctl); + if (get_char(p, dctl) == '_') { + p = get_number(p+1, ¶m_num, dctl); + if (get_char(p, dctl) == '_') { + write_id_str("[lambda", dctl); + p = demangle_type(p+1, dctl); + write_id_str(" in default argument ", dctl); + write_id_number(param_num, dctl); + write_id_str(" (from end)", dctl); + if (discriminator > 0) { + write_id_str(" (instance ", dctl); + write_id_number(discriminator, dctl); + write_id_str(")", dctl); + is_special_name = TRUE; + end_ptr = p; + if (instance_emitted != NULL) *instance_emitted = TRUE; + } else { + bad_mangled_name(dctl); + } /* if */ + write_id_str("]", dctl); + } else { + bad_mangled_name(dctl); + } /* if */ + } /* if */ + } else if (start_of_id_is("ab", p, dctl)) { + /* __abnn: Mangling for __attribute(abi_tag((tag)). Multiple + "abi_tag" attributes can be specified. */ + unsigned long count; + p = p+2; + write_id_str("[abi:", dctl); + for (;;) { + p = get_number(p, &count, dctl); + if (count == 0 || p+count > dctl->end_of_name) { + bad_mangled_name(dctl); + break; + } /* if */ + while (count--) { + write_id_ch(*p++, dctl); + } /* while */ + if (start_of_id_is("__ab", p, dctl)) { + p = p+4; + write_id_ch(',', dctl); + } else { + break; + } /* if */ + } /* for */ + write_id_ch(']', dctl); + if (!dctl->err_in_id) { + /* This mangling is basically a prefix; what remains is still a name + (possibly with special names that are checked for above); recurse + to handle that case. */ + if (nchars != 0) { + if (nchars > (p - ptr)) { + nchars -= (unsigned long)(p - ptr); + } else { + bad_mangled_name(dctl); + } /* if */ + } /* if */ + end_ptr = demangle_name(p, nchars, stop_on_underscores, nchars_left, + mclass, temp_par_info, instance_emitted, dctl); + } else { + end_ptr = p; + } /* if */ + goto end_of_routine; + } else if (start_of_id_is("SBC__", p, dctl)) { + /* Mangled name for a structured binding container. */ + write_id_str("structured binding for [", dctl); + for (p = p+5; *p != '\0';) { + if (get_char(p, dctl) == '_') { + if (get_char(p+1, dctl) == '_') { + if (get_char(p+2, dctl) == '_') { + if (get_char(p+3, dctl) == '_') { + /* Quadruple underscore -- end of structured binding. */ + p += 4; + } else { + p += 3; + bad_mangled_name(dctl); + } /* if */ + break; + } else { + /* Double underscore -- structured binding delimiter. */ + write_id_ch(',', dctl); + p += 2; + } /* if */ + } else { + /* Underscore followed by non-underscore -- just copy to output. */ + write_id_ch('_', dctl); + write_id_ch(p[1], dctl); + p += 2; + } /* if */ + } else { + /* Not an underscore -- just copy to output. */ + write_id_ch(*p, dctl); + p += 1; + } /* if */ + } /* for */ + write_id_ch(']', dctl); + end_ptr = p; + goto end_of_routine; + } else { + /* Something unrecognized. */ + } /* if */ + } /* if */ + /* Here, end_ptr non-null means the end of the string has been found + already (because the name is special in some way). */ + if (end_ptr == NULL) { + /* Not a special name. Find the end of the string and set end_ptr. + Also look for template-related things that terminate the name + earlier. */ + for (p = ptr; ; p++) { + char ch = get_char(p, dctl); + /* Stop at the end of the string. */ + if (ch == '\0') break; + /* Stop on a double underscore, but not one at the start of the string. + More than 2 underscores in a row does not terminate the string, + so that something like the name for "void f_()" (i.e., "f___Fv") + can be demangled successfully. */ + if (ch == '_' && p != ptr && + get_char(p+1, dctl) == '_' && + get_char(p+2, dctl) != '_' && + /* When stop_on_underscores is FALSE, stop only on "__tm__", + "__ps__", "__pt__", or "__S". Double underscores can appear + in the middle of some names, e.g., member names used as + template arguments. */ + (stop_on_underscores || + (get_char(p+2, dctl) == 't' && + get_char(p+3, dctl) == 'm' && + get_char(p+4, dctl) == '_' && + get_char(p+5, dctl) == '_') || + (get_char(p+2, dctl) == 'p' && + get_char(p+3, dctl) == 's' && + get_char(p+4, dctl) == '_' && + get_char(p+5, dctl) == '_') || + (get_char(p+2, dctl) == 'p' && + get_char(p+3, dctl) == 't' && + get_char(p+4, dctl) == '_' && + get_char(p+5, dctl) == '_') || + get_char(p+2, dctl) == 'S')) { + break; + } /* if */ + } /* for */ + end_ptr = p; + } /* if */ + /* Here, end_ptr indicates the character after the end of the initial + part of the name. */ + if (!is_special_name) { + /* Output the characters of the base name. */ + for (p = ptr; p < end_ptr; p++) write_id_ch(*p, dctl); + } /* if */ + /* If there's a template argument list for a partial specialization + (beginning with "__ps__"), process it. */ + if (start_of_id_is("__ps__", end_ptr, dctl)) { + /* Write the arguments. This first argument list gives the arguments + that appear in the partial specialization declaration: + template struct A { ... }; + template struct A { ... }; + ^^^^^^^^this argument list + This first argument list will be followed by another argument list + that gives the arguments according to the partial specialization. + For A according to the example above, the second + argument list is . The second argument list is scanned but + not put out, except when argument correspondences are output. */ + end_ptr = demangle_template_arguments(end_ptr+6, /*emit_arg_values=*/TRUE, + /*suppress_angle_brackets=*/FALSE, + temp_par_info, dctl); + note_specialization(end_ptr, temp_par_info); + is_partial_spec = TRUE; + } /* if */ + /* If there's a specialization indication ("__S"), ignore it. */ + if (get_char(end_ptr, dctl) == '_' && + get_char(end_ptr+1, dctl) == '_' && + get_char(end_ptr+2, dctl) == 'S' && + (!stop_on_underscores || + get_char(end_ptr+3, dctl) == '\0' || + (get_char(end_ptr+3, dctl) == '_' && + get_char(end_ptr+4, dctl) == '_'))) { + note_specialization(end_ptr, temp_par_info); + end_ptr += 3; + } /* if */ + /* If there's a template argument list (beginning with "__pt__" or "__tm__"), + process it. */ + if ((is_pt = start_of_id_is("__pt__", end_ptr, dctl)) || + start_of_id_is("__tm__", end_ptr, dctl)) { + /* The "__pt__ form indicates an old-style mangled template name. */ + if (is_pt && temp_par_info != NULL ) { + temp_par_info->use_old_form_for_template_output = TRUE; + } /* if */ + /* For the second argument list of a partial specialization, + process the argument list but suppress output. */ + if (is_partial_spec && temp_par_info != NULL && + !temp_par_info->output_only_correspondences) { + dctl->suppress_id_output++; + partial_spec_output_suppressed = TRUE; + } /* if */ + /* Write the arguments. */ + end_ptr = demangle_template_arguments(end_ptr+6, /*emit_arg_values=*/FALSE, + /*suppress_angle_brackets=*/FALSE, + temp_par_info, dctl); + if (partial_spec_output_suppressed) dctl->suppress_id_output--; + /* If there's a(nother) specialization indication ("__S"), ignore it. */ + if (get_char(end_ptr, dctl) == '_' && + get_char(end_ptr+1, dctl) == '_' && + get_char(end_ptr+2, dctl) == 'S' && + (!stop_on_underscores || + get_char(end_ptr+3, dctl) == '\0' || + (get_char(end_ptr+3, dctl) == '_' && + get_char(end_ptr+4, dctl) == '_'))) { + note_specialization(end_ptr, temp_par_info); + end_ptr += 3; + } /* if */ + } /* if */ + /* Check that we took exactly the characters we should have. */ + if (nchars_left != NULL) { + /* Return the count of characters not taken. We're not required to + end at the right place. */ + *nchars_left = nchars - (unsigned long)(end_ptr-ptr); + } else if (((nchars != 0) ? (end_ptr-ptr == nchars) : (*end_ptr == '\0')) || + (stop_on_underscores && + get_char(end_ptr, dctl) == '_' && + get_char(end_ptr+1, dctl) == '_')) { + /* Okay. */ + } else { + bad_mangled_name(dctl); + } /* if */ +end_of_routine: + if (prev_end != NULL) dctl->end_of_name = prev_end; + return end_ptr; +} /* demangle_name */ + + +static a_const_char *demangle_type_name_with_preceding_length( + a_const_char *ptr, + a_boolean base_name_only, + unsigned long nchars, + unsigned long *nchars_left, + a_template_param_block_ptr temp_par_info, + a_decode_control_block_ptr dctl) +/* +Demangle a type name (or namespace name) that is preceded by a length, e.g., +"3abc" for the type name "abc". The name can include template parameters or a +function-local indication but is not a nested type. If nchars is non-zero on +input, the length has already been scanned and nchars gives its value. In that +case, not all nchars characters of input need be taken, scanning will +stop on a "__", and *nchars_left is set to the number of characters not +taken. Return a pointer to the character position following what was +demangled. When temp_par_info != NULL, it points to a block that controls +output of extra information on template parameters. When base_name_only +is TRUE, suppress any function-local information. +*/ +{ + a_const_char *p = ptr, *orig_end, *prev_end; + a_const_char *p2; + unsigned long nchars2, instance; + a_boolean has_function_local_info = FALSE; + a_boolean instance_emitted; + a_boolean stop_on_underscores; + + if (nchars == 0) { + /* Get the length. */ + p = get_length(p, &nchars, &prev_end, dctl); + nchars_left = NULL; + stop_on_underscores = FALSE; + } else { + /* Length was gotten by the caller. */ + if (nchars_left != NULL) *nchars_left = 0; + prev_end = dctl->end_of_name; + dctl->end_of_name = orig_end = ptr+nchars; + stop_on_underscores = TRUE; + } /* if */ + if (nchars >= 8) { + /* Look for a function-local indication, e.g., "__Ln__f" for block + "n" of function "f". */ + for (p2 = p+1; p2+6 < p+nchars; p2++) { + if (get_char(p2, dctl) == '_' && + get_char(p2+1, dctl) == '_') { + if (get_char(p2+2, dctl) == 't' && + get_char(p2+3, dctl) == 'm' && + get_char(p2+4, dctl) == '_' && + get_char(p2+5, dctl) == '_') { + /* Beware of a local type in a template argument list; don't + decode the local information yet, it will be decoded when the + local type is processed. */ + unsigned long skip; + a_const_char *dummy; + p2 = get_length(p2+6, &skip, &dummy, dctl); + p2 += skip; + } else if (get_char(p2+2, dctl) == 'L') { + has_function_local_info = TRUE; + nchars2 = nchars; + /* Set the length for the scan below to stop just before "__L". */ + nchars = (unsigned long)(p2 - p); + p2 += 3; /* Points to block number after "__L". */ + nchars2 -= (unsigned long)(p2 - p); + /* Output the block number and function name. */ + if (base_name_only) dctl->suppress_id_output++; + p2 = demangle_function_local_indication(p2, nchars2, &instance, + dctl); + if (base_name_only) dctl->suppress_id_output--; + break; + } /* if */ + } /* if */ + } /* for */ + } /* if */ + /* Demangle the name. */ + p = demangle_name(p, nchars, stop_on_underscores, + nchars_left, (char *)NULL, temp_par_info, + &instance_emitted, dctl); + if (has_function_local_info) { + /* Don't write the instance number in cases where an unnamed type or + lambda has already emitted it. */ + if (!instance_emitted && !base_name_only) emit_instance(instance, dctl); + p = p2; + if (nchars_left != NULL) *nchars_left = (unsigned long)(orig_end - p2); + } /* if */ + dctl->end_of_name = prev_end; + return p; +} /* demangle_type_name_with_preceding_length */ + + +static a_const_char *demangle_name_with_preceding_length( + a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle a name with a preceding length (e.g., "3abc") and return a pointer +to the character position following what was demangled. +*/ +{ + ptr = demangle_type_name_with_preceding_length( + ptr, + /*base_name_only=*/TRUE, + (unsigned long)0, + (unsigned long *)NULL, + (a_template_param_block_ptr)NULL, + dctl); + return ptr; +} /* demangle_name_with_preceding_length */ + + +static a_const_char *demangle_simple_type_name( + a_const_char *ptr, + a_boolean base_name_only, + a_template_param_block_ptr temp_par_info, + a_decode_control_block_ptr dctl) +/* +Demangle a type name (or namespace name) that can appear as part of a +nested name. Return a pointer to the character position following what +was demangled. The name is not a nested name, but it can have template +arguments. When temp_par_info != NULL, it points to a block that +controls output of extra information on template parameters. +When base_name_only is TRUE, suppress any function-local information. +*/ +{ + a_const_char *p = ptr; + + if (get_char(p, dctl) == 'Z') { + /* A template parameter name. */ + p = demangle_template_parameter_name(p, /*nontype=*/FALSE, dctl); + } else if (get_char(p, dctl) == 'G') { + /* A global scope indicator (e.g., ::A). This only occurs in the + context of a qualified name, so the caller will emit the requisite + "::" string (this is basically treated as a null qualifier). */ + p++; + } else if (isdigit((unsigned char)get_char(p, dctl))) { + /* A simple mangled type name consists of digits indicating the length of + the name followed by the name itself, e.g., "3abc". */ + p = demangle_type_name_with_preceding_length(p, base_name_only, + (unsigned long)0, + (unsigned long *)NULL, + temp_par_info, dctl); + } else { + /* Presumably a decltype or typeof. */ + p = demangle_type(p, dctl); + } /* if */ + return p; +} /* demangle_simple_type_name */ + + +static a_const_char *full_demangle_type_name( + a_const_char *ptr, + a_boolean base_name_only, + a_template_param_block_ptr temp_par_info, + a_boolean is_destructor_name, + a_decode_control_block_ptr dctl) +/* +Demangle the type name at ptr and output the demangled form. Return a pointer +to the character position following what was demangled. The name can be +a simple type name or a nested type name, or the name of a namespace. +If base_name_only is TRUE, do not put out any nested type qualifiers, +e.g., put out "A::x" as simply "x". When temp_par_info != NULL, it +points to a block that controls output of extra information on template +parameters. Note that this routine is called for namespaces too +(the mangling is the same as for class names; you can't actually tell +the difference in a mangled name). If is_destructor_name is TRUE, this type is +actually the name of a destructor and an implied "~" should be emitted before +the last component of a qualified name (e.g., T::~X). See demangle_type_name +for an interface to this routine for the simple case. +*/ +{ + a_const_char *p = ptr; + unsigned long nquals; + + if (get_char(p, dctl) == 'Q') { + /* A nested type name has the form + Q2_5outer5inner (outer::inner) + ^-----^--------Names from outermost to innermost + ^----------------Number of levels of qualification. + Note that the levels in the qualifier can be class names or namespace + names. */ + p = get_number(p+1, &nquals, dctl); + p = advance_past_underscore(p, dctl); + /* Handle each level of qualification. */ + for (; nquals > 0; nquals--) { + if (dctl->err_in_id) break; /* Avoid infinite loops on errors. */ + /* Do not put out the nested type qualifiers if base_name_only is + TRUE. */ + if (base_name_only && nquals != 1) dctl->suppress_id_output++; + if (is_destructor_name && nquals == 1) write_id_ch('~', dctl); + p = demangle_simple_type_name(p, base_name_only, temp_par_info, dctl); + if (nquals != 1) write_id_str("::", dctl); + if (base_name_only && nquals != 1) dctl->suppress_id_output--; + } /* for */ + } else { + /* A simple (non-nested) type name. */ + if (is_destructor_name) write_id_ch('~', dctl); + p = demangle_simple_type_name(p, base_name_only, temp_par_info, dctl); + } /* if */ + return p; +} /* full_demangle_type_name */ + + +static a_const_char *demangle_vtbl_class_name(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle a class or base class name that is one component of a virtual +function table name. Such names are mangled mostly as types, but with +a few special quirks. +*/ +{ + a_const_char *p = ptr, *prev_end; + unsigned long nchars, nchars_left; + + /* This code handles both the base class part of the name and + the class part. A base class name has the form + followed by one or more optionally followed + by an ambiguity specification, __A optionally followed by a number. + The ambiguity specification is not included in the length. + A is a class name mangling without preceding length, or + a "Q" nested-type-name specification. + A class name has the form + + or + Q nested-type-name specification (i.e., without preceding length). + */ + if (get_char(p, dctl) == 'Q') { + /* Nested-type-name "Q" without preceding length. This is used only + for the complete object class (the last section), not for the + base classes. */ + p = demangle_type_name(p, dctl); + } else { + /* Get the length. */ + p = get_length(p, &nchars, &prev_end, dctl); + while (!dctl->err_in_id) { + a_boolean nested_name_case = FALSE; + /* Check a "Q" nested-type-name specification by checking for "Q", + some digits, and an underscore. This rules out class names that + start with "Q". A class whose name starts with something like + "Q2_" is still going to be a problem, but that's a truly + ambiguous case. This is inherited from Cfront. */ + if (get_char(p, dctl) == 'Q') { + a_const_char *p2 = p+1; + if (isdigit((unsigned char)get_char(p2, dctl))) { + do { p2++; } while (isdigit((unsigned char)get_char(p2, dctl))); + if (get_char(p2, dctl) == '_') { + nested_name_case = TRUE; + } /* if */ + } /* if */ + } /* if */ + if (nested_name_case) { + /* Nested class name. */ + a_const_char *end_ptr = demangle_type_name(p, dctl); + unsigned long chars_taken = (unsigned long)(end_ptr - p); + nchars -= chars_taken; + p = end_ptr; + } else { + /* Non-nested class name without preceding length. */ + p = demangle_type_name_with_preceding_length( + p, /*base_name_only=*/FALSE, + nchars, &nchars_left, + (a_template_param_block_ptr)NULL, + dctl); + nchars = nchars_left; + } /* if */ + /* Leave the loop if there is not another base class in the + derivation. */ + if (nchars < 3 || !start_of_id_is("__", p, dctl)) break; + p += 2; + nchars -= 2; + write_id_str(" in ", dctl); + } /* while */ + /* Make sure we took all the characters indicated by the length. */ + if (nchars != 0) { + bad_mangled_name(dctl); + } /* if */ + dctl->end_of_name = prev_end; + if (start_of_id_is("__A", p, dctl)) { + /* "__A" indicates an ambiguous base class. This is used only on + the base class specifications. */ + write_id_str(" (ambiguous)", dctl); + p += 3; + /* Ignore the number following __A, if any. */ + while (isdigit((unsigned char)get_char(p, dctl))) p++; + } /* if */ + } /* if */ + return p; +} /* demangle_vtbl_class_name */ + + +static a_const_char *demangle_type_qualifiers( + a_const_char *ptr, + a_boolean trailing_space, + a_decode_control_block_ptr dctl) +/* +Demangle any type qualifiers (const/volatile/restrict) at the indicated +location. Return a pointer to the character position following what was +demangled. If trailing_space is TRUE, add a space at the end if any qualifiers +were put out. +*/ +{ + a_const_char *p = ptr; + a_boolean any_quals = FALSE; + + for (;; p++) { + if (get_char(p, dctl) == 'C') { + if (any_quals) write_id_ch(' ', dctl); + write_id_str("const", dctl); + } else if (get_char(p, dctl) == 'V') { + if (any_quals) write_id_ch(' ', dctl); + write_id_str("volatile", dctl); + } else if (get_char(p, dctl) == 'D' && get_char(p+1, dctl) == 'r') { + if (any_quals) write_id_ch(' ', dctl); + write_id_str("restrict", dctl); + p++; + } else { + break; + } /* if */ + any_quals = TRUE; + } /* for */ + if (any_quals && trailing_space) write_id_ch(' ', dctl); + return p; +} /* demangle_type_qualifiers */ + + +static a_const_char *demangle_ref_qualifiers( + a_const_char *p, + a_const_char **ref_qual, + a_decode_control_block_ptr dctl) +/* +The character preceding *p is an "F", indicating a function type; see if +there is a ref-qualifier, and if so, set *ref_qual to a string suitable for +output (set to NULL otherwise). Returns a pointer to the character position +following the optional ref-qualifier. +*/ +{ + *ref_qual = NULL; + if (get_char(p, dctl) == '_' && (get_char(p+1, dctl) == 'R')) { + p += 2; + *ref_qual = "&"; + } else if (get_char(p, dctl) == '_' && (get_char(p+1, dctl) == 'E')) { + p += 2; + *ref_qual = "&&"; + } /* if */ + return p; +} /* demangle_ref_qualifiers */ + + +static a_const_char *demangle_type_specifier(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle the type at ptr and output the specifier part. Return a pointer +to the character position following what was demangled. +*/ +{ + a_const_char *p = ptr, *s; + char ch; + + /* Process type qualifiers. */ + p = demangle_type_qualifiers(p, /*trailing_space=*/TRUE, dctl); + ch = get_char(p, dctl); + if (isdigit((unsigned char)ch) || ch == 'Q' || ch == 'Z') { + /* Named type, like class or enum, e.g., "3abc". */ + p = demangle_type_name(p, dctl); + } else { + /* Builtin type. */ + if (ch == 'a') { + /* GNU vector_size attribute. */ + write_id_str("__attribute__((vector_size(", dctl); + p++; + /* Scan the size. */ + while (ch = get_char(p, dctl), isdigit((unsigned char)ch)) { + write_id_ch(ch, dctl); + p++; + } /* while */ + write_id_str("))) ", dctl); + /* The underlying type follows an underscore. */ + p = advance_past_underscore(p, dctl); + ch = get_char(p, dctl); + } /* if */ + /* Handle signed and unsigned, and _Complex. */ + if (ch == 'S') { + write_id_str("signed ", dctl); + p++; + } else if (ch == 'U') { + write_id_str("unsigned ", dctl); + p++; + } else if (ch == 'x') { + write_id_str("_Complex ", dctl); + p++; + } /* if */ + switch (get_char(p++, dctl)) { + case 'v': + s = "void"; + break; + case 'c': + s = "char"; + break; + case 'w': + s = "wchar_t"; + break; + case 'b': + s = "bool"; + break; + case 's': + s = "short"; + break; + case 'i': + s = "int"; + break; + case 'l': + s = "long"; + break; + case 'L': + s = "long long"; + break; + case 'f': + s = "float"; + break; + case 'd': + s = "double"; + break; + case 'r': + s = "long double"; + break; + case 'm': + /* Microsoft intrinsic __intN types (Visual C++ 6.0 and later), as + well as GNU 128-bit integers (m16) and GNU __float80/__float128. */ + switch (get_char(p++, dctl)) { + case '1': + if (get_char(p, dctl) == '6') { + s = "__int128"; + p++; + } else { + s = "__int8"; + } /* if */ + break; + case '2': + s = "__int16"; + break; + case '4': + s = "__int32"; + break; + case '8': + s = "__int64"; + break; + case 'f': + if (get_char(p++, dctl) == '1') { + switch (get_char(p++, dctl)) { + case '0': + s = "__float80"; + break; + case '6': + s = "__float128"; + break; + default: + bad_mangled_name(dctl); + s = ""; + } /* switch */ + } else { + bad_mangled_name(dctl); + s = ""; + } /* if */ + break; + default: + bad_mangled_name(dctl); + s = ""; + } /* switch */ + break; + case 'n': + s = "std::nullptr_t"; + break; + case 'j': + s = "__nullptr"; + break; + case 'u': + s = "auto"; + break; + case 'q': + s = "decltype(auto)"; + break; + case 'g': + s = "char16_t"; + break; + case 'k': + s = "char32_t"; + break; + case 't': + /* typeof(type) */ + write_id_str("typeof(", dctl); + p = demangle_type(p, dctl); + s = ")"; + break; + case 'p': + /* typeof(expression) */ + write_id_str("typeof(", dctl); + p = demangle_expression(p, /*need_parens=*/FALSE, dctl); + s = ")"; + break; + case 'y': + /* decltype of an id-expression or class member access. */ + write_id_str("decltype(", dctl); + p = demangle_expression(p, /*need_parens=*/FALSE, dctl); + s = ")"; + break; + case 'Y': + /* decltype of an expression. */ + write_id_str("decltype((", dctl); + p = demangle_expression(p, /*need_parens=*/FALSE, dctl); + s = "))"; + break; + case 'o': + /* __underlying_type(type) */ + write_id_str("__underlying_type(", dctl); + p = demangle_type(p, dctl); + s = ")"; + break; + default: + bad_mangled_name(dctl); + s = ""; + } /* switch */ + write_id_str(s, dctl); + } /* if */ + return p; +} /* demangle_type_specifier */ + + +static a_const_char *demangle_function_parameters( + a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle the parameter list beginning at ptr and output the demangled form. +Return a pointer to the character position following what was demangled. +*/ +{ + a_const_char *p = ptr; + a_const_char *param_pos[10]; + unsigned long curr_param_num, param_num, nreps; + a_boolean any_params = FALSE; + + write_id_ch('(', dctl); + if (get_char(p, dctl) == 'v') { + /* Void parameter list. */ + p++; + } else { + any_params = TRUE; + /* Loop for each parameter. */ + curr_param_num = 1; + for (;;) { + char ch; + if (dctl->err_in_id) break; /* Avoid infinite loops on errors. */ + ch = get_char(p, dctl); + if ((ch == 'T' && isdigit((unsigned char)get_char(p+1, dctl))) || + ch == 'N') { + /* Tn means repeat the type of parameter "n". Note that a type can + begin with "Tr" (i.e., a C++/CLI tracking reference), so check for + a digit following the "T" to differentiate the two cases. */ + /* Nmn means "m" repetitions of the type of parameter "n". "m" + is a one-digit number. */ + /* "n" is also treated as a single-digit number; the front end enforces + that (in non-cfront object code compatibility mode). cfront does + not, which leads to some ambiguities when "n" is followed by + a class name. */ + if (get_char(p++, dctl) == 'N') { + /* Get the number of repetitions. */ + p = get_single_digit_number(p, &nreps, dctl); + } else { + nreps = 1; + } /* if */ + /* Get the parameter number. */ + p = get_single_digit_number(p, ¶m_num, dctl); + if (param_num < 1 || param_num >= curr_param_num || + param_pos[param_num] == NULL) { + /* Parameter number out of range. */ + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + /* Produce "nreps" copies of parameter "param_num". */ + for (; nreps > 0; nreps--) { + if (dctl->err_in_id) break; /* Avoid infinite loops on errors. */ + if (curr_param_num < 10) param_pos[curr_param_num] = NULL; + (void)demangle_type(param_pos[param_num], dctl); + if (nreps != 1) write_id_str(", ", dctl); + curr_param_num++; + } /* if */ + } else { + /* A normal parameter. */ + if (curr_param_num < 10) param_pos[curr_param_num] = p; + p = demangle_type(p, dctl); + curr_param_num++; + } /* if */ + /* Stop after the last parameter. */ + ch = get_char(p, dctl); + if (ch == '\0' || ch == 'e' || ch == '_' || ch == 'F') break; + write_id_str(", ", dctl); + } /* for */ + } /* if */ + if (get_char(p, dctl) == 'e') { + /* Ellipsis. */ + if (any_params) write_id_str(", ", dctl); + write_id_str("...", dctl); + p++; + } /* if */ + write_id_ch(')', dctl); +end_of_routine: + return p; +} /* demangle_function_parameters */ + + +static a_const_char *skip_extern_C_indication(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +ptr points to the character after the "F" of a function type. Skip over +and ignore an indication of extern "C" following the "F", if one is present. +Return a pointer to the character following the extern "C" indication. +There's no syntax for representing the extern "C" in the function type, so +just ignore it. +*/ +{ + if (get_char(ptr, dctl) == 'K') ptr++; + return ptr; +} /* skip_extern_C_indication */ + + +static a_const_char *demangle_type_first_part( + a_const_char *ptr, + a_boolean under_lhs_declarator, + a_boolean need_trailing_space, + a_decode_control_block_ptr dctl) +/* +Demangle the type at ptr and output the specifier part and the part of the +declarator that precedes the name. Return a pointer to the character +position following what was demangled. If under_lhs_declarator is TRUE, +this type is directly under a type that uses a left-side declarator, +e.g., a pointer type. (That's used to control use of parentheses around +parts of the declarator.) If need_trailing_space is TRUE, put a space +at the end of the type first part (needed if the declarator part is +not empty, because it contains a name or a derived type). +*/ +{ + a_const_char *p = ptr, *qualp = p; + char kind, ext_kind; + + /* Remove type qualifiers. */ + p = remove_immediate_type_qualifiers(p, dctl); + kind = get_char(p, dctl); + if (kind == 'P' || kind == 'R' || kind == 'E' || kind == 'H') { + a_boolean need_space = TRUE; + /* Pointer, reference, rvalue reference, or C++/CLI pointer-like type. + For example, "Pc" is pointer to char. */ + if (kind == 'H') { + /* Some kind of C++/CLI pointer-like type (handle, tracking reference, + interior_ptr, pin_ptr). */ + p++; + ext_kind = get_char(p, dctl); + if (ext_kind == 'i') { + write_id_str("interior_ptr<", dctl); + need_space = FALSE; + } else if (ext_kind == 'p') { + write_id_str("pin_ptr<", dctl); + need_space = FALSE; + } /* if */ + } /* if */ + p = demangle_type_first_part(p+1, /*under_lhs_declarator=*/TRUE, + need_space, dctl); + /* Output "*" (pointer), "&" (reference), "&&" (rvalue reference), + "^" (handle), or "%" (tracking reference). */ + if (kind == 'R') { + write_id_ch('&', dctl); + } else if (kind == 'E') { + write_id_str("&&", dctl); + } else if (kind == 'H') { + if (ext_kind == 'h') { + write_id_ch('^', dctl); + } else if (ext_kind == 't') { + write_id_ch('%', dctl); + } else if (ext_kind == 'i') { + write_id_ch('>', dctl); + } else if (ext_kind == 'p') { + write_id_ch('>', dctl); + } else { + bad_mangled_name(dctl); + } /* if */ + } else { + write_id_ch('*', dctl); + } /* if */ + /* Output the type qualifiers on the pointer, if any. */ + (void)demangle_type_qualifiers(qualp, need_trailing_space, dctl); + } else if (kind == 'M') { + /* Pointer-to-member type, e.g., "M1Ai" is pointer to member of A of + type int. */ + a_const_char *classp = p+1; + /* Skip over the class name. */ + dctl->suppress_id_output++; + p = demangle_type_name(classp, dctl); + dctl->suppress_id_output--; + p = demangle_type_first_part(p, /*under_lhs_declarator=*/TRUE, + /*need_trailing_space=*/TRUE, dctl); + /* Output Classname::*. */ + (void)demangle_type_name(classp, dctl); + write_id_str("::*", dctl); + /* Output the type qualifiers on the pointer, if any. */ + (void)demangle_type_qualifiers(qualp, need_trailing_space, dctl); + } else if (kind == 'F') { + /* Function type, e.g., "Fii_f" is function(int, int) returning float. + The return type is not present for top-level function types (except + for template functions). */ + p++; + /* An optional ref-qualifier is indicated if the 'F' is followed by + an underscore. Skip it on this pass. */ + if (get_char(p, dctl) == '_' && + (get_char(p+1, dctl) == 'R' || get_char(p+1, dctl) == 'E')) { + p += 2; + } /* if */ + /* An optional exception specification. Skip it on this pass. */ + if (get_char(p, dctl) == 'D' && + (get_char(p+1, dctl) == 'o' || get_char(p+1, dctl) == 'O')) { + if (get_char(p+1, dctl) == 'O') { + dctl->suppress_id_output++; + p = demangle_constant(p+2, /*suppress_address_of=*/FALSE, + /*need_parens=*/FALSE, dctl); + dctl->suppress_id_output--; + } else { + p += 2; + } /* if */ + } /* if */ + p = skip_extern_C_indication(p, dctl); + /* Skip over the parameter types without outputting anything. */ + dctl->suppress_id_output++; + p = demangle_function_parameters(p, dctl); + dctl->suppress_id_output--; + if (get_char(p, dctl) == '_' && get_char(p+1, dctl) != '_') { + /* The return type is present. */ + p = demangle_type_first_part(p+1, /*under_lhs_declarator=*/FALSE, + /*need_trailing_space=*/TRUE, dctl); + } /* if */ + /* This is a right-side declarator, so if it's under a left-side declarator + parentheses are needed. */ + if (under_lhs_declarator) write_id_ch('(', dctl); + } else if (kind == 'A') { + /* Array type, e.g., "A10_i" is array[10] of int. */ + p++; + if (get_char(p, dctl) == '_') { + /* Length is specified by a constant expression based on template + parameters. Ignore the expression. */ + p++; + dctl->suppress_id_output++; + p = demangle_constant(p, /*suppress_address_of=*/FALSE, + /*need_parens=*/FALSE, dctl); + dctl->suppress_id_output--; + } else if (get_char(p, dctl) == 'O') { + /* Length is specified as an expression. */ + dctl->suppress_id_output++; + p = demangle_expression(p, /*need_parens=*/FALSE, dctl); + dctl->suppress_id_output--; + } else { + /* Normal constant number of elements. */ + /* Skip the array size. */ + while (isdigit((unsigned char)get_char(p, dctl))) p++; + } /* if */ + p = advance_past_underscore(p, dctl); + /* Process the element type. */ + p = demangle_type_first_part(p, /*under_lhs_declarator=*/FALSE, + /*need_trailing_space=*/TRUE, dctl); + /* This is a right-side declarator, so if it's under a left-side declarator + parentheses are needed. */ + if (under_lhs_declarator) write_id_ch('(', dctl); + } else if (kind == 'D') { + /* The 'D' is used as an "escape" character. The following character + determines the actual action to be taken. */ + p++; + kind = get_char(p, dctl); + if (kind == 'p') { + /* A pack expansion. */ + p = demangle_type_first_part(p+1, /*under_lhs_declarator=*/FALSE, + /*need_trailing_space=*/FALSE, dctl); + } else { + bad_mangled_name(dctl); + } /* if */ + } else { + /* No declarator part to process. Handle the specifier type. */ + p = demangle_type_specifier(qualp, dctl); + if (need_trailing_space) write_id_ch(' ', dctl); + } /* if */ + return p; +} /* demangle_type_first_part */ + + +static void demangle_type_second_part( + a_const_char *ptr, + a_boolean under_lhs_declarator, + a_decode_control_block_ptr dctl) +/* +Demangle the type at ptr and output the part of the declarator that follows the +name. This routine does not return a pointer to the character position +following what was demangled; it is assumed that the caller will save +that from the call of demangle_type_first_part, and it saves a lot of +time if this routine can avoid scanning the specifiers again. +If under_lhs_declarator is TRUE, this type is directly under a type that +uses a left-side declarator, e.g., a pointer type. (That's used to control +use of parentheses around parts of the declarator.) +*/ +{ + a_const_char *p = ptr, *qualp = p; + char kind; + + /* Remove type qualifiers. */ + p = remove_immediate_type_qualifiers(p, dctl); + kind = get_char(p, dctl); + if (kind == 'P' || kind == 'R' || kind == 'E' || kind == 'H') { + /* Pointer, reference, rvalue reference, or C++/CLI pointer-like type. + For example, "Pc" is pointer to char. */ + /* If it's a C++/CLI pointer-like type, there's a second character after + the "H", but we ignore that here. */ + if (kind == 'H') p++; + demangle_type_second_part(p+1, /*under_lhs_declarator=*/TRUE, dctl); + } else if (kind == 'M') { + /* Pointer-to-member type, e.g., "M1Ai" is pointer to member of A of + type int. */ + /* Advance over the class name. */ + dctl->suppress_id_output++; + p = demangle_type_name(p+1, dctl); + dctl->suppress_id_output--; + demangle_type_second_part(p, /*under_lhs_declarator=*/TRUE, dctl); + } else if (kind == 'F') { + a_const_char *ref_qual, *save_noexcept_constant = NULL; + a_boolean is_noexcept = FALSE; + /* Function type, e.g., "Fii_f" is function(int, int) returning float. + The return type is not present for top-level function types (except + for template functions). */ + /* This is a right-side declarator, so if it's under a left-side declarator + parentheses are needed. */ + if (under_lhs_declarator) write_id_ch(')', dctl); + p++; + /* An optional ref-qualifier is indicated if the 'F' is followed by + an underscore. Emit the ref-qualifier at the end of the type. */ + p = demangle_ref_qualifiers(p, &ref_qual, dctl); + /* An optional exception specification. */ + if (get_char(p, dctl) == 'D' && + (get_char(p+1, dctl) == 'o' || get_char(p+1, dctl) == 'O')) { + if (get_char(p+1, dctl) == 'O') { + dctl->suppress_id_output++; + save_noexcept_constant = p+2; + p = demangle_constant(save_noexcept_constant, + /*suppress_address_of=*/FALSE, + /*need_parens=*/FALSE, dctl); + dctl->suppress_id_output--; + } else { + is_noexcept = TRUE; + p += 2; + } /* if */ + } /* if */ + p = skip_extern_C_indication(p, dctl); + /* Put out the parameter types. */ + p = demangle_function_parameters(p, dctl); + /* Put out any cv-qualifiers (member functions). */ + /* Note that such things could come up on nonmember functions in the + presence of typedefs. In such a case what we generate here will not + be valid C, but it's a reasonable representation of the mangled + type, and there's no way of getting the typedef name in there, + so let it be. */ + if (*qualp != 'F') { + write_id_ch(' ', dctl); + (void)demangle_type_qualifiers(qualp, /*trailing_space=*/FALSE, dctl); + } /* if */ + if (get_char(p, dctl) == '_' && get_char(p+1, dctl) != '_') { + /* Process the return type. */ + demangle_type_second_part(p+1, /*under_lhs_declarator=*/FALSE, dctl); + } /* if */ + if (ref_qual != NULL) write_id_str(ref_qual, dctl); + if (is_noexcept) { + write_id_str(" noexcept", dctl); + } else if (save_noexcept_constant != NULL) { + write_id_str(" noexcept(", dctl); + (void)demangle_constant(save_noexcept_constant, + /*suppress_address_of=*/FALSE, + /*need_parens=*/FALSE, dctl); + write_id_ch(')', dctl); + } /* if */ + } else if (kind == 'A') { + /* Array type, e.g., "A10_i" is array[10] of int. */ + /* This is a right-side declarator, so if it's under a left-side declarator + parentheses are needed. */ + if (under_lhs_declarator) write_id_ch(')', dctl); + write_id_ch('[', dctl); + p++; + if (get_char(p, dctl) == '_') { + /* Length is specified by a constant expression based on template + parameters. */ + p++; + p = demangle_constant(p, /*suppress_address_of=*/FALSE, + /*need_parens=*/FALSE, dctl); + } else if (get_char(p, dctl) == 'O') { + /* Length is specified as an expression. */ + p = demangle_expression(p, /*need_parens=*/FALSE, dctl); + } else { + /* Normal constant number of elements. */ + if (get_char(p, dctl) == '0' && get_char(p+1, dctl) == '_') { + /* Size is zero, so do not put out a size (the result is "[]"). */ + p++; + } else { + /* Put out the array size. */ + while (isdigit((unsigned char)get_char(p, dctl))) { + write_id_ch(*p++, dctl); + } /* while */ + } /* if */ + } /* if */ + p = advance_past_underscore(p, dctl); + write_id_ch(']', dctl); + /* Process the element type. */ + demangle_type_second_part(p, /*under_lhs_declarator=*/FALSE, dctl); + } else if (kind == 'D') { + /* The 'D' is used as an "escape" character. The following character + determines the actual action to be taken. */ + p++; + kind = get_char(p, dctl); + if (kind == 'p') { + /* A pack expansion. */ + p++; + write_id_str("...", dctl); + demangle_type_second_part(p, /*under_lhs_declarator=*/FALSE, dctl); + } else { + bad_mangled_name(dctl); + } /* if */ + } else { + /* No declarator part to process. No need to scan the specifiers type -- + it was done by demangle_type_first_part. */ + } /* if */ +} /* demangle_type_second_part */ + + +static a_const_char *demangle_type(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle the type at ptr and output the demangled form. Return a pointer to +the character position following what was demangled. +*/ +{ + a_const_char *p; + + /* Generate the specifier part of the type. */ + p = demangle_type_first_part(ptr, /*under_lhs_declarator=*/FALSE, + /*need_trailing_space=*/FALSE, dctl); + /* Generate the declarator part of the type. */ + demangle_type_second_part(ptr, /*under_lhs_declarator=*/FALSE, dctl); + return p; +} /* demangle_type */ + + +static a_const_char *demangle_identifier_with_preceding_length( + a_const_char *ptr, + a_boolean suppress_parent_and_local_info, + a_decode_control_block_ptr dctl) +/* +Demangle the identifier at ptr and output the demangled form. The +identifier is preceded by a length. Return a pointer to the character +position following what was demangled. An identifier can include template +argument, parent, and function-local information. +If suppress_parent_and_local_info is TRUE, do not output parent and +function-local information if present (but do scan over it). +*/ +{ + a_const_char *p = ptr, *prev_end; + unsigned long nchars; + + p = get_length(p, &nchars, &prev_end, dctl); + dctl->mangling_nesting_level++; + p = full_demangle_identifier(p, nchars, suppress_parent_and_local_info, + dctl); + dctl->mangling_nesting_level--; + dctl->end_of_name = prev_end; + return p; +} /* demangle_identifier_with_preceding_length */ + + +static a_const_char *full_demangle_identifier( + a_const_char *ptr, + unsigned long nchars, + a_boolean suppress_parent_and_local_info, + a_decode_control_block_ptr dctl) +/* +Demangle the identifier at ptr and output the demangled form. Return +a pointer to the character position following what was demangled. +If nchars > 0, take no more than that many characters. +If suppress_parent_and_local_info is TRUE, do not output parent +and function-local information if present (but do scan over it). +An identifier can include template argument, parent, and function-local +information. +*/ +{ + a_const_char *p = ptr, *pname, *end_ptr, *function_local_end_ptr = NULL; + a_const_char *final_specialization, *end_ptr_first_scan, *prev_end = NULL; + char ch; + a_const_char *oname; + a_boolean is_function = TRUE; + a_template_param_block + temp_par_info; + a_boolean is_externalized_static = FALSE; + a_boolean has_function_local_info = FALSE; + unsigned long instance; + + clear_template_param_block(&temp_par_info); + if (nchars != 0) { + prev_end = dctl->end_of_name; + dctl->end_of_name = ptr + nchars; + } /* if */ + if (start_of_id_is("__STF__", ptr, dctl)) { + /* Static function made external by addition of prefix "__STF__" and + suffix of module id. */ + is_externalized_static = TRUE; + /* Advance past __STF__. */ + ptr += 7; + if (nchars != 0) nchars -= 7; + p = ptr; + } /* if */ + /* Scan through the name (the first part of the mangled name) without + generating output, to see what's beyond it. Special processing is + necessary for names of constructors, conversion routines, etc. */ + /* If the name has a specialization indication in it (which can happen for + function names), note that fact. */ + temp_par_info.set_final_specialization = TRUE; + dctl->suppress_id_output++; + p = demangle_name(ptr, nchars, /*stop_on_underscores=*/TRUE, + (unsigned long *)NULL, + (char *)NULL, &temp_par_info, + (a_boolean *)NULL, dctl); + dctl->suppress_id_output--; + final_specialization = temp_par_info.final_specialization; + clear_template_param_block(&temp_par_info); + temp_par_info.final_specialization = final_specialization; + if (get_char(p, dctl) == '\0') { + /* There is no mangled part of the name. This happens for strange + cases like + extern "C" int operator +(A, A); + which gets mangled as "__pl". Just write out the name and stop. */ + end_ptr = demangle_name(ptr, nchars, + /*stop_on_underscores=*/TRUE, + (unsigned long *)NULL, + (char *)NULL, + (a_template_param_block_ptr)NULL, + (a_boolean *)NULL, dctl); + } else { + /* There's more. There should be a "__" between the name and the + additional mangled information. */ + if (get_char(p, dctl) != '_' || get_char(p+1, dctl) != '_') { + bad_mangled_name(dctl); + end_ptr = p; + goto end_of_routine; + } /* if */ + end_ptr = p + 2; + /* Now ptr points to the original-name part of the mangled name, and + end_ptr points to the mangled-name part at the end. + f__1AFv + ^---- end_ptr + ^------- ptr + The mangled-name part is + (a) A class name for a static data member. + (b) A class name followed by "F" followed by the encoding for the + parameter types for a member function. + (c) "F" followed by the encoding for the parameter types for a + nonmember function. + (d) "L" plus a local block number, followed by the mangled function + name, for a function-local entity. + Members of namespaces are encoded similarly. */ + p = end_ptr; + pname = NULL; + if (suppress_parent_and_local_info) dctl->suppress_id_output++; + ch = get_char(end_ptr, dctl); + if (ch == 'L') { + unsigned long nchars2 = nchars; + /* The name of an entity within a function, mangled on promotion out + of the function. For example, "i__L1__f__Fv" for "i" from block 1 + of function "f(void)". Note that this is not the same mangling + used by cfront (in the cfront scheme, the __L1 is at the end, and + the number is different). */ + /* Set a length for the name without the function-local indication, + for the processing in the rest of this routine. */ + nchars = (unsigned long)((p - 2) - ptr); + /* Demangle the function name and block number. */ + p++; /* Points to the block number following "__L". */ + if (nchars2 != 0) nchars2 -= (unsigned long)(p - ptr); + function_local_end_ptr = + demangle_function_local_indication(p, nchars2, &instance, dctl); + has_function_local_info = TRUE; + p = end_ptr = ptr + nchars; + is_function = FALSE; + /* Go on to demangle the name of the local entity. */ + } else if (ch != 'F') { + /* A class (or namespace) name must be next. */ + /* Remember the location of the parent entity name. */ + pname = end_ptr; + /* Scan over the class name, producing no output, and remembering the + position of the final specialization, if any. If we already + found a specialization on the function name, it's the final one + and we shouldn't change it. */ + dctl->suppress_id_output++; + if (temp_par_info.final_specialization == NULL) { + temp_par_info.set_final_specialization = TRUE; + } /* if */ + end_ptr = full_demangle_type_name(pname, /*base_name_only=*/FALSE, + &temp_par_info, + /*is_destructor_name=*/FALSE, + dctl); + temp_par_info.set_final_specialization = FALSE; + dctl->suppress_id_output--; + /* If the name ends here, this is a simple member (e.g., a static + data member). */ + ch = get_char(end_ptr, dctl); + if (ch == '\0' || + (ch == '_' && get_char(end_ptr+1, dctl) == '_')) { + is_function = FALSE; + } /* if */ + } /* if */ + if (suppress_parent_and_local_info) dctl->suppress_id_output--; + oname = NULL; + if (is_function) { + /* "S" here means a static member function (ignore). */ + if (get_char(end_ptr, dctl) == 'S') end_ptr++; + /* "O" here means the base class of a function that this function + explicitly overrides (a Microsoft extension) is next. */ + if (get_char(end_ptr, dctl) == 'O') { + /* Skip over the class name, producing no output. Remember its + position for later output. */ + oname = ++end_ptr; + dctl->suppress_id_output++; + end_ptr = demangle_type_name(oname, dctl); + dctl->suppress_id_output--; + } /* if */ + /* Write the specifier part of the type. */ + end_ptr_first_scan = + demangle_type_first_part(end_ptr, + /*under_lhs_declarator=*/FALSE, + /*need_trailing_space=*/TRUE, dctl); + } /* if */ + temp_par_info.nesting_level = 0; + if (pname != NULL && + !suppress_parent_and_local_info) { + /* Write the parent class or namespace qualifier. */ + if (temp_par_info.final_specialization != NULL) { + /* Up to the final specialization, put out actual template arguments + for specializations. */ + temp_par_info.actual_template_args_until_final_specialization = TRUE; + } /* if */ + (void)full_demangle_type_name(pname, /*base_name_only=*/FALSE, + &temp_par_info, + /*is_destructor_name=*/FALSE, + dctl); + /* Force template parameter information out on the function even if + it is specialized. */ + temp_par_info.actual_template_args_until_final_specialization = FALSE; + write_id_str("::", dctl); + } /* if */ + /* Write the name of the member. */ + (void)demangle_name(ptr, nchars, /*stop_on_underscores=*/TRUE, + (unsigned long *)NULL, + pname, &temp_par_info, + (a_boolean *)NULL, dctl); + if (oname != NULL) { + /* Put out the name of the class of the function explicitly overridden, + if noted above. */ + write_id_str(" [overriding function in ", dctl); + (void)demangle_type_name(oname, dctl); + write_id_str("] ", dctl); + } /* if */ + if (is_function) { + /* Write the declarator part of the type. */ + demangle_type_second_part(end_ptr, /*under_lhs_declarator=*/FALSE, + dctl); + end_ptr = end_ptr_first_scan; + } /* if */ + if (!temp_par_info.use_old_form_for_template_output && + temp_par_info.nesting_level != 0) { + /* Put out correspondences for template parameters, e.g, "T=int". */ + temp_par_info.nesting_level = 0; + temp_par_info.first_correspondence = TRUE; + temp_par_info.output_only_correspondences = TRUE; + /* Output is suppressed in general, and turned on only where + appropriate. */ + dctl->suppress_id_output++; + if (pname != NULL) { + /* Write the parent class or namespace qualifier. */ + if (temp_par_info.final_specialization != NULL) { + /* Up to the final specialization, put out actual template arguments + for specializations. */ + temp_par_info.actual_template_args_until_final_specialization = TRUE; + } /* if */ + (void)full_demangle_type_name(pname, /*base_name_only=*/FALSE, + &temp_par_info, + /*is_destructor_name=*/FALSE, + dctl); + } /* if */ + /* Force template parameter information out on the function even if + it is specialized. */ + temp_par_info.actual_template_args_until_final_specialization = FALSE; + /* Write the name of the member. */ + (void)demangle_name(ptr, nchars, /*stop_on_underscores=*/TRUE, + (unsigned long *)NULL, + pname, &temp_par_info, + (a_boolean *)NULL, dctl); + dctl->suppress_id_output--; + if (!temp_par_info.first_correspondence) { + /* End the list of correspondences. */ + write_id_ch(']', dctl); + } /* if */ + } /* if */ + } /* if */ +end_of_routine: + /* If the identifier had local function information, write the instance + number now. */ + if (has_function_local_info) emit_instance(instance, dctl); + /* When a function-local indication is scanned, end_ptr has been set + to the end of the local entity name, and needs to be set to after the + function-local indication at the end of the whole name. */ + if (function_local_end_ptr != NULL) end_ptr = function_local_end_ptr; + if (is_externalized_static) { + /* Advance over the module id part of the name. */ + while (get_char(end_ptr, dctl) != '\0') end_ptr++; + } /* if */ + if (prev_end != NULL) dctl->end_of_name = prev_end; + return end_ptr; +} /* full_demangle_identifier */ + + +static a_boolean is_mangled_type_name(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Return TRUE if the encoding beginning at ptr appears to be a mangled +type name. This is used to distinguish a local mangled non-nested +type name with template arguments (e.g., __15MyTemp__tm__2_i) from a +cfront-style local name (e.g., __2name); the character passed in is +the one after the double underscore. +*/ +{ + a_boolean is_type_name = FALSE; + a_const_char *p = ptr; + + if (isdigit((unsigned char)get_char(p, dctl))) { + /* Skip over the number. */ + do { p++; } while (isdigit((unsigned char)get_char(p, dctl))); + /* The next character is typically alphabetic. */ + if (isalpha((unsigned char)get_char(p, dctl))) { + /* This doesn't have to be a full recognizer; it just has to distinguish + the two cases given above. To do that, look for the double underscore + that must appear in a mangled name that has template arguments. */ + for (p++; get_char(p, dctl) != '\0'; p++) { + if (get_char(p, dctl) == '_' && get_char(p+1, dctl) == '_') { + is_type_name = TRUE; + break; + } /* if */ + } /* for */ + } else if (get_char(p, dctl) == '_' && + get_char(p+1, dctl) == '_' && + get_char(p+2, dctl) == 'U' && + (get_char(p+3, dctl) == 't' || + get_char(p+3, dctl) == 'l' || + get_char(p+3, dctl) == 'm' || + get_char(p+3, dctl) == 'd')) { + /* An unnamed type or lambda. */ + is_type_name = TRUE; + } /* if */ + } /* if */ + return is_type_name; +} /* is_mangled_type_name */ + + +static a_const_char *demangle_static_variable_name( + a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle the name of a static variable promoted to being external by +addition of a prefix "__STV__" and a suffix of a module id. Just put out +the part in the middle, which is the original name. +*/ +{ + a_const_char *start_ptr; + + ptr += 7; /* Move to after "__STV__". */ + /* Copy the name until "__". */ + start_ptr = ptr; + while (get_char(ptr, dctl) != '_' || + get_char(ptr+1, dctl) != '_' || + ptr == start_ptr) { + if (get_char(ptr, dctl) == '\0') { + bad_mangled_name(dctl); + break; + } /* if */ + write_id_ch(*ptr, dctl); + ptr++; + } /* while */ + /* Advance over the module id part of the name. */ + while (get_char(ptr, dctl) != '\0') ptr++; + return ptr; +} /* demangle_static_variable_name */ + + +static a_const_char *demangle_local_name(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle the local name at ptr and output the demangled form. Return +a pointer to the character position following what was demangled. +This demangles the "__nn_mm_name" form produced by the C-generating +back end. This is not something visible unless the C-generating back end +is used, and it's a local name, which is ordinarily outside the charter +of these demangling routines, but it's an easy and common case, so... + +Also handles the cfront-style __nnName form. +*/ +{ + a_const_char *p = ptr+2; + + /* Check for the initial two numbers and underscores. The caller checked + for the two initial underscores and the digit following that. */ + do { p++; } while (isdigit((unsigned char)get_char(p, dctl))); + if (isalpha((unsigned char)get_char(p, dctl))) { + /* Cfront-style local name, like "__2name". */ + } else { + if (get_char(p, dctl) != '_') { + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + p++; + if (!isdigit((unsigned char)get_char(p, dctl))) { + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + do { p++; } while (isdigit((unsigned char)get_char(p, dctl))); + if (get_char(p, dctl) != '_') { + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + p++; + } /* if */ + /* Copy the rest of the string to output. */ + while (get_char(p, dctl) != '\0') { + write_id_ch(*p, dctl); + p++; + } /* while */ +end_of_routine: + return p; +} /* demangle_local_name */ + + +static a_const_char *uncompress_mangled_name(a_const_char *id, + a_decode_control_block_ptr dctl) +/* +Uncompress the compressed mangled name beginning at id. Return the +address of the uncompressed name. +*/ +{ + a_const_char *uncompressed_name = id, *src_end = dctl->end_of_name; + unsigned long length; + + /* Advance past "__CPR". */ + id += 5; + /* Accumulate the length of the uncompressed name. */ + if (!isdigit((unsigned char)*id)) { + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + id = get_number(id, &length, dctl); + /* Check for the two underscores following the length. */ + if (id[0] != '_' || id[1] != '_') { + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + /* Save the uncompressed length so it can be used later in telling the + caller how big a buffer is required. */ + dctl->uncompressed_length = length; + id += 2; + if (length+1 >= dctl->output_id_size) { + /* The buffer supplied by the caller is too small to contain the + uncompressed name. */ + dctl->output_overflow_err = TRUE; + goto end_of_routine; + } else { + a_const_char *src, *dst_end = dctl->output_id+dctl->output_id_size; + char *dst; + /* Uncompress to the end of the buffer supplied by the caller, then + do the demangling in the space remaining at the beginning. */ + uncompressed_name = dst_end-(length+1); + dctl->output_id_size -= length+1; + dst = (char *)uncompressed_name; + for (src = id; *src != '\0';) { + char ch = *src++; + if (ch != 'J') { + /* Just copy this character. */ + if (dst >= dst_end) { + /* Overflowed buffer (probably malformed input). */ + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + *dst++ = ch; + } else { + if (*src == 'J') { + /* "JJ" indicates a simple "J". */ + /* Simple "J". */ + if (dst >= dst_end) { + /* Overflowed buffer (probably malformed input). */ + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + *dst++ = 'J'; + } else { + /* "JnnnJ" indicates a repetition of a string that appeared + earlier, at position "nnn". */ + unsigned long pos, prev_len; + a_const_char *prev_str, *prev_str2, *prev_end; + dctl->end_of_name = src_end; + src = get_number(src, &pos, dctl); + if (*src != 'J' || pos > length) { + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + prev_str = uncompressed_name+pos; + if (!isdigit(*prev_str)) { + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + /* Get the length of the repeated string. */ + dctl->end_of_name = uncompressed_name + length; + prev_str2 = get_length(prev_str, &prev_len, &prev_end, dctl); + /* Copy the repeated string to the uncompressed output. */ + prev_str2 += prev_len; + if (dst+prev_len >= dst_end) { + /* Overflowed buffer (probably malformed input). */ + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + while (prev_str < prev_str2) *dst++ = *prev_str++; + } /* if */ + /* Advance past the final "J". */ + src++; + } /* if */ + } /* for */ + if (dst - uncompressed_name != length) { + /* The length didn't come out right. */ + bad_mangled_name(dctl); + } /* if */ + if (dst >= dst_end) { + /* Overflowed buffer (probably malformed input). */ + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + /* Add the final null. */ + *dst++ = '\0'; + dctl->end_of_name = uncompressed_name + length; + } /* if */ +end_of_routine:; + return uncompressed_name; +} /* uncompress_mangled_name */ + + +void decode_identifier(a_const_char *id, + char *output_buffer, + sizeof_t output_buffer_size, + a_boolean *err, + a_boolean *buffer_overflow_err, + sizeof_t *required_buffer_size) +/* +Demangle the identifier id (which is null-terminated), and put the demangled +form (null-terminated) into the output_buffer provided by the caller. +output_buffer_size gives the allocated size of output_buffer. If there +is some error in the demangling process, *err will be returned TRUE. +In addition, if the error is that the output buffer is too small, +*buffer_overflow_err will (also) be returned TRUE, and *required_buffer_size +is set to the size of buffer required to do the demangling. Note that +if the mangled name is compressed, and the buffer size is smaller than +the size of the uncompressed mangled name, the size returned will be +enough to uncompress the name but not enough to produce the demangled form. +The caller must be prepared in that case to loop a second time (the +length returned the second time will be correct). +*/ +{ + a_const_char *end_ptr, *p; + a_decode_control_block control_block; + a_decode_control_block_ptr dctl = &control_block; + + clear_control_block(dctl); + dctl->end_of_name = strchr(id, '\0'); + dctl->output_id = output_buffer; + dctl->output_id_size = output_buffer_size; + if (start_of_id_is("__CPR", id, dctl)) { + /* Uncompress a compressed name. */ + id = uncompress_mangled_name(id, dctl); + } /* if */ + /* Check for special cases. */ + if (dctl->output_overflow_err) { + /* Previous error (not enough room in the buffer to uncompress). */ + } else if (dctl->err_in_id) { + /* Invalid compressed input. */ + } else if (start_of_id_is("__vtbl__", id, dctl)) { + write_id_str("virtual function table for ", dctl); + /* The overall mangled name is one of + __vtbl__ + __vtbl__ __ + __vtbl__ __ + __ + */ + end_ptr = demangle_vtbl_class_name(id+8, dctl); + while (start_of_id_is("__", end_ptr, dctl)) { + /* Further derived class. */ + end_ptr += 2; + write_id_str(" in ", dctl); + end_ptr = demangle_vtbl_class_name(end_ptr, dctl); + } /* while */ + } else if (start_of_id_is("__CBI__", id, dctl)) { + write_id_str("can-be-instantiated flag for ", dctl); + end_ptr = demangle_identifier(id+7, dctl); + } else if (start_of_id_is("__DNI__", id, dctl)) { + write_id_str("do-not-instantiate flag for ", dctl); + end_ptr = demangle_identifier(id+7, dctl); + } else if (start_of_id_is("__TIR__", id, dctl)) { + write_id_str("template-instantiation-request flag for ", dctl); + end_ptr = demangle_identifier(id+7, dctl); + } else if (start_of_id_is("__LSG__", id, dctl)) { + write_id_str("initialization guard variable for ", dctl); + end_ptr = demangle_identifier(id+7, dctl); + } else if (start_of_id_is("__THI__", id, dctl)) { + write_id_str("thread_local initialization routine for ", dctl); + end_ptr = demangle_identifier(id+7, dctl); + } else if (start_of_id_is("__IFV__", id, dctl)) { + id += 7; + write_id_str("ifunc variable for ", dctl); + if (start_of_id_is("__IFC__", id, dctl)) { + /* This can be nested (and this routine isn't recursive). */ + write_id_str("ifunc function for ", dctl); + id += 7; + } /* if */ + end_ptr = demangle_identifier(id, dctl); + } else if (start_of_id_is("__RES__", id, dctl)) { + id += 7; + write_id_str("resolver function for ", dctl); + if (start_of_id_is("__IFC__", id, dctl)) { + /* This can be nested (and this routine isn't recursive). */ + write_id_str("ifunc function for ", dctl); + id += 7; + } /* if */ + end_ptr = demangle_identifier(id, dctl); + } else if (start_of_id_is("__IFC__", id, dctl)) { + write_id_str("ifunc function for ", dctl); + end_ptr = demangle_identifier(id+7, dctl); + } else if (start_of_id_is("__TGT__", id, dctl)) { + /* A "target" attribute: look for closing "__". */ + a_const_char *target_attr, *target_attr_end; + id += 7; + target_attr = id; + while (id < dctl->end_of_name) { + if (start_of_id_is("__", id, dctl)) { + target_attr_end = id; + id += 2; + break; + } /* if */ + id++; + } /* while */ + if (id < dctl->end_of_name) { + end_ptr = demangle_identifier(id, dctl); + /* Emit the "target" attribute. Note that this isn't exactly in the + same format as the input (commas and periods have been replaced by + underscores), but it should convey the idea. */ + write_id_str(" __attribute__((target(", dctl); + while (target_attr < target_attr_end) { + write_id_ch(*target_attr++, dctl); + } /* if */ + write_id_str(")))", dctl); + } else { + bad_mangled_name(dctl); + end_ptr = id; + } /* if */ + } else if (start_of_id_is("__TWR__", id, dctl)) { + write_id_str("thread_local wrapper for ", dctl); + end_ptr = demangle_identifier(id+7, dctl); + } else if (start_of_id_is("__TID_", id, dctl)) { + write_id_str("type identifier for ", dctl); + end_ptr = demangle_type(id+6, dctl); + } else if (start_of_id_is("__T_", id, dctl)) { + write_id_str("typeinfo for ", dctl); + end_ptr = demangle_type(id+4, dctl); + } else if (start_of_id_is("__VFE__", id, dctl)) { + write_id_str("surrogate in class ", dctl); + p = demangle_type(id+7, dctl); + if (get_char(p, dctl) != '_' || get_char(p+1, dctl) != '_') { + bad_mangled_name(dctl); + end_ptr = p; + } else { + write_id_str(" for ", dctl); + end_ptr = demangle_identifier(p+2, dctl); + } /* if */ + } else if (start_of_id_is("__Q", id, dctl) || + (start_of_id_is("__", id, dctl) && + is_mangled_type_name(id+2, dctl))) { + /* Mangled type name. */ + end_ptr = demangle_type_name(id+2, dctl); + } else if (start_of_id_is("__STV__", id, dctl)) { + /* Static variable made external by addition of prefix "__STV__" and + suffix of module id. */ + end_ptr = demangle_static_variable_name(id, dctl); + } else if (start_of_id_is("__", id, dctl) && isdigit((unsigned char)id[2])) { + /* Local variable mangled by the C-generating back end: __nn_mm_name, + where "nn" and "mm" are decimal integers. */ + end_ptr = demangle_local_name(id, dctl); + } else { + /* Normal case: function name, static data member name, or + name of type or variable promoted out of function. */ + end_ptr = demangle_identifier(id, dctl); + } /* if */ + if (dctl->output_overflow_err) { + dctl->err_in_id = TRUE; + } else { + /* Add a terminating null. */ + dctl->output_id[dctl->output_id_len] = 0; + } /* if */ + /* Make sure the whole identifier was taken. */ + if (!dctl->err_in_id && *end_ptr != '\0') bad_mangled_name(dctl); + *err = dctl->err_in_id; + *buffer_overflow_err = dctl->output_overflow_err; + *required_buffer_size = dctl->output_id_len + 1; /* +1 for final null. */ + /* If the name is compressed, we need room for the uncompressed + form, and a null, in the buffer. */ + if (dctl->uncompressed_length != 0) { + *required_buffer_size += dctl->uncompressed_length+1; + } /* if */ +} /* decode_identifier */ + +#else /* IA64_ABI */ + +/* +Start of demangling code for IA-64 ABI. +*/ + +/* +TRUE if the bugs in the g++ 3.2 implementation of the IA-64 ABI should +be emulated. Can be changed by a command line option. +*/ +a_boolean emulate_gnu_abi_bugs = DEFAULT_EMULATE_GNU_ABI_BUGS; + +/* +TRUE if the host integer representation is little-endian. +External because it's declared extern in host_envir.h. +*/ +a_boolean host_little_endian; + +/* +Bits used to represent cv-qualifiers in a bit set. +*/ +typedef int a_cv_qualifier_set; +#define CVQ_NONE ((a_cv_qualifier_set)0) +#define CVQ_CONST ((a_cv_qualifier_set)0x1) +#define CVQ_VOLATILE ((a_cv_qualifier_set)0x2) +#define CVQ_RESTRICT ((a_cv_qualifier_set)0x4) + +/* +Values used to represent an optional ref-qualifier. +*/ +typedef int a_ref_qualifier; +#define REFQ_NONE ((a_ref_qualifier)0) +#define REFQ_LVALUE ((a_ref_qualifier)0x1) +#define REFQ_RVALUE ((a_ref_qualifier)0x2) + + +/* +Information about a function that has to be preserved from the +time of scanning of the name (e.g., in a ) until later use +in processing the . +*/ +typedef struct a_func_block { + a_boolean no_return_type; + /* TRUE if the function is one that will not have + a return type encoded in the function type. */ + a_cv_qualifier_set + cv_quals; + /* If the function is a cv-qualified member function, + the set of cv-qualifiers. 0 otherwise. */ + a_ref_qualifier + ref_qual; + /* If the function is a ref-qualified member function, + the ref-qualifier. 0 otherwise. */ + char ctor_dtor_kind; + /* If the function is a constructor or destructor, + the character from the mangled name identifying its + kind, e.g., '2' for a subobject constructor/ + destructor. ' ' if the function is not a + constructor or destructor. */ +} a_func_block; + + +/* +Information about an entity in the mangled name that may be reused +by referring back to it by number as a "substitution". +*/ +/* Code for type of syntactic object substituted for: */ +typedef enum a_substitution_kind { + subk_unscoped_template_name, + /* An . */ + subk_prefix, /* A . */ + subk_template_prefix, /* A . */ + subk_type, /* A . */ + subk_template_template_param + /* A . */ +} a_substitution_kind; + +typedef struct a_substitution { + a_const_char *start; /* First character of the encoding of the entity. */ + a_substitution_kind + kind; /* Kind of entity. */ + unsigned long num_levels; + /* For subk_prefix and subk_template_prefix, the + number of levels of the prefix included. That is, + is the substitution A:: or A::B:: or A::B::C::. + For the subk_template_prefix case, the count + is the number of complete levels (name plus + optional template argument list) that precede + the final name (and no template argument list) + that is part of the substitution. (Therefore, + the count could be zero.) */ + a_boolean parse_template_args; + /* The value of the parse_template_args boolean + at the time the subk_type substitution is recorded + (so that value can be used on subsequent + demanglings of that substitution string). Ignored + for other substitution kinds. */ +} a_substitution; + +static a_substitution + *substitutions = NULL; + /* A dynamically allocated array. substitutions[n] + gives the meaning of the substitution numbered + "n". */ +static unsigned long + num_substitutions = 0; + /* The number of substitutions currently defined, i.e., + the number of elements of the array that have + been set. */ +static unsigned long + allocated_substitutions = 0; + /* The allocated size of the array, as a number of + elements. */ + +static char *ud_suffix_buffer = NULL; + /* A dynamically allocated buffer into which + ud-suffixes are copied (when demangling literal + operators). */ +static unsigned long + ud_suffix_buffer_length = 0; + /* The allocated length of ud_suffix_buffer. */ + +static a_const_char *demangle_type_first_part( + a_const_char *ptr, + a_cv_qualifier_set cv_quals, + a_boolean under_lhs_declarator, + a_boolean need_trailing_space, + a_boolean parse_template_args, + a_decode_control_block_ptr dctl); +static void demangle_type_second_part( + a_const_char *ptr, + a_cv_qualifier_set cv_quals, + a_boolean under_lhs_declarator, + a_decode_control_block_ptr dctl); +static a_const_char *full_demangle_type( + a_const_char *ptr, + a_boolean parse_template_args, + a_boolean is_pack_expansion, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_simple_id(a_const_char *ptr, + a_decode_control_block_ptr dctl); +/* +Macro to invoke full_demangle_type in the usual case where parse_template_args +is TRUE and is_pack_expansion is FALSE. +*/ +#define demangle_type(ptr, dctl) \ + full_demangle_type(ptr, /*parse_template_args=*/TRUE, \ + /*is_pack_expansion=*/FALSE, dctl) + +static a_const_char *demangle_template_args(a_const_char *ptr, + a_decode_control_block_ptr dctl); + +/* +Bit mask used to determine which portion(s) of a should +be emitted by demangle_name. For most cases, DNO_ALL is correct, but in +cases where a is scanned more than once, different portions of the +name may be emitted on different passes. +*/ +typedef int a_demangle_name_option; +#define DNO_NONE ((a_demangle_name_option)0) +#define DNO_EXTERNALIZATION \ + ((a_demangle_name_option)0x1) +#define DNO_NAME ((a_demangle_name_option)0x2) +#define DNO_ALL (DNO_EXTERNALIZATION | DNO_NAME) + +static a_const_char *demangle_name(a_const_char *ptr, + a_func_block *func_block, + a_demangle_name_option options, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_unresolved_name(a_const_char *ptr, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_expression(a_const_char *ptr, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_encoding( + a_const_char *ptr, + a_boolean include_func_params, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_nested_name_components( + a_const_char *ptr, + unsigned long num_levels, + a_boolean *is_no_return_name, + a_boolean *has_templ_arg_list, + char *ctor_dtor_kind, + a_const_char **last_component_name, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_unscoped_name( + a_const_char *ptr, + a_func_block *func_block, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_unqualified_name( + a_const_char *ptr, + a_boolean *is_no_return_name, + a_decode_control_block_ptr dctl); +static a_const_char *demangle_template_param(a_const_char *ptr, + a_decode_control_block_ptr dctl); +static void output_cv_qualifiers(a_cv_qualifier_set cv_quals, + a_boolean trailing_space, + a_decode_control_block_ptr dctl); + + +static void clear_func_block(a_func_block *func_block) +/* +Clear a function information block to default values. +*/ +{ + func_block->no_return_type = FALSE; + func_block->cv_quals = 0; + func_block->ref_qual = REFQ_NONE; + func_block->ctor_dtor_kind = ' '; +} /* clear_func_block */ + + +static a_const_char *get_number(a_const_char *p, + long *num, + a_decode_control_block_ptr dctl) +/* +Accumulate a number starting at position p and return its value in *num. +Return a pointer to the character position following the number. +A negative number is indicated by a leading "n". +*/ +{ + long n = 0; + a_boolean negative = FALSE; + + if (*p == 'n') { + negative = TRUE; + p++; + } /* if */ + if (!isdigit((unsigned char)*p)) { + bad_mangled_name(dctl); + } else { + do { + n = n*10 + (*p - '0'); + p++; + } while (isdigit((unsigned char)*p)); + } /* if */ + if (negative) n = -n; + *num = n; + return p; +} /* get_number */ + + +static void record_substitutable_entity( + a_const_char *start, + a_substitution_kind kind, + unsigned long num_levels, + a_boolean parse_template_args, + a_decode_control_block_ptr dctl) +/* +Record the entity whose mangled name starts at "start", and whose +kind (of syntax term) is given by "kind", as a potentially +substitutable entity, one that can be used again by referencing +it in a later substitution. num_levels gives added information +for the subk_prefix and subk_template_prefix cases. +For subk_type cases, the value of parse_template_args is stored with +the substitution information (so that it can be used when demangling +the type when it is used as a substitution). +*/ +{ + /* Do not record anything if we are suppressing recording of substitutions. + One case in which that is true is when an error has been detected. */ + if (!dctl->suppress_substitution_recording) { + unsigned long number = num_substitutions++; + a_substitution *subp; + if (num_substitutions > allocated_substitutions) { + /* Need to allocate or extend the substitutions array. */ + true_size_t new_size; + allocated_substitutions += 500; + new_size = allocated_substitutions*sizeof(a_substitution); + if (substitutions == NULL) { + substitutions = (a_substitution*)malloc(new_size); + } else { + substitutions = (a_substitution*)realloc(substitutions, new_size); + } /* if */ + if (substitutions == NULL) { + bad_mangled_name(dctl); + return; + } /* if */ + } /* if */ + subp = &substitutions[number]; + subp->start = start; + subp->kind = kind; + subp->num_levels = num_levels; + subp->parse_template_args = parse_template_args; + } /* if */ +} /* record_substitutable_entity */ + + +static a_const_char *demangle_substitution( + a_const_char *ptr, + int type_pass_num, + a_cv_qualifier_set cv_quals, + a_boolean under_lhs_declarator, + a_boolean need_trailing_space, + a_const_char **last_component_name, + a_const_char **substitution, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 and output the demangled form. +Return a pointer to the character position following what was demangled. +A repeats a construct previously encoded in the +mangled name by referring to it by number, as a way to reduce the +size of mangled names. There are also some predefined substitutions, +for entities from the standard library that are likely to come up +often. The syntax is: + + ::= S _ + ::= S_ + ::= St # ::std:: + ::= Sa # ::std::allocator + ::= Sb # ::std::basic_string + ::= Ss # ::std::basic_string < char, + ::std::char_traits, + ::std::allocator > + ::= Si # ::std::basic_istream > + ::= So # ::std::basic_ostream > + ::= Sd # ::std::basic_iostream > + +When the substitution is a type, type_pass_num indicates whether to +do the first-part (1) or second-part (2) processing. type_pass_num == 0 +means do both parts, i.e., the whole type. cv_quals, +under_lhs_declarator, and need_trailing_space give extra information +to be passed through to the type demangling routines in that case. If +last_component_name is non-NULL, and the substitution decoded is a +prefix of a nested name, a pointer to the encoding for the last +component of the nested name is returned in *last_component_name. It +will not be a substitution. This is needed for generating the names +of constructors and destructors. If substitution is non-NULL, *substitution +is set to the point in the mangled name where the substitution source +occurs (in case the caller needs to examine it -- for example to see what +type the substitution represents). +*/ +{ + char ch2 = ptr[1]; + + if (last_component_name != NULL) *last_component_name = NULL; + if (substitution != NULL) *substitution = NULL; + if (islower((unsigned char)ch2)) { + /* Predefined substitution. */ + a_const_char *str = ""; + a_const_char *last_name = ""; + if (ch2 == 't') { + str = "std"; + last_name = "3std"; + } else if (ch2 == 'a') { + str = "std::allocator"; + last_name = "9allocator"; + } else if (ch2 == 'b') { + str = "std::basic_string"; + last_name = "12basic_string"; + } else if (ch2 == 's') { + str = + "std::basic_string, std::allocator>"; + last_name = "12basic_string"; + } else if (ch2 == 'i') { + str = "std::basic_istream>"; + last_name = "13basic_istream"; + } else if (ch2 == 'o') { + str = "std::basic_ostream>"; + last_name = "13basic_ostream"; + } else if (ch2 == 'd') { + str = "std::basic_iostream>"; + last_name = "14basic_iostream"; + } /* if */ + /* Output nothing if we want only the second-pass output. */ + if (type_pass_num != 2) { + output_cv_qualifiers(cv_quals, TRUE, dctl); + write_id_str(str, dctl); + } /* if */ + ptr += 2; + if (last_component_name != NULL) *last_component_name = last_name; + } else { + /* Not a predefined substitution. Convert the base-36 sequence number. */ + uint32_t number = 0; + a_substitution *subp; + a_const_char *p; + ptr++; + if (ch2 != '_') { + do { + static a_const_char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + number *= 36; + if (*ptr == '\0') { + bad_mangled_name(dctl); + break; + } /* if */ + p = strchr(digits, *ptr); + if (p == NULL) { + bad_mangled_name(dctl); + break; + } /* if */ + number += (uint32_t)(p - digits); + ptr++; + } while (*ptr != '_'); + number++; + } /* if */ + if (number >= num_substitutions) { + bad_mangled_name(dctl); + } else { + a_func_block func_block; + ptr = advance_past_underscore(ptr, dctl); + subp = &substitutions[number]; + p = subp->start; + if (substitution != NULL) *substitution = p; + /* Rescan the encoding for the entity, outputting the demangled form + again at the current output position. Don't record substitutions + when rescanning. */ + dctl->suppress_substitution_recording++; + if (type_pass_num == 2 && subp->kind != subk_type) { + /* If we're expecting a type, and we want the second-pass + processing, and we get something other than a type, ignore it. + It's a type specifier, and we don't put those out on the + second pass. */ + } else { + switch (subp->kind) { + case subk_unscoped_template_name: + if (type_pass_num == 1 || type_pass_num == 0) { + /* Emit any cv-qualifiers that are applicable to this + substitution. */ + output_cv_qualifiers(cv_quals, TRUE, dctl); + } /* if */ + (void)demangle_unscoped_name(p, &func_block, dctl); + break; + case subk_prefix: + case subk_template_prefix: + { a_boolean is_no_return_name, has_templ_arg_list; + char ctor_dtor_kind; + output_cv_qualifiers(cv_quals, TRUE, dctl); + /* Take the right number of levels of the name. Note that a + substitution counts as one level even if it represents + several. */ + if (subp->num_levels > 0) { + p = demangle_nested_name_components(p, + subp->num_levels, + &is_no_return_name, + &has_templ_arg_list, + &ctor_dtor_kind, + last_component_name, + dctl); + } /* if */ + if (subp->kind == subk_template_prefix) { + /* For the template prefix case, take one more + . */ + if (subp->num_levels > 0) write_id_str("::", dctl); + p = demangle_unqualified_name(p, &is_no_return_name, dctl); + } /* if */ + } + break; + case subk_type: + if (type_pass_num == 1 || type_pass_num == 0) { + /* Make sure to use the value of parse_template_args that + was in effect when the type substitution was first + recorded. */ + (void)demangle_type_first_part(p, cv_quals, + under_lhs_declarator, + need_trailing_space, + subp->parse_template_args, + dctl); + } /* if */ + if (type_pass_num == 2 || type_pass_num == 0) { + demangle_type_second_part(p, cv_quals, + under_lhs_declarator, dctl); + } /* if */ + break; + case subk_template_template_param: + (void)demangle_template_param(p, dctl); + break; + default: + bad_mangled_name(dctl); + } /* switch */ + } /* if */ + dctl->suppress_substitution_recording--; + } /* if */ + } /* if */ + return ptr; +} /* demangle_substitution */ + + +/* +Bit mask used to determine which portion(s) of a should +be emitted. The contains an optional return type +as well as one or more parameter types. +*/ +typedef int a_bare_function_type_option; +#define BFT_NONE ((a_bare_function_type_option)0) +#define BFT_RETURN ((a_bare_function_type_option)0x1) +#define BFT_PARAMS ((a_bare_function_type_option)0x2) + + +static a_const_char *demangle_bare_function_type( + a_const_char *ptr, + a_boolean no_return_type, + a_bare_function_type_option options, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 and output selected pieces of the +demangled form. Return a pointer to the character position following what was +demangled. A encodes the return and parameter types of a +function type without the surrounding F/E delimiters. It is used in cases +where the only possibility is a function type, e.g., in a top-level encoding +for a function. The syntax is: + + ::= + + # types are possible return type, then parameter types + +That is, the is one or more type encodings. +no_return_type is TRUE if the return type is not present in the mangled +encoding. The pieces of the that are emitted are +controlled by the options bit mask; when BFT_RETURN is specified the return +type (and a space character) is emitted, when BFT_PARAMS is specified the +parameter types (with surrounding "( )") are emitted. In all cases, the +returned value reflects the end position of (regardless of +what portion(s) of it were emitted). +*/ +{ +/* Stop on a (possibly ref-qualified) "E" or end of the input. */ +#define end_of_param_list(p) (*(p) == 'E' || *(p) == '\0' || \ + ((*(p) == 'R' || *(p) == 'O') && \ + *((p)+1) == 'E')) + + /* Handle the return type first. */ + if ((options & BFT_RETURN) == 0) dctl->suppress_id_output++; + if (!no_return_type) { + /* A return type is present and must be scanned. */ + ptr = demangle_type(ptr, dctl); + write_id_ch(' ', dctl); + } /* if */ + if ((options & BFT_RETURN) == 0) dctl->suppress_id_output--; + /* The remaining portion is the parameter type(s). */ + if ((options & BFT_PARAMS) == 0) dctl->suppress_id_output++; + write_id_ch('(', dctl); + if (end_of_param_list(ptr)) { + /* Error, there are no parameter types (there's supposed to be at + least a "v" for a void parameter list). This is likely caused + by absence of a return type when one is expected. */ + bad_mangled_name(dctl); + } else if (*ptr == 'v' && end_of_param_list(ptr+1)) { + /* An empty parameter list is encoded as a single void type. + Put out just "()" for that case. */ + ptr++; + } else { + for (;;) { + if (*ptr == 'z') { + /* Encoding for ellipsis. */ + write_id_str("...", dctl); + ptr++; + if (!end_of_param_list(ptr)) { + bad_mangled_name(dctl); + break; + } /* if */ + } else { + /* Normal type, not an ellipsis. */ + ptr = demangle_type(ptr, dctl); + } /* if */ + /* Stop on a (possibly ref-qualified) "E" or at the end of the input. */ + if (end_of_param_list(ptr)) break; + /* Stop on an error. */ + if (dctl->err_in_id) break; + /* Continuing, so we need a comma between parameter types. */ + write_id_str(", ", dctl); + } /* if */ + } /* if */ + write_id_ch(')', dctl); + if ((options & BFT_PARAMS) == 0) dctl->suppress_id_output--; + return ptr; +#undef end_of_param_list +} /* demangle_bare_function_type */ + + +static a_const_char *get_cv_qualifiers(a_const_char *ptr, + a_cv_qualifier_set *cv_quals) +/* +Advance over any cv-qualifiers (const/volatile) at the indicated location +and return in *cv_quals a bit set indicating the qualifiers encountered. +Return a pointer to the character position following what was demangled. +Note that the IA-64 ABI defines a general-purpose "vendor extended type +qualifier" that is not implemented (as yet). Certain vendor extended type +qualifiers are however used by the front end to represent C++/CLI declarators; +those are handled in the declarator processing rather than here +(order-insensitive vendor extended type qualifiers should be handled here, +but currently the front end doesn't use any). +*/ +{ + *cv_quals = 0; + for (;; ptr++) { + if (*ptr == 'K') { + *cv_quals |= CVQ_CONST; + } else if (*ptr == 'V') { + *cv_quals |= CVQ_VOLATILE; + } else if (*ptr == 'r') { + *cv_quals |= CVQ_RESTRICT; + } else { + break; + } /* if */ + } /* for */ + return ptr; +} /* get_cv_qualifiers */ + + +static a_const_char *get_ref_qualifier(a_const_char *ptr, + a_ref_qualifier *ref_qual) +/* +Advance over a ref-qualifier (lvalue/rvalue) at the indicated location +and return in *ref_qual a value indicating the ref-qualifier encountered. +Return a pointer to the character position following what was demangled. +*/ +{ + *ref_qual = REFQ_NONE; + if (*ptr == 'R') { + *ref_qual = REFQ_LVALUE; + ptr++; + } else if (*ptr == 'O') { + *ref_qual = REFQ_RVALUE; + ptr++; + } /* if */ + return ptr; +} /* get_ref_qualifier */ + + +static a_boolean is_vendor_extended_declarator(a_const_char *ptr) +/* +Returns TRUE if the location pointed to by ptr contains an EDG-specific vendor +extended type qualifier and the extension is being used as a declarator. +Note that these vendor extended type qualifiers are treated as order-sensitive. +*/ +{ + return (start_of_id_is("U8__handle", ptr) || + start_of_id_is("U8__trkref", ptr) || + start_of_id_is("U14__interior_ptr", ptr) || + start_of_id_is("U9__pin_ptr", ptr)); +} /* is_vendor_extended_declarator */ + + +static a_const_char *demangle_vector_size_qualifier( + a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle the GNU vector_size qualifier if it appears at the indicated +location. Return a pointer to the character position following what was +demangled. +*/ +{ + if (start_of_id_is("U8__vector", ptr)) { + ptr += 10; + write_id_str("__attribute__((vector_size(?))) ", dctl); + } /* for */ + return ptr; +} /* demangle_vector_size_qualifier */ + + +static void output_cv_qualifiers(a_cv_qualifier_set cv_quals, + a_boolean trailing_space, + a_decode_control_block_ptr dctl) +/* +Output any cv-qualifiers (const/volatile) in the bit set cv_quals. +If trailing_space is TRUE, add a space at the end if any qualifiers were +put out. +*/ +{ + a_boolean any_previous = FALSE; + + if (cv_quals & CVQ_CONST ) { + write_id_str("const", dctl); + any_previous = TRUE; + } /* if */ + if (cv_quals & CVQ_VOLATILE) { + if (any_previous) write_id_ch(' ', dctl); + write_id_str("volatile", dctl); + any_previous = TRUE; + } /* if */ + if (cv_quals & CVQ_RESTRICT) { + if (any_previous) write_id_ch(' ', dctl); + write_id_str("restrict", dctl); + any_previous = TRUE; + } /* if */ + if (any_previous && trailing_space) write_id_ch(' ', dctl); +} /* output_cv_qualifiers */ + + +static void output_ref_qualifier(a_ref_qualifier ref_qual, + a_decode_control_block_ptr dctl) +/* +Output a ref-qualifier (lvalue/rvalue) if ref_qual indicates there is one. +*/ +{ + if (ref_qual == REFQ_LVALUE) { + write_id_str("&", dctl); + } else if (ref_qual & REFQ_RVALUE) { + write_id_str("&&", dctl); + } /* if */ +} /* output_ref_qualifier */ + + +static a_const_char *demangle_template_param(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 and output the demangled form. Return +a pointer to the character position following what was demangled. +A encodes a reference to a template parameter. +The syntax is: + + ::= T_ # first template parameter + ::= T _ + +*/ +{ + long num = 1; + char buffer[50]; + + /* Advance past the "T". */ + ptr++; + if (*ptr != '_') { + ptr = get_number(ptr, &num, dctl); + if (num < 0) { + bad_mangled_name(dctl); + num = 0; + } else { + num += 2; + } /* if */ + } /* if */ + ptr = advance_past_underscore(ptr, dctl); + (void)sprintf(buffer, "T%ld", num); + write_id_str(buffer, dctl); + return ptr; +} /* demangle_template_param */ + + +static a_const_char *demangle_parameter_reference( + a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 and output the demangled form. +Function parameter placeholders are needed for late-specified return types. +Return a pointer to the character position following what was demangled. +A encodes a reference to a function parameter. +The syntax is: + + ::= fpT # "this" + ::= fp _ + # L == 0, first parameter + ::= fp + _ + # L == 0, second and later parameters + ::= fL p + _ + # L > 0, first parameter + ::= fL p + + _ + # L > 0, second and later parameters + +*/ +{ + long num = 1, level = -1; + char buffer[50]; + a_cv_qualifier_set cv_quals; + + /* Advance past the "f". */ + ptr++; + if (*ptr == 'L') { + /* Get the optional level information. */ + ptr++; + ptr = get_number(ptr, &level, dctl); + if (level < 0) { + bad_mangled_name(dctl); + goto end_of_routine; + } else { + level += 1; + } /* if */ + } /* if */ + if (*ptr != 'p') { + bad_mangled_name(dctl); + goto end_of_routine; + } /* if */ + ptr++; + if (*ptr == 'T') { + /* Implicit "this" in trailing return type. */ + ptr++; + write_id_str("this", dctl); + } else { + if (*ptr != '_' && !isdigit((unsigned char)*ptr)) { + /* Optional cv-qualifiers. */ + ptr = get_cv_qualifiers(ptr, &cv_quals); + output_cv_qualifiers(cv_quals, /*trailing_space=*/TRUE, dctl); + } /* if */ + if (*ptr != '_') { + /* Parameter number. */ + ptr = get_number(ptr, &num, dctl); + if (num < 0) { + bad_mangled_name(dctl); + goto end_of_routine; + } else { + num += 2; + } /* if */ + } /* if */ + ptr = advance_past_underscore(ptr, dctl); + write_id_str("param#", dctl); + if (level == -1) { + (void)sprintf(buffer, "%ld", num); + } else { + (void)sprintf(buffer, "%ld[up %ld level%s]", num, level, + level > 1 ? "s" : ""); + } /* if */ + write_id_str(buffer, dctl); + } /* if */ +end_of_routine: + return ptr; +} /* demangle_parameter_reference */ + + +/* Forward reference. */ +static a_const_char *demangle_source_name( + a_const_char *ptr, + a_boolean is_module_id, + a_decode_control_block_ptr dctl); + + +/* +Macro to determine if the character string pointed to by "p" is a +. s are a single lower-case letter or two +characters starting with the character "D". Exceptions to this rule are +the mangling for decltype (i.e., "DT" and "Dt") as well as the EDG extension +for typeof (i.e., "DY" and "Dy"), __underlying_type (i.e., "Du") and pack +expansions (i.e., "Dp"). The lower case letter "r" is used in +for "restrict" and is not a . +*/ +#define is_builtin_type(p) \ + ((islower((unsigned char)*(p)) && \ + *(p) != 'r') || \ + (*(p) == 'D' && \ + !((p)[1] == 'p' || (p)[1] == 'u' || \ + (p)[1] == 'T' || (p)[1] == 't' || \ + (p)[1] == 'Y' || (p)[1] == 'y'))) + +/* +Macro to determine if the type pointed to by "p" needs a substitution +recorded for it. s are not recorded with the exception of +vendor extended types which are recorded. +*/ +#define record_substitution_for_type(p) (!(is_builtin_type(p)) || *(p) == 'u') + +static a_const_char *demangle_type_specifier( + a_const_char *ptr, + a_boolean parse_template_args, + a_decode_control_block_ptr dctl) +/* +Demangle the type at ptr and output the specifier part. Return a pointer +to the character position following what was demangled. The syntax is: + + ::= + ::= + ::= + ::= + ::= Dp # pack expansion of (C++11) + ::= Dt E # decltype of an id-expression or class member + # access (C++11) + ::= DT E # decltype of an expression (C++11) + ::= Dy E # typeof(type) (EDG extension) + ::= DY E # typeof(expression) (EDG extension) + ::= Du E # __underlying_type(type) (EDG extension) + +Other parts of are handled in demangle_type_first_part and +demangle_type_second_part. In particular, substitutions are handled +at that level. cv-qualifiers have been handled by the caller. +If parse_template_args is TRUE then any in the type should be +parsed as part of the type. parse_template_args is FALSE when parsing the + of a conversion function operator-name (the are +demangled as part of the template function instead). +*/ +{ + a_const_char *p = ptr, *s; + + /* Builtin type encodings are typically lower-case (with some exceptions). + Names begin with a digit or an upper-case letter. */ + if (!is_builtin_type(p)) { + if (*p == 'T') { + /* A template parameter, possibly a template template parameter. */ + a_const_char *tstart = p; + p = demangle_template_param(p, dctl); + if (*p == 'I' && parse_template_args) { + /* A list. */ + /* Record the template template parameter as a potential + substitution. */ + record_substitutable_entity(tstart, subk_template_template_param, 0L, + /*parse_template_args=*/FALSE, dctl); + p = demangle_template_args(p, dctl); + } /* if */ + } else if (*p == 'D' && p[1] == 'p') { + /* A pack expansion. */ + p = full_demangle_type(p+2, /*parse_template_args=*/TRUE, + /*is_pack_expansion=*/TRUE, dctl); + } else if (*p == 'D' && + (p[1] == 't' || p[1] == 'T')) { + /* decltype: + Dt E # decltype of an id-expression or class member + # access + DT E # decltype of an expression */ + write_id_str("decltype(", dctl); + if (p[1] == 't') { + p = demangle_expression(p+2, dctl); + } else { + write_id_ch('(', dctl); + p = demangle_expression(p+2, dctl); + write_id_ch(')', dctl); + } /* if */ + write_id_ch(')', dctl); + p = advance_past('E', p, dctl); + } else if (*p == 'D' && + (p[1] == 'y' || p[1] == 'Y')) { + /* typeof: + This is an EDG extension to the IA-64 ABI spec to handle GNU typeof + (and GNU doesn't provide a mangling that we can follow): + + ::= Dy E # typeof(type) + ::= DY E # typeof(expression) + */ + write_id_str("typeof(", dctl); + if (p[1] == 'y') { + p = demangle_type(p+2, dctl); + } else { + p = demangle_expression(p+2, dctl); + } /* if */ + write_id_ch(')', dctl); + p = advance_past('E', p, dctl); + } else if (*p == 'D' && p[1] == 'u') { + /* __underlying_type: + This is an EDG extension to the IA-64 ABI spec to handle + __underlying_type (a helper function used to implement the C++11 + underlying_type type trait): + + ::= Du E # __underlying_type(type) + */ + write_id_str("__underlying_type(", dctl); + p = demangle_type(p+2, dctl); + write_id_ch(')', dctl); + p = advance_past('E', p, dctl); + } else { + /* , i.e., */ + a_func_block func_block; + p = demangle_name(p, &func_block, /*options=*/DNO_ALL, dctl); + } /* if */ + } else { + /* Builtin type. */ + switch (*p++) { + case 'v': + s = "void"; + break; + case 'w': + s = "wchar_t"; + break; + case 'b': + s = "bool"; + break; + case 'c': + s = "char"; + break; + case 'a': + s = "signed char"; + break; + case 'h': + s = "unsigned char"; + break; + case 's': + s = "short"; + break; + case 't': + s = "unsigned short"; + break; + case 'i': + s = "int"; + break; + case 'j': + s = "unsigned int"; + break; + case 'l': + s = "long"; + break; + case 'm': + s = "unsigned long"; + break; + case 'x': + s = "long long"; + break; + case 'y': + s = "unsigned long long"; + break; + case 'n': + s = "__int128"; + break; + case 'o': + s = "unsigned __int128"; + break; + case 'f': + s = "float"; + break; + case 'd': + s = "double"; + break; + case 'e': + /* Also __float80 in some configurations. */ + s = "long double"; + break; + case 'g': + s = "__float128"; + break; + case 'u': + /* A vendor extended type is specified as: + ::= u . */ + p = demangle_source_name(p, /*is_module_id=*/FALSE, dctl); + s = ""; + break; + case 'D': + /* Additional built-in types (too many to assign a single character + to each one). */ + switch (*p++) { + case 'a': + s = "auto"; + break; + case 'c': + s = "decltype(auto)"; + break; + case 'n': + s = "std::nullptr_t"; + break; + case 'N': + /* EDG extension for C++/CLI managed __nullptr. */ + s = "__nullptr"; + break; + case 's': + s = "char16_t"; + break; + case 'i': + s = "char32_t"; + break; + case 'v': + /* Vector type. Format is: "Dv_". Scan the type + portion twice in order to get the proper "vector_size" value + (by "multiplying" by sizeof(type) -- since the demangling + doesn't know the size of types). */ + { long num; + a_const_char *typep; + p = get_number(p, &num, dctl); + if (*p != '_') { + bad_mangled_name(dctl); + } else { + p++; + typep = p; + p = demangle_type(p, dctl); + write_id_str(" __attribute((vector_size(", dctl); + write_id_signed_number(num, dctl); + write_id_str("*sizeof(", dctl); + dctl->suppress_substitution_recording++; + (void)demangle_type(typep, dctl); + dctl->suppress_substitution_recording--; + write_id_str(")))) ", dctl); + } /* if */ + s = ""; + } + break; + default: + bad_mangled_name(dctl); + s = ""; + } /* switch */ + break; + case 'z': /* Ellipsis not handled here; + see demangle_bare_function_type. */ + default: + bad_mangled_name(dctl); + s = ""; + } /* switch */ + write_id_str(s, dctl); + } /* if */ + return p; +} /* demangle_type_specifier */ + + +static a_const_char *skip_extern_C_indication(a_const_char *ptr) +/* +ptr points to the character after the "F" of a function type. Skip over +and ignore an indication of extern "C" following the "F", if one is present. +Return a pointer to the character following the extern "C" indication. +There's no syntax for representing the extern "C" in the function type, so +just ignore it. +*/ +{ + if (*ptr == 'Y') ptr++; + return ptr; +} /* skip_extern_C_indication */ + + +static a_const_char *demangle_type_first_part( + a_const_char *ptr, + a_cv_qualifier_set cv_quals, + a_boolean under_lhs_declarator, + a_boolean need_trailing_space, + a_boolean parse_template_args, + a_decode_control_block_ptr dctl) +/* +Demangle the type at ptr and output the specifier part and the part of the +declarator that precedes the name. Return a pointer to the character +position following what was demangled. If under_lhs_declarator is TRUE, +this type is directly under a type that uses a left-side declarator, +e.g., a pointer type. (That's used to control use of parentheses around +parts of the declarator.) If need_trailing_space is TRUE, put a space +at the end of the specifiers part (needed if the declarator part is +not empty, because it contains a name or a derived type). cv_quals +indicates any previously-scanned cv-qualifiers that are to be considered +to be on top of the type. If parse_template_args is TRUE then any + in the type should be parsed as part of the type. +*/ +{ + a_const_char *p = ptr, *qualp = p, *unqualp; + char kind; + a_cv_qualifier_set local_cv_quals; + a_boolean record_substitution = TRUE; + a_boolean record_cv_qual_substitution = TRUE; + + /* Accumulate cv-qualifiers. */ + p = get_cv_qualifiers(p, &local_cv_quals); + cv_quals |= local_cv_quals; + unqualp = p; + kind = *p; + if (kind == 'S' && + /* "St" for "std::" is the beginning of a name, not a type. */ + p[1] != 't') { + /* A substitution. */ + p = demangle_substitution(p, 1, cv_quals, + under_lhs_declarator, + need_trailing_space, + (a_const_char **)NULL, + (a_const_char **)NULL, + dctl); + record_substitution = FALSE; + if (*p == 'I') { + /* A list (the substitution must be a template). */ + p = demangle_template_args(p, dctl); + record_substitution = TRUE; + } /* if */ + } else if (kind == 'P' || kind == 'R' || kind == 'O' || kind == 'C' || + (kind == 'U' && is_vendor_extended_declarator(p))) { + a_const_char *vendor_ext = NULL; + a_boolean need_space = TRUE; + /* Look for type qualifiers: + ::= + ::= P # pointer-to + ::= R # reference-to + ::= O # rvalue reference-to (C++11) + ::= C # complex pair (C 2000) + ::= U # vendor extended type qualifier + */ + p++; + if (kind == 'U') { + /* This is a vendor extended type qualifier that is being used by the + front end to encode C++/CLI pointer-like types (i.e., handles, + tracking references, interior_ptrs, and pin_ptrs). This avoids adding + EDG-specific manglings for these entities that might be used in + subsequent IA-64 ABI revisions. Note that these extensions are + treated as "order-sensitive" for the purposes of substitutions. */ + long num; + if (start_of_id_is("8__handle", p)) { + vendor_ext = "^"; + } else if (start_of_id_is("8__trkref", p)) { + vendor_ext = "%"; + } else if (start_of_id_is("14__interior_ptr", p)) { + write_id_str("interior_ptr<", dctl); + vendor_ext = ">"; + need_space = FALSE; + } else if (start_of_id_is("9__pin_ptr", p)) { + write_id_str("pin_ptr<", dctl); + vendor_ext = ">"; + need_space = FALSE; + } else { + bad_mangled_name(dctl); + } /* if */ + /* Advance past the vendor string. */ + p = get_number(p, &num, dctl); + p += num; + } /* if */ + if (kind == 'C') { + write_id_str("_Complex ", dctl); + } /* if */ + p = demangle_type_first_part(p, CVQ_NONE, /*under_lhs_declarator=*/TRUE, + need_space, parse_template_args, dctl); + if (kind == 'P') { + write_id_ch('*', dctl); + } else if (kind == 'R') { + write_id_ch('&', dctl); + } else if (kind == 'O') { + write_id_str("&&", dctl); + } else if (vendor_ext != NULL) { + write_id_str(vendor_ext, dctl); + } /* if */ + /* Output the cv-qualifiers on the pointer, if any. */ + output_cv_qualifiers(cv_quals, /*trailing_space=*/TRUE, dctl); + } else if (kind == 'M') { + /* Pointer-to-member type, M . */ + a_const_char *classp = p+1; + /* Skip over the class name. */ + /* Substitutions do get recorded on this scan. */ + dctl->suppress_id_output++; + p = demangle_type(classp, dctl); + dctl->suppress_id_output--; + p = demangle_type_first_part(p, CVQ_NONE, /*under_lhs_declarator=*/TRUE, + /*need_trailing_space=*/TRUE, + parse_template_args, dctl); + /* Output Classname::*. */ + dctl->suppress_substitution_recording++; + (void)demangle_type(classp, dctl); + dctl->suppress_substitution_recording--; + write_id_str("::*", dctl); + /* Output the cv-qualifiers on the pointer, if any. */ + output_cv_qualifiers(cv_quals, /*trailing_space=*/TRUE, dctl); + } else if (kind == 'F' || + (kind == 'D' && + (p[1] == 'o' || p[1] == 'O'))) { + a_ref_qualifier dummy; + /* Function type, F [Y] [] E + where "Y" indicates extern "C" (and is ignored here). An + looks like: [Do | DO E | Dw * E ]. The front end + doesn't implement the "Dw" mangling, so the demangling is omitted. */ + if (kind == 'D') { + /* An exception specification is present (skip it here). */ + switch (p[1]) { + case 'o': + p += 2; + break; + case 'O': + dctl->suppress_id_output++; + p = demangle_expression(p+2, dctl); + dctl->suppress_id_output--; + p = advance_past('E', p, dctl); + break; + default: + bad_mangled_name(dctl); + break; + } /* switch */ + } /* if */ + p = skip_extern_C_indication(p+1); + /* Output the return type. */ + p = demangle_type_first_part(p, CVQ_NONE, /*under_lhs_declarator=*/FALSE, + /*need_trailing_space=*/TRUE, + parse_template_args, dctl); + /* Skip over the parameter types without outputting anything. */ + /* Substitutions do get recorded on this scan. */ + p = demangle_bare_function_type(p, /*no_return_type=*/TRUE, BFT_NONE, + dctl); + /* Look for an optional ref-qualifier (and skip it in this pass). */ + p = get_ref_qualifier(p, &dummy); + p = advance_past('E', p, dctl); + /* This is a right-side declarator, so if it's under a left-side declarator + parentheses are needed. */ + if (under_lhs_declarator) write_id_ch('(', dctl); + /* Cv-qualifiers that are applied to function types are considered an + indivisible portion of the type and no substitution is made for the + unqualified type. */ + record_cv_qual_substitution = FALSE; + } else if (kind == 'A') { + /* Array type, + A _ + A [ ] _ + */ + p++; + if (!isdigit((unsigned char)*p)) { + if (*p != '_') { + /* Length is specified by an expression based on template + parameters. Ignore the expression. */ + /* Substitutions do get recorded on this scan. */ + dctl->suppress_id_output++; + p = demangle_expression(p, dctl); + dctl->suppress_id_output--; + } /* if */ + } else { + /* Normal constant number of elements. */ + /* Skip the array size. */ + while (isdigit((unsigned char)*p)) p++; + } /* if */ + p = advance_past_underscore(p, dctl); + /* Process the element type. */ + p = demangle_type_first_part(p, CVQ_NONE, /*under_lhs_declarator=*/FALSE, + /*need_trailing_space=*/TRUE, + parse_template_args, dctl); + /* This is a right-side declarator, so if it's under a left-side declarator + parentheses are needed. */ + if (under_lhs_declarator) write_id_ch('(', dctl); + } else { + /* No declarator part to process. Handle the specifier type. */ + output_cv_qualifiers(cv_quals, /*trailing_space=*/TRUE, dctl); + p = demangle_vector_size_qualifier(p, dctl); + p = demangle_type_specifier(p, parse_template_args, dctl); + if (need_trailing_space) write_id_ch(' ', dctl); + if (!record_substitution_for_type(unqualp)) { + /* Do not record a substitution for (most) s. */ + record_substitution = FALSE; + } /* if */ + } /* if */ + if (record_substitution) { + /* Record the non-cv-qualified version of the type as a potential + substitution. */ + record_substitutable_entity(unqualp, subk_type, 0L, parse_template_args, + dctl); + } /* if */ + if (qualp != unqualp && record_cv_qual_substitution) { + /* The type is cv-qualified, so record another potential substitution + for the fully-qualified type. */ + record_substitutable_entity(qualp, subk_type, 0L, parse_template_args, + dctl); + } /* if */ + return p; +} /* demangle_type_first_part */ + + +static void demangle_type_second_part( + a_const_char *ptr, + a_cv_qualifier_set cv_quals, + a_boolean under_lhs_declarator, + a_decode_control_block_ptr dctl) +/* +Demangle the type at ptr and output the part of the declarator that follows the +name. This routine does not return a pointer to the character position +following what was demangled; it is assumed that the caller will save +that from the call of demangle_type_first_part, and it saves a lot of +time if this routine can avoid scanning the specifiers again. +If under_lhs_declarator is TRUE, this type is directly under a type that +uses a left-side declarator, e.g., a pointer type. (That's used to control +use of parentheses around parts of the declarator.) cv_quals +indicates any previously-scanned cv-qualifiers that are to considered +to be on top of the type. +*/ +{ + a_const_char *p = ptr; + char kind; + a_cv_qualifier_set local_cv_quals; + + /* Accumulate cv-qualifiers. */ + p = get_cv_qualifiers(p, &local_cv_quals); + cv_quals |= local_cv_quals; + kind = *p; + if (kind == 'S' && + /* "St" for "std::" is the beginning of a name, not a type. */ + p[1] != 't') { + /* A substitution. */ + p = demangle_substitution(p, 2, cv_quals, + under_lhs_declarator, + /*need_trailing_space=*/FALSE, + (a_const_char **)NULL, + (a_const_char **)NULL, + dctl); + /* No need to scan the list if there is one -- + that was done by demangle_type_first_part. */ + } else if (kind == 'P' || kind == 'R' || kind == 'O' || kind == 'C' || + (kind == 'U' && is_vendor_extended_declarator(p))) { + /* Look for type qualifiers: + ::= + ::= P # pointer-to + ::= R # reference-to + ::= O # rvalue reference-to (C++11) + ::= C # complex pair (C 2000) + ::= U # vendor extended type qualifier + */ + p++; + if (kind == 'U') { + /* This is a vendor extended type qualifier that is being used + by the front end as a declarator; skip over it. */ + dctl->suppress_id_output++; + p = demangle_source_name(p, /*is_module_id=*/FALSE, dctl); + dctl->suppress_id_output--; + } /* if */ + demangle_type_second_part(p, CVQ_NONE, /*under_lhs_declarator=*/TRUE, + dctl); + } else if (kind == 'M') { + /* Pointer-to-member type, M . */ + /* Advance over the class name. */ + dctl->suppress_id_output++; + dctl->suppress_substitution_recording++; + p = demangle_type(p+1, dctl); + dctl->suppress_substitution_recording--; + dctl->suppress_id_output--; + demangle_type_second_part(p, CVQ_NONE, /*under_lhs_declarator=*/TRUE, + dctl); + } else if (kind == 'F' || + (kind == 'D' && + (p[1] == 'o' || p[1] == 'O' || p[1] == 'w'))) { + a_const_char *returnt, *exception_spec = NULL; + a_const_char *save_exception_expr = NULL; + a_ref_qualifier ref_qual; + /* Function type, F [Y] [] E + where "Y" indicates extern "C" (and is ignored here). An + looks like: [Do | DO E | Dw * E ]. The front end + doesn't implement the "Dw" mangling, so the demangling is omitted. */ + if (kind == 'D') { + /* An exception specification is present. */ + switch (p[1]) { + case 'o': + exception_spec = " noexcept"; + p += 2; + break; + case 'O': + /* Emit the expression later (save a pointer to it), but skip it + now. */ + save_exception_expr = p + 2; + dctl->suppress_id_output++; + p = demangle_expression(save_exception_expr, dctl); + dctl->suppress_id_output--; + p = advance_past('E', p, dctl); + break; + default: + bad_mangled_name(dctl); + break; + } /* switch */ + } /* if */ + /* This is a right-side declarator, so if it's under a left-side declarator + parentheses are needed. */ + if (under_lhs_declarator) write_id_ch(')', dctl); + p = skip_extern_C_indication(p+1); + /* Put out the parameter types (the return type is skipped and not + output). */ + returnt = p; + dctl->suppress_substitution_recording++; + p = demangle_bare_function_type(p, /*no_return_type=*/FALSE, BFT_PARAMS, + dctl); + dctl->suppress_substitution_recording--; + /* Get a ref-qualifier if there is one. */ + p = get_ref_qualifier(p, &ref_qual); + p = advance_past('E', p, dctl); + /* Put out any cv-qualifiers (member functions). */ + /* Note that such things could come up on nonmember functions in the + presence of typedefs. In such a case what we generate here will not + be valid C, but it's a reasonable representation of the mangled + type, and there's no way of getting the typedef name in there, + so let it be. */ + if (cv_quals != 0) { + write_id_ch(' ', dctl); + output_cv_qualifiers(cv_quals, /*trailing_space=*/FALSE, dctl); + } /* if */ + if (ref_qual != REFQ_NONE) { + /* Output a ref-qualifier, if any. */ + write_id_ch(' ', dctl); + output_ref_qualifier(ref_qual, dctl); + } /* if */ + /* Output the return type. */ + demangle_type_second_part(returnt, CVQ_NONE, + /*under_lhs_declarator=*/FALSE, dctl); + if (exception_spec != NULL) { + write_id_str(exception_spec, dctl); + } else if (save_exception_expr != NULL) { + write_id_str(" noexcept(", dctl); + (void)demangle_expression(save_exception_expr, dctl); + write_id_ch(')', dctl); + } /* if */ + } else if (kind == 'A') { + /* Array type, + A _ + A [ ] _ + */ + /* This is a right-side declarator, so if it's under a left-side declarator + parentheses are needed. */ + if (under_lhs_declarator) write_id_ch(')', dctl); + write_id_ch('[', dctl); + p++; + if (!isdigit((unsigned char)*p)) { + if (*p != '_') { + /* Length is specified by a constant expression based on template + parameters. */ + dctl->suppress_substitution_recording++; + p = demangle_expression(p, dctl); + dctl->suppress_substitution_recording--; + } /* if */ + } else { + /* Normal constant number of elements. */ + /* Put out the array size. */ + while (isdigit((unsigned char)*p)) write_id_ch(*p++, dctl); + } /* if */ + p = advance_past_underscore(p, dctl); + write_id_ch(']', dctl); + /* Process the element type. */ + demangle_type_second_part(p, CVQ_NONE, /*under_lhs_declarator=*/FALSE, + dctl); + } else { + /* No declarator part to process. No need to scan the specifiers type -- + it was done by demangle_type_first_part. */ + } /* if */ +} /* demangle_type_second_part */ + + +static a_const_char *full_demangle_type( + a_const_char *ptr, + a_boolean parse_template_args, + a_boolean is_pack_expansion, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 and output the demangled form. Return a pointer +to the character position following what was demangled. A encodes +a type. The syntax is: + + ::= + ::= + ::= + ::= + ::= + ::= + ::= + ::= + ::= + ::= P # pointer-to + ::= R # reference-to + ::= F [Y] E + ::= + ::= A _ + ::= A [] _ + ::= M + +If parse_template_args is TRUE then any in the type should be +parsed as part of the type. When is_pack_expansion is TRUE, emit an +indication that the type is a pack expansion. +*/ +{ + a_const_char *p; + + /* Generate the specifier part of the type. */ + p = demangle_type_first_part(ptr, CVQ_NONE, /*under_lhs_declarator=*/FALSE, + /*need_trailing_space=*/FALSE, + parse_template_args, dctl); + if (is_pack_expansion) { + /* Emit the pack expansion indication between processing the two type + parts so that function and pointer to member function types are handled + properly. */ + write_id_str("...", dctl); + } /* if */ + /* Generate the declarator part of the type. */ + demangle_type_second_part(ptr, CVQ_NONE, /*under_lhs_declarator=*/FALSE, + dctl); + return p; +} /* full_demangle_type */ + + +static a_const_char *get_operator_name( + a_const_char *ptr, + int *num_operands, + int *length, + a_const_char **close_str, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 and return the demangled form. +Return NULL if the operator is invalid. An encodes +an operator in an expression or operator function name. +*num_operands is set to the number of operands expected by the operator. +*length is set to the mangled name length (2 except for vendor extended +operators). *close_str is set to a string that closes the operator, +if necessary, e.g., "]" for subscripting; it is set to "" if not needed. +Note that for the literal operator case (i.e., for the "li" mangled operator), +the string that is returned is in a temporary buffer (and a subsequent call +with another "li" mangled name will overwrite it), so the string should +be copied quickly. +*/ +{ + a_const_char *str = NULL; + + *num_operands = 2; + *close_str = ""; + *length = 0; + if (*ptr == '\0') { + bad_mangled_name(dctl); + } else { + char ch2 = ptr[1]; + switch (*ptr) { + case 'a': + if (ch2 == 'a') { + str = "&&"; + } else if (ch2 == 'd') { + str = "&"; + *num_operands = 1; + } else if (ch2 == 'n') { + str = "&"; + } else if (ch2 == 'N') { + str = "&="; + } else if (ch2 == 'S') { + str = "="; + } else if (ch2 == 't') { + /* alignof(type) -- newer mangling form */ + str = "alignof("; + *num_operands = 0; + *close_str = ")"; + } else if (ch2 == 'w') { + /* co_await */ + str = "co_await"; + *num_operands = 1; + } else if (ch2 == 'z') { + /* alignof(expression) -- newer mangling form */ + str = "alignof("; + *close_str = ")"; + *num_operands = 1; + } /* if */ + break; + case 'c': + if (ch2 == 'c') { + str = "const_cast"; + *num_operands = 1; + } else if (ch2 == 'l') { + str = "()"; + *num_operands = 0; /* Call is variable-length. */ + } else if (ch2 == 'm') { + str = ","; + } else if (ch2 == 'o') { + str = "~"; + *num_operands = 1; + } else if (ch2 == 'v') { + str = "cast"; + *num_operands = 1; + } /* if */ + break; + case 'd': + if (ch2 == 'a') { + str = "delete[] "; + *num_operands = 1; + } else if (ch2 == 'c') { + str = "dynamic_cast"; + *num_operands = 1; + } else if (ch2 == 'e') { + str = "*"; + *num_operands = 1; + } else if (ch2 == 'l') { + str = "delete "; + *num_operands = 1; + } else if (ch2 == 's') { + str = ".*"; + } else if (ch2 == 'v') { + str = "/"; + } else if (ch2 == 'V') { + str = "/="; + } /* if */ + break; + case 'e': + if (ch2 == 'o') { + str = "^"; + } else if (ch2 == 'O') { + str = "^="; + } else if (ch2 == 'q') { + str = "=="; + } /* if */ + break; + case 'g': + if (ch2 == 'e') { + str = ">="; + } else if (ch2 == 't') { + str = ">"; + } /* if */ + break; + case 'i': + if (ch2 == 'x') { + str = "["; + *close_str = "]"; + } /* if */ + break; + case 'l': + if (ch2 == 'e') { + str = "<="; + } else if (ch2 == 'i') { + /* A literal operator is followed by a ud-suffix (with a pre-pended + length). Copy the ud-suffix into a buffer following the "" + for the literal operator (we need to concatenate '"" ' and + the ud-suffix). */ + long ud_suffix_len; + a_const_char *ud_suffix_ptr; + ud_suffix_ptr = get_number(ptr+2, &ud_suffix_len, dctl); + /* Note that a literal operator can have zero, one, or two operands, + but we can't tell which case that is here, so consume zero + operands and let the operands be parsed by the "cl" mangling + for the implicit call to the literal operator. */ + *num_operands = 0; + str = NULL; + if (!dctl->err_in_id) { + if (ud_suffix_len <= 0) { + bad_mangled_name(dctl); + } else { + if (ud_suffix_buffer == NULL) { + ud_suffix_buffer_length = 128; + ud_suffix_buffer = (char *)malloc( + (true_size_t)ud_suffix_buffer_length); + } else if (ud_suffix_len + 3 + 1 > ud_suffix_buffer_length) { + ud_suffix_buffer_length = ud_suffix_len + 3 + 1; + ud_suffix_buffer = (char *)realloc(ud_suffix_buffer, + (true_size_t)ud_suffix_buffer_length); + } /* if */ + if (ud_suffix_buffer != NULL) { + if (strlen(ud_suffix_ptr) < ud_suffix_len) { + bad_mangled_name(dctl); + } else { + strcpy(ud_suffix_buffer, "\"\""); + strncpy(&ud_suffix_buffer[2], ud_suffix_ptr, ud_suffix_len); + ud_suffix_buffer[2 + ud_suffix_len] = '\0'; + str = (a_const_char *)ud_suffix_buffer; + *length = ud_suffix_ptr + ud_suffix_len - ptr; + } /* if */ + } else { + bad_mangled_name(dctl); + } /* if */ + } /* if */ + } /* if */ + } else if (ch2 == 's') { + str = "<<"; + } else if (ch2 == 'S') { + str = "<<="; + } else if (ch2 == 't') { + str = "<"; + } /* if */ + break; + case 'm': + if (ch2 == 'i') { + str = "-"; + } else if (ch2 == 'I') { + str = "-="; + } else if (ch2 == 'l') { + str = "*"; + } else if (ch2 == 'L') { + str = "*="; + } else if (ch2 == 'm') { + str = "--"; + *num_operands = 1; + } /* if */ + break; + case 'n': + if (ch2 == 'a') { + str = "new[] "; + } else if (ch2 == 'e') { + str = "!="; + } else if (ch2 == 'g') { + str = "-"; + *num_operands = 1; + } else if (ch2 == 't') { + str = "!"; + *num_operands = 1; + } else if (ch2 == 'w') { + str = "new "; + } else if (ch2 == 'x') { + str = "noexcept("; + *close_str = ")"; + *num_operands = 1; + } /* if */ + break; + case 'o': + if (ch2 == 'o') { + str = "||"; + } else if (ch2 == 'r') { + str = "|"; + } else if (ch2 == 'R') { + str = "|="; + } /* if */ + break; + case 'p': + if (ch2 == 'l') { + str = "+"; + } else if (ch2 == 'L') { + str = "+="; + } else if (ch2 == 'm') { + str = "->*"; + } else if (ch2 == 'p') { + str = "++"; + *num_operands = 1; + } else if (ch2 == 's') { + str = "+"; + *num_operands = 1; + } else if (ch2 == 't') { + str = "->"; + } /* if */ + break; + case 'q': + if (ch2 == 'u') { + str = "?"; + *num_operands = 3; + } /* if */ + break; + case 'r': + if (ch2 == 'c') { + str = "reinterpret_cast"; + *num_operands = 1; + } else if (ch2 == 'm') { + str = "%"; + } else if (ch2 == 'M') { + str = "%="; + } else if (ch2 == 's') { + str = ">>"; + } else if (ch2 == 'S') { + str = ">>="; + } /* if */ + break; + case 's': + if (ch2 == 'c') { + str = "static_cast"; + *num_operands = 1; + } else if (ch2 == 't') { + /* sizeof(type) */ + str = "sizeof("; + *num_operands = 0; + *close_str = ")"; + } else if (ch2 == 'z') { + /* sizeof(expression) */ + str = "sizeof("; + *close_str = ")"; + *num_operands = 1; + } /* if */ + break; + case 't': + if (ch2 == 'e') { + /* typeid(expression) -- newer mangling form */ + str = "typeid("; + *close_str = ")"; + *num_operands = 1; + } else if (ch2 == 'i') { + /* typeid(type) -- newer mangling form */ + str = "typeid("; + *close_str = ")"; + *num_operands = 0; + } else if (ch2 == 'r') { + /* rethrow (no arguments) */ + str = "throw"; + *num_operands = 0; + } else if (ch2 == 'w') { + /* throw (expression) */ + str = "throw "; + *num_operands = 1; + } /* if */ + break; + case 'v': + /* Vendor extended operators. */ + if (start_of_id_is("v18alignofe", ptr)) { + /* __alignof__(expr) -- older mangling form */ + str = "__alignof__("; + *close_str = ")"; + *num_operands = 1; + *length = 11; + } else if (start_of_id_is("v17alignof", ptr)) { + /* __alignof__(type) -- older mangling form */ + str = "__alignof__("; + *close_str = ")"; + *num_operands = 0; + *length = 10; + } else if (start_of_id_is("v19__uuidofe", ptr)) { + /* __uuidof(expr) */ + str = "__uuidof("; + *close_str = ")"; + *num_operands = 1; + *length = 12; + } else if (start_of_id_is("v18__uuidof", ptr)) { + /* __uuidof(type) */ + str = "__uuidof("; + *close_str = ")"; + *num_operands = 0; + *length = 11; + } else if (start_of_id_is("v17typeide", ptr)) { + /* typeid(expr) -- older mangling form */ + str = "typeid("; + *close_str = ")"; + *num_operands = 1; + *length = 10; + } else if (start_of_id_is("v16typeid", ptr)) { + /* typeid(type) -- older mangling form */ + str = "typeid("; + *close_str = ")"; + *num_operands = 0; + *length = 9; + } else if (start_of_id_is("v19clitypeid", ptr)) { + /* C++/CLI T::typeid. */ + str = "::typeid"; + *num_operands = 0; + *length = 12; + } else if (start_of_id_is("v23min", ptr)) { + /* GNU "?" */ + str = ">?"; + *length = 6; + *num_operands = 2; + } else if (start_of_id_is("v18__real__", ptr)) { + /* __real(expr) */ + str = "__real("; + *close_str = ")"; + *length = 11; + *num_operands = 1; + } else if (start_of_id_is("v18__imag__", ptr)) { + /* __imag(expr) */ + str = "__imag("; + *close_str = ")"; + *length = 11; + *num_operands = 1; + } else if (start_of_id_is("v19clihandle", ptr)) { + /* C++/CLI handle-to */ + str = "%"; + *length = 12; + *num_operands = 1; + } else if (start_of_id_is("v112clisafe_cast", ptr)) { + /* C++/CLI safe_cast() */ + str = "safe_cast"; + *length = 16; + *num_operands = 1; + } else if (start_of_id_is("9builtin", ptr+2)) { + /* Builtin operation. Name is + vN9builtinXX + ^^-- Operation number + ^------------ Number of operands (<= 9) + */ + static char builtin_name[] = "builtin-operation-XX"; + str = builtin_name; + builtin_name[18] = ptr[10]; + builtin_name[19] = ptr[11]; + *length = 12; + *num_operands = ptr[1]-'0'; + } else if (start_of_id_is("12clisubscript", ptr+2) && + ptr[1] >= '0' && ptr[1] <= '9') { + /* C++/CLI subscript operation with variable number of operands + (<= 9). The caller handles this as a special case. */ + str = "subscript"; + *length = 16; + *num_operands = ptr[1]-'0'; + } /* if */ + break; + default: + break; + } /* switch */ + if (*length == 0) *length = 2; + } /* if */ + return str; +} /* get_operator_name */ + + +static a_const_char *demangle_source_name( + a_const_char *ptr, + a_boolean is_module_id, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 and output the demangled form. +Return a pointer to the character position following what was demangled. +A encodes an unqualified name as a length plus the +characters of the name. The syntax is: + + ::= + ::= + +If is_module_id is TRUE, the identifier is a module id string, which +begins with a (second) count that gives the length of the file name +part. Just put out the file name part (continue scanning, but do not +output the rest of the string). This is used for an EDG extension. +*/ +{ + long num; + a_boolean output_chars = TRUE; + + ptr = get_number(ptr, &num, dctl); + if (num <= 0) { + bad_mangled_name(dctl); + } else if (is_module_id) { + /* A module id name (an EDG extension), which has the form + _ _ + Only the file name part is put out. */ + ptr = demangle_module_id(ptr, (unsigned long)num, (char *)NULL, dctl); + } else if (num >= 9 && start_of_id_is("_INTERNAL", ptr)) { + /* An EDG extension to individuate certain entities so they don't + collide with similarly named (or unnamed) entities in other + translation units. */ + write_id_str("[local to ", dctl); + ptr = demangle_module_id(ptr+9, (unsigned long)num-9, ptr, dctl); + write_id_str("]", dctl); + } else { + if (num >= 11 && start_of_id_is("_GLOBAL__N_", ptr)) { + /* g++ uses names beginning with "_GLOBAL__N_" to identify unnamed + namespaces, and the EDG C++ Front End does also to be compatible + with that. */ + write_id_str("", dctl); + output_chars = FALSE; + } /* if */ + for (; num > 0; ptr++, num--) { + if (*ptr == '\0') { + /* The name string ends before enough characters have been + accumulated. */ + bad_mangled_name(dctl); + break; + } else if (!isalnum((unsigned char)*ptr) && *ptr != '_' && *ptr != '$') { + /* Invalid character in identifier. */ + /* g++ names for unnamed namespaces contain bad characters, + e.g., periods. */ + if (output_chars) { + bad_mangled_name(dctl); + break; + } /* if */ + } else if (output_chars) { + write_id_ch(*ptr, dctl); + } /* if */ + } /* for */ + } /* if */ + return ptr; +} /* demangle_source_name */ + + +static a_const_char *get_instance_number(a_const_char *p, + unsigned long *instance, + a_decode_control_block_ptr dctl) +/* +An underscore optionally preceded by a non-negative instance number is +expected at *p. Advance past the underscore. Return the instance number +(non-negative number plus two -- or one if no number is present) in *instance. +*/ +{ + *instance = 1; + if (isdigit((unsigned char)*p)) { + long num; + p = get_number(p, &num, dctl); + if (num < 0) { + bad_mangled_name(dctl); + } else { + *instance = num+2; + } /* if */ + } /* if */ + if (*p == '_') { + p += 1; + } else { + bad_mangled_name(dctl); + } /* if */ + return p; +} /* get_instance_number */ + + +static a_const_char *demangle_unnamed_type(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 . Return a pointer to the character +position following what was demangled. + + ::= Ut [ ] _ + ::= + ::= Ul E [ ] _ + ::= + + # Parameter types or "v" if the lambda has no parameters + + +*/ +{ + unsigned long instance; + + if (*ptr == 'U' && ptr[1] == 't') { + /* An unnamed type has an optional instance number followed by an + underscore. */ + ptr = get_instance_number(ptr+2, &instance, dctl); + if (!dctl->err_in_id) { + write_id_str("[unnamed type (instance ", dctl); + write_id_number(instance, dctl); + write_id_str(")]", dctl); + } /* if */ + } else if (*ptr == 'U' && ptr[1] == 'l') { + /* A lambda has the encoding for the operator() bare function type (without + the return type) and an optional instance number followed by an + underscore. */ + write_id_str("[lambda", dctl); + ptr = demangle_bare_function_type(ptr+2, /*no_return_type=*/TRUE, + BFT_PARAMS, dctl); + if (*ptr == 'E') { + ptr = get_instance_number(ptr+1, &instance, dctl); + if (!dctl->err_in_id) { + write_id_str(" (instance ", dctl); + write_id_number(instance, dctl); + write_id_str(")", dctl); + } /* if */ + } else { + bad_mangled_name(dctl); + } /* if */ + write_id_str("]", dctl); + } else { + bad_mangled_name(dctl); + } /* if */ + return ptr; +} /* demangle_unnamed_type */ + + +static a_const_char *demangle_abi_tag_attribute( + a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +GNU uses a 'B' suffix (ostensibly to , but it appears in other +contexts as well) to indicate a list of "abi_tag" attributes -- decode that +list here. Note that the resulting "__attribute(...)" string doesn't appear +in the proper location in the demangled name (but it serves the purpose of +differentiating the unmangled name from those without the "abi_tag" attribute). +This is a GNU-specific extension that uses the same letter ('B') as an +EDG-specific extension used for the module id of an externalized name but they +don't collide as the EDG-specific use uses 'B' as a prefix while the +GNU-specific extension uses 'B' as a suffix. +*/ +{ + long num; + + write_id_str("[abi:", dctl); + while (*ptr == 'B') { + ptr++; + ptr = get_number(ptr, &num, dctl); + if (num <= 0) { + bad_mangled_name(dctl); + break; + } else { + for (; num > 0; ptr++, num--) { + if (*ptr == '\0') { + bad_mangled_name(dctl); + break; + } else { + write_id_ch(*ptr, dctl); + } /* if */ + } /* for */ + if (*ptr == 'B') { + /* Another "abi_tag" attribute follows. */ + write_id_ch(',', dctl); + } /* if */ + } /* if */ + } /* while */ + write_id_ch(']', dctl); + return ptr; +} /* demangle_abi_tag_attribute */ + + +static a_const_char *demangle_unqualified_name( + a_const_char *ptr, + a_boolean *is_no_return_name, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 and output the demangled form. +Return a pointer to the character position following what was demangled. +An encodes a name that is not qualified, e.g., +"f" rather than "A::f". The syntax is: + + ::= + ::= # Not handled here + ::= + ::= + ::= DC + E # structured binding + +Constructor and destructor names do not get here; see +demangle_nested_name_components. *is_no_return_name is returned TRUE +if the name is one that does not get a return type (e.g., a +conversion function). is_no_return_name can be NULL if the +caller does not need the value. +*/ +{ + if (is_no_return_name != NULL) *is_no_return_name = FALSE; + if (isdigit((unsigned char)*ptr)) { + /* A , which has a length followed by the characters + of the identifier, as in "3abc". */ + ptr = demangle_source_name(ptr, /*is_module_id=*/FALSE, dctl); + } else if (*ptr == 'U' && + (ptr[1] == 't' || + ptr[1] == 'l')) { + /* */ + ptr = demangle_unnamed_type(ptr, dctl); + } else if (*ptr == 'D' && ptr[1] == 'C') { + /* A mangled name for a structured binding container. */ + ptr += 2; + write_id_str("[structured binding for ", dctl); + while (*ptr != 'E' && *ptr != '\0') { + ptr = demangle_source_name(ptr, /*is_module_id=*/FALSE, dctl); + if (*ptr != 'E' && *ptr != '\0') write_id_ch(',', dctl); + } /* while */ + if (*ptr != 'E') { + bad_mangled_name(dctl); + } else { + write_id_ch(']', dctl); + ptr++; + } /* if */ + } else { + /* */ + write_id_str("operator ", dctl); + if (*ptr == 'c' && ptr[1] == 'v') { + /* A conversion function. */ + if (is_no_return_name != NULL) *is_no_return_name = TRUE; + /* A demangling ambiguity exists in the IA-64 ABI when parsing a + templated conversion operator. We can't differentiate between + these type productions in this case: + ::= + ::= + For example, when presented with T_I1BIS4_IiEEEEv, should just the T_ + be parsed (as a ) or should the entire T_I1BIS4_IiEEEE + be parsed (as a )? + We can't do a local retry here because the type may parse just + fine both ways and we only find out later that there is a problem when + a substitution number is too large. On the initial attempt, prefer + the case (parse_template_args); on a + subsequent attempt (if the demangling fails), we'll try the other + case. */ + ptr = full_demangle_type(ptr+2, + dctl->parse_template_args_after_conversion_operator, + /*is_pack_expansion=*/FALSE, + dctl); + dctl->contains_conversion_operator = TRUE; + } else { + /* Other operator function (not conversion function). */ + int num_operands, length; + a_const_char *op_str, *close_str; + op_str = get_operator_name(ptr, &num_operands, &length, &close_str, + dctl); + if (op_str == NULL) { + bad_mangled_name(dctl); + } else { + write_id_str(op_str, dctl); + write_id_str(close_str, dctl); + ptr += length; + } /* if */ + } /* if */ + } /* if */ + if (*ptr == 'B') { + /* A 'B' suffix for a is a GNU-specific extension for + an abi_tag attribute. */ + ptr = demangle_abi_tag_attribute(ptr, dctl); + } /* if */ + return ptr; +} /* demangle_unqualified_name */ + + +static unsigned char get_hex_digit(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Convert a hexadecimal digit at ptr to an integral value, and return the +value. +*/ +{ + unsigned char value; + unsigned char ch = (unsigned char)ptr[0]; + + if (isdigit(ch)) { + value = (ch - '0'); + } else if (isxdigit(ch) && islower(ch)) { + value = (ch - 'a' + 10); + } else { + bad_mangled_name(dctl); + value = 0; + } /* if */ + return value; +} /* get_hex_digit */ + + +static a_const_char *demangle_float_number(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle a floating point number as specified in an IA-64 float or complex +literal and output the demangled form. The floating point number is +terminated by either an E or underscore, and the return value will point +to the terminating character. +*/ +{ + sizeof_t i, length; + char *p; + union { +#if USE_LONG_DOUBLE_FOR_HOST_FP_VALUE + long double ld; +#endif /* USE_LONG_DOUBLE_FOR_HOST_FP_VALUE */ + double d; + float f; + } x; + + /* Zero the bits of x. */ +#if USE_LONG_DOUBLE_FOR_HOST_FP_VALUE + x.ld = 0.0; +#else /* !USE_LONG_DOUBLE_FOR_HOST_FP_VALUE */ + x.d = 0.0; +#endif /* USE_LONG_DOUBLE_FOR_HOST_FP_VALUE */ + /* Determine the number of digits in the value by scanning to the + terminating "E" or "_". */ + length = 0; + p = (char *)ptr; + while (*p != 'E' && *p != '_' && *p != '\0') { + length++; + p++; + } /* while */ + if (length % 2 != 0) { + /* An odd number of bytes is an error. */ + bad_mangled_name(dctl); + length -= 1; + } /* if */ + /* Convert the length to a byte count. */ + length /= 2; + if (length > sizeof(x)) { + /* Too many bytes is an error. */ + bad_mangled_name(dctl); + length = sizeof(x); + } /* if */ + /* Convert the right number of bytes. */ + for (i = 0; i < length; i++, ptr+=2) { + unsigned char byte = get_hex_digit(ptr, dctl); + if (dctl->err_in_id) break; + byte = byte<<4 | get_hex_digit(ptr+1, dctl); + if (dctl->err_in_id) break; + if (host_little_endian) { + ((unsigned char *)&x)[length-1-i] = byte; + } else { + ((unsigned char *)&x)[i] = byte; + } /* if */ + } /* for */ + if (!dctl->err_in_id) { + /* Convert the floating-point value in x to a string. */ + char str[60]; + int ndig; + if (i <= sizeof(float)) { +#ifdef FLT_DIG + ndig = FLT_DIG; +#else /* !defined(FLT_DIG) */ + ndig = 6; +#endif /* ifdef FLT_DIG */ + (void)sprintf(str, "%.*g", ndig, x.f); +#if USE_LONG_DOUBLE_FOR_HOST_FP_VALUE + } else if (i > sizeof(double)) { +#ifdef LDBL_DIG + ndig = LDBL_DIG; +#else /* !defined(LDBL_DIG) */ + ndig = 18; +#endif /* ifdef LDBL_DIG */ + (void)sprintf(str, "%.*Lg", ndig, x.ld); +#endif /* USE_LONG_DOUBLE_FOR_HOST_FP_VALUE */ + } else { +#ifdef DBL_DIG + ndig = DBL_DIG; +#else /* !defined(DBL_DIG) */ + ndig = 15; +#endif /* ifdef DBL_DIG */ + (void)sprintf(str, "%.*g", ndig, x.d); + } /* if */ + /* Add trailing ".0" if no decimal point or exponent indication was put out + and the last character of the string is a digit (i.e., not + "inf" or "nan"). */ + p = str + strlen(str) - 1; + if (strchr(str, '.') == NULL && + strchr(str, 'e') == NULL && + isdigit((unsigned char)*p)) { + p++; + *p++ = '.'; + *p++ = '0'; + *p++ = '\0'; + } /* if */ + write_id_str(str, dctl); + } /* if */ + return ptr; +} /* demangle_float_number */ + + +static a_const_char *demangle_float_literal(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 float literal and output the demangled form. +Return a pointer to the character position following what was demangled. +The syntax is: + + ::= L E + + is the hexadecimal representation of the floating-point value, +high-order bytes first, using lower-case letters. +*/ +{ + /* Put parentheses around the type to make a cast. */ + write_id_ch('(', dctl); + ptr = demangle_type(ptr+1, dctl); + write_id_ch(')', dctl); + if (!dctl->err_in_id) { + ptr = demangle_float_number(ptr, dctl); + if (!dctl->err_in_id) { + ptr = advance_past('E', ptr, dctl); + } /* if */ + } /* if */ + return ptr; +} /* demangle_float_literal */ + + +static a_const_char *demangle_complex_literal(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 complex float literal and output the demangled form. +Return a pointer to the character position following what was demangled. +The syntax is: + + ::= L _ E + + is the hexadecimal representation of the floating-point value, +high-order bytes first, using lower-case letters. +*/ +{ + /* Put parentheses around the type to make a cast. */ + write_id_ch('(', dctl); + ptr = demangle_type(ptr+1, dctl); + write_id_str(")(", dctl); + /* Emit the literal as ( + i). */ + if (!dctl->err_in_id) { + ptr = demangle_float_number(ptr, dctl); + if (!dctl->err_in_id) { + ptr = advance_past('_', ptr, dctl); + if (!dctl->err_in_id) { + write_id_ch('+', dctl); + ptr = demangle_float_number(ptr, dctl); + if (!dctl->err_in_id) { + write_id_str("i)", dctl); + if (!dctl->err_in_id) { + ptr = advance_past('E', ptr, dctl); + } /* if */ + } /* if */ + } /* if */ + } /* if */ + } /* if */ + return ptr; +} /* demangle_complex_literal */ + +/* +Macro that returns TRUE if the character represents a floating point type. +*/ +#define is_floating_point_type(ch) \ + ((ch) == 'd' || (ch) == 'e' || (ch) == 'f' || (ch) == 'g') + +static a_const_char *demangle_expr_primary(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 literal or external name and output the demangled form. +Return a pointer to the character position following what was demangled. +The syntax is: + + ::= L E # integer literal + ::= L E # floating literal + ::= L E # string literal + ::= L E # nullptr literal (i.e., "LDnE") + ::= L _ E + # complex floating point literal (C 2000) + ::= L E # external name + +*/ +{ + a_const_char *sub = NULL; + + if (ptr[1] == 'S') { + /* Most of the types used in literals are s, so there are no + substitutions, but complex literals and string literals can have + substitutions, so watch out for these. */ + dctl->suppress_id_output++; + (void)demangle_substitution(ptr+1, 0, CVQ_NONE, + /*under_lhs_declarator=*/FALSE, + /*need_trailing_space=*/FALSE, + (a_const_char **)NULL, + &sub, + dctl); + dctl->suppress_id_output--; + } /* if */ + if (ptr[1] == '_') { + /* External name, L_Z E. */ + if (ptr[2] != 'Z') { + bad_mangled_name(dctl); + } else { + ptr = demangle_encoding(ptr+3, /*include_func_params=*/FALSE, dctl); + ptr = advance_past('E', ptr, dctl); + } /* if */ + } else if (is_floating_point_type(ptr[1])) { + /* Float literal, L E, where is the hexadecimal + representation of the value, high-order bytes first, with + lower-case hex letters. */ + ptr = demangle_float_literal(ptr, dctl); + } else if ((ptr[1] == 'C' && is_floating_point_type(ptr[2])) || + (sub != NULL && + (sub[0] == 'C' && is_floating_point_type(sub[1])))) { + /* Complex floating point literal. */ + ptr = demangle_complex_literal(ptr, dctl); + } else if (ptr[1] == 'D' && + (ptr[2] == 'n' || ptr[2] == 'N') && + ptr[3] == 'E') { + /* Recognize the literal for nullptr or __nullptr (the mangling is an + EDG extension for the C++/CLI managed __nullptr keyword). */ + dctl->suppress_id_output++; + (void)demangle_type(ptr+1, dctl); + dctl->suppress_id_output--; + if (ptr[2] == 'N') { + write_id_str("__nullptr", dctl); + } else { + write_id_str("nullptr", dctl); + } /* if */ + ptr += 4; + } else { + /* Integer literal, or string literal. */ + /* Put parentheses around the type to make a cast. */ + write_id_ch('(', dctl); + ptr = demangle_type(ptr+1, dctl); + write_id_ch(')', dctl); + if (*ptr == 'E') { + /* There's no value -- must have been a string literal. */ + write_id_str("\"...\"", dctl); + } else { + /* Copy the literal value. "n" is translated to a "-". */ + if (*ptr == 'n') { + write_id_ch('-', dctl); + ptr++; + } /* if */ + /* g++ 3.2 puts out L1xE instead of L_Z1xE, which gets demangled + sort of okay in the g++ demangler because the name is treated + as a type and a cast is put out with nothing following it: (x) */ + if (!isdigit((unsigned char)*ptr) && !emulate_gnu_abi_bugs) { + bad_mangled_name(dctl); + } else { + while (isdigit((unsigned char)*ptr)) { + write_id_ch(*ptr, dctl); + ptr++; + } /* while */ + } /* if */ + } /* if */ + ptr = advance_past('E', ptr, dctl); + } /* if */ + return ptr; +} /* demangle_expr_primary */ + + +static a_const_char *demangle_expression_list_full( + a_const_char *ptr, + char stop_char, + char open_paren, + char close_paren, + a_decode_control_block_ptr dctl) +/* +Demangle zero or more expressions, terminated by stop_char. The expression +list output is enclosed by open_paren/close_paren and separated by commas. +Returns a pointer to the terminating character (unless an error occurs). +*/ +{ + a_boolean first_time = TRUE; + + write_id_ch(open_paren, dctl); + while (*ptr != stop_char && !dctl->err_in_id) { + if (*ptr == '\0') { + bad_mangled_name(dctl); + break; + } /* if */ + if (!first_time) { + write_id_str(", ", dctl); + } else { + first_time = FALSE; + } /* if */ + ptr = demangle_expression(ptr, dctl); + } /* while */ + write_id_ch(close_paren, dctl); + return ptr; +} /* demangle_expression_list_full */ + + +static a_const_char *demangle_expression_list( + a_const_char *ptr, + char stop_char, + a_decode_control_block_ptr dctl) +/* +Demangle zero or more expressions, terminated by stop_char. The expression +list output is enclosed in parentheses and separated by commas. Returns a +pointer to the terminating character (unless an error occurs). +*/ +{ + return demangle_expression_list_full(ptr, stop_char, '(', ')', dctl); +} /* demangle_expression_list */ + + +static a_const_char *demangle_initializer( + a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle an (or an 'E') starting at ptr. + + ::= pi * E # parenthesized initialization + ::= il * E # braced-init list + +*/ +{ + if (*ptr == 'E') { + ptr++; + } else { + if (*ptr == 'p' && ptr[1] == 'i') { + ptr = demangle_expression_list(ptr+2, 'E', dctl); + ptr = advance_past('E', ptr, dctl); + } else if (*ptr == 'i' && ptr[1] == 'l') { + ptr = demangle_expression_list_full(ptr+2, 'E', '{', '}', dctl); + ptr = advance_past('E', ptr, dctl); + } else { + bad_mangled_name(dctl); + } /* if */ + } /* if */ + return ptr; +} /* demangle_initializer */ + + +static a_const_char *demangle_expression(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 and output the demangled form. +Return a pointer to the character position following what was demangled. +An encodes an expression (usually for a nontype +template argument value written in terms of template parameters or +trailing return types specified using decltype). +The syntax is: + + ::= + ::= + ::= + + ::= cl + E + # call + ::= cp * E + # call (with ADL suppressed) + ::= cv + # conversion with one argument + ::= cv _ * E + # conversion with a different number of arguments + ::= [gs] nw * _ E + # new (expr-list) type + ::= [gs] nw * _ + # new (expr-list) type (init) + ::= [gs] na * _ E + # new[] (expr-list) type + ::= [gs] na * _ + # new[] (expr-list) type (init) + ::= [gs] dl + # delete expression + ::= [gs] da + # delete[] expression + ::= pp_ + # prefix ++ + ::= mm_ + # prefix -- + ::= ti + # typeid (type) + ::= te + # typeid (expression) + ::= dc + # dynamic_cast (expression) + ::= sc + # static_cast (expression) + ::= cc + # const_cast (expression) + ::= rc + # reinterpret_cast (expression) + ::= st + # sizeof (a type) + ::= at + # alignof (a type) + ::= + ::= + ::= dt + # expr.name + ::= pt + # expr->name + ::= ds + # expr.*expr + ::= tw + # throw expression + ::= tr + # throw with no operand (rethrow) + ::= + # f(p), N::f(p), ::f(p), + # freestanding dependent name (e.g., T::x), + # objectless nonstatic member reference + ::= sZ + # size of a parameter pack + ::= sZ + # size of a function parameter pack + ::= sp + # pack expansion + ::= il * E + # initializer list + ::= tl * E + # typed initializer list + ::= fl + # ( ... op pack ) + ::= fr + # ( pack op ... ) + ::= fL + # ( expr op ... op pack ) + ::= fR + # ( pack op ... op expr ) + ::= + +Also, these non-standard expressions (EDG-specific) are demangled: + + ::= gc _ E + # gcnew type + ::= gc _ + # gcnew type (init) + ::= gc * _ E + # gcnew array(dims) + ::= gc * _ + # gcnew array(dims) {init} + +*/ +{ + int num_operands, length; + a_const_char *op_str, *close_str; + + if (*ptr == 'L') { + /* A literal or external name. */ + ptr = demangle_expr_primary(ptr, dctl); + } else if (*ptr == 'T') { + /* A template parameter. */ + ptr = demangle_template_param(ptr, dctl); + } else if (*ptr == 'f') { + if (ptr[1] == 'p' || + (ptr[1] == 'L' && isdigit((unsigned char)ptr[2]))) { + /* A reference to a function parameter. */ + ptr = demangle_parameter_reference(ptr, dctl); + } else { + /* A "fold expression". */ + a_boolean unary, left; + a_const_char *op_str, *close_str; + int num_operands, length; + switch (ptr[1]) { + case 'l': unary = TRUE; left = TRUE; break; + case 'L': unary = FALSE; left = TRUE; break; + case 'r': unary = TRUE; left = FALSE; break; + case 'R': unary = FALSE; left = FALSE; break; + default: + bad_mangled_name(dctl); + break; + } /* switch */ + ptr += 2; + op_str = get_operator_name(ptr, &num_operands, &length, &close_str, + dctl); + if (op_str == NULL) { + bad_mangled_name(dctl); + } else { + ptr += length; + write_id_ch('(', dctl); + if (unary) { + if (left) { + write_id_str("...", dctl); + write_id_str(op_str, dctl); + ptr = demangle_expression(ptr, dctl); + } else { + ptr = demangle_expression(ptr, dctl); + write_id_str(op_str, dctl); + write_id_str("...", dctl); + } /* if */ + } else { + ptr = demangle_expression(ptr, dctl); + write_id_str(op_str, dctl); + write_id_str("...", dctl); + write_id_str(op_str, dctl); + ptr = demangle_expression(ptr, dctl); + } /* if */ + write_id_ch(')', dctl); + } /* if */ + } /* if */ + } else if (*ptr == 'c' && ptr[1] == 'l') { + /* Call expression: "cl + E" */ + ptr += 2; + ptr = demangle_expression(ptr, dctl); + ptr = demangle_expression_list(ptr, 'E', dctl); + ptr = advance_past('E', ptr, dctl); + } else if (*ptr == 'c' && ptr[1] == 'p') { + /* Call expression (w/ADL suppressed): "cp * E" */ + ptr += 2; + write_id_ch('(', dctl); + ptr = demangle_simple_id(ptr, dctl); + write_id_ch(')', dctl); + ptr = demangle_expression_list(ptr, 'E', dctl); + ptr = advance_past('E', ptr, dctl); + } else if (*ptr == 'c' && ptr[1] == 'v') { + /* Cast/conversion (with one type and zero or more arguments). When + exactly one expression is specified, emit "(T)expr", otherwise + emit T(expr). */ + a_const_char *nptr; + a_boolean one_argument = FALSE; + /* Take a peek (without emitting the type, but recording substitutions) + to see which case we have. */ + dctl->suppress_id_output++; + nptr = demangle_type(ptr+2, dctl); + dctl->suppress_id_output--; + if (!dctl->err_in_id && *nptr != '_') { + one_argument = TRUE; + write_id_ch('(', dctl); + } /* if */ + /* Re-scan the type, this time emitting it (but not recording + substitutions). */ + dctl->suppress_substitution_recording++; + ptr = demangle_type(ptr+2, dctl); + dctl->suppress_substitution_recording--; + if (!dctl->err_in_id) { + if (one_argument) { + /* Exactly one expression. */ + write_id_ch(')', dctl); + ptr = demangle_expression(ptr, dctl); + } else { + /* Some number of expressions (other than one). */ + if (*ptr != '_') { + bad_mangled_name(dctl); + } else { + ptr = demangle_expression_list(ptr+1, 'E', dctl); + ptr = advance_past('E', ptr, dctl); + } /* if */ + } /* if */ + } /* if */ + } else if (*ptr == 'g' && ptr[1] == 's') { + /* global scope: "::". This prefix precedes new/delete operations as + well as the scope-resolution operator in . */ + write_id_str("::", dctl); + ptr = demangle_expression(ptr+2, dctl); + } else if (*ptr == 'n' && (ptr[1] == 'w' || ptr[1] == 'a')) { + /* new or new[] */ + if (ptr[1] == 'w') { + write_id_str("new ", dctl); + } else { + write_id_str("new[] ", dctl); + } /* if */ + ptr+=2; + /* Optional placement expressions. */ + if (*ptr != '_') { + ptr = demangle_expression_list(ptr, '_', dctl); + write_id_ch(' ', dctl); + } /* if */ + ptr = advance_past('_', ptr, dctl); + if (!dctl->err_in_id) { + ptr = demangle_type(ptr, dctl); + if (!dctl->err_in_id) { + ptr = demangle_initializer(ptr, dctl); + } /* if */ + } /* if */ + } else if (*ptr == 'g' && ptr[1] == 'c') { + /* C++/CLI gcnew (EDG-specific mangling). */ + ptr+=2; + write_id_str("gcnew ", dctl); + /* Optional array dimension expressions. */ + if (*ptr != '_') { + a_const_char *optr = ptr, *ptr2; + /* We need the type before the dimension list, so suppress the list + to get to the type. */ + dctl->suppress_id_output++; + dctl->suppress_substitution_recording++; + ptr2 = demangle_expression_list(ptr, '_', dctl); + dctl->suppress_id_output--; + dctl->suppress_substitution_recording--; + if (!dctl->err_in_id) { + ptr2 = advance_past('_', ptr2, dctl); + ptr = demangle_type(ptr2, dctl); + (void)demangle_expression_list(optr, '_', dctl); + write_id_ch(' ', dctl); + } /* if */ + } else { + ptr = advance_past('_', ptr, dctl); + ptr = demangle_type(ptr, dctl); + } /* if */ + if (!dctl->err_in_id) { + ptr = demangle_initializer(ptr, dctl); + } /* if */ + } else if (*ptr == 'd' && ptr[1] == 't') { + /* expr.name */ + write_id_ch('(', dctl); + ptr = demangle_expression(ptr+2, dctl); + if (!dctl->err_in_id) { + write_id_ch('.', dctl); + ptr = demangle_unresolved_name(ptr, dctl); + write_id_ch(')', dctl); + } /* if */ + } else if (*ptr == 'p' && ptr[1] == 't') { + /* expr->name */ + write_id_ch('(', dctl); + ptr = demangle_expression(ptr+2, dctl); + if (!dctl->err_in_id) { + write_id_str("->", dctl); + ptr = demangle_unresolved_name(ptr, dctl); + write_id_ch(')', dctl); + } /* if */ + } else if (*ptr == 's' && ptr[1] == 'Z') { + /* Size of a parameter pack. */ + write_id_str("sizeof...(", dctl); + ptr+=2; + if (*ptr == 'T') { + /* sizeof...() */ + ptr = demangle_template_param(ptr, dctl); + } else if (*ptr == 'f' ) { + /* sizeof...() */ + ptr = demangle_parameter_reference(ptr, dctl); + } else { + bad_mangled_name(dctl); + } /* if */ + write_id_ch(')', dctl); + } else if (*ptr == 's' && ptr[1] == 'p') { + /* Pack expansion. */ + ptr+=2; + ptr = demangle_expression(ptr, dctl); + write_id_str("...", dctl); + } else if ((*ptr == 't' || *ptr == 'i') && ptr[1] == 'l') { + /* Initializer list. */ + if (*ptr == 't') { + /* A type is included. */ + ptr = demangle_type(ptr+2, dctl); + } else { + ptr += 2; + } /* if */ + if (!dctl->err_in_id) { + ptr = demangle_expression_list_full(ptr, 'E', '{', '}', dctl); + ptr = advance_past('E', ptr, dctl); + } /* if */ + } else if ((op_str = get_operator_name(ptr, &num_operands, &length, + &close_str, dctl)) != NULL) { + /* An expression beginning with an operator name. */ + /* As a heuristic, to avoid extraneous parentheses in the demangled output, + assume that any operator that has a closing string doesn't need + parentheses around it. */ + a_boolean needs_parens = strcmp(close_str, "") == 0; + ptr += length; + if (needs_parens) write_id_ch('(', dctl); + if (strncmp(op_str, "builtin-operation-", 18) == 0) { + /* Builtin operation. Has a variable number of operands. */ + int i; + write_id_str(op_str, dctl); + write_id_ch('(', dctl); + for (i = 1; i <= num_operands; i++) { + if (*ptr == 'T' && ptr[1] == 'O') { + /* "TO" indicates a type operand. */ + ptr = demangle_type(ptr+2, dctl); + } else { + ptr = demangle_expression(ptr, dctl); + } /* if */ + if (i != num_operands) write_id_str(", ", dctl); + } /* for */ + write_id_ch(')', dctl); + } else if (strncmp(op_str, "subscript", 9) == 0) { + /* C++/CLI subscript operation. Has a variable number of operands. */ + int i; + ptr = demangle_expression(ptr, dctl); + write_id_ch('[', dctl); + for (i = 2; i <= num_operands; i++) { + ptr = demangle_expression(ptr, dctl); + if (i != num_operands) write_id_str(", ", dctl); + } /* for */ + write_id_ch(']', dctl); + } else if (num_operands == 1) { + char cast_close = 0; + /* Unary operations (old style cast is handled above). */ + if (strcmp(op_str, "++") == 0 || + strcmp(op_str, "--") == 0) { + if (*ptr == '_') { + /* Prefix version. */ + ptr++; + } else { + /* Postfix version. */ + close_str = op_str; + op_str = ""; + } /* if */ + } /* if */ + write_id_str(op_str, dctl); + if (strcmp(op_str, "static_cast") == 0 || + strcmp(op_str, "dynamic_cast") == 0 || + strcmp(op_str, "const_cast") == 0 || + strcmp(op_str, "reinterpret_cast") == 0 || + strcmp(op_str, "safe_cast") == 0) { + /* New style cast. */ + write_id_ch('<', dctl); + ptr = demangle_type(ptr, dctl); + write_id_str(">(", dctl); + cast_close = ')'; + } /* if */ + ptr = demangle_expression(ptr, dctl); + if (cast_close != 0) write_id_ch(cast_close, dctl); + } else if (num_operands == 2) { + /* Binary operations. */ + ptr = demangle_expression(ptr, dctl); + write_id_str(op_str, dctl); + ptr = demangle_expression(ptr, dctl); + } else if (num_operands == 3) { + /* Ternary operations ("?"). */ + ptr = demangle_expression(ptr, dctl); + write_id_str(op_str, dctl); + ptr = demangle_expression(ptr, dctl); + write_id_str(":", dctl); + ptr = demangle_expression(ptr, dctl); + } else { + /* Special cases: sizeof(type), __alignof__(type), + __uuidof(type), typeid(type), T::typeid, scope resolution "::", + throw (just the rethrow variety). */ + if (strcmp(op_str, "sizeof(") == 0) { + /* sizeof(type). */ + write_id_str(op_str, dctl); + ptr = demangle_type(ptr, dctl); + } else if (strcmp(op_str, "alignof(") == 0 || + strcmp(op_str, "__alignof__(") == 0) { + /* __alignof__(type). */ + write_id_str(op_str, dctl); + ptr = demangle_type(ptr, dctl); + } else if (strcmp(op_str, "__uuidof(") == 0) { + /* __uuidof(type). */ + write_id_str(op_str, dctl); + ptr = demangle_type(ptr, dctl); + } else if (strcmp(op_str, "typeid(") == 0) { + /* typeid(type). */ + write_id_str(op_str, dctl); + ptr = demangle_type(ptr, dctl); + } else if (strcmp(op_str, "::typeid") == 0) { + /* C++/CLI T::typeid. */ + ptr = demangle_type(ptr, dctl); + write_id_str(op_str, dctl); + } else if (strcmp(op_str, "throw") == 0) { + /* throw. This handles the rethrow variety, throw-expression is + handled separately. */ + write_id_str(op_str, dctl); + } else if (strncmp(op_str, "\"\"", 2) == 0) { + /* A literal operator. The EDG front end doesn't produce a mangled + name that should get here (i.e., there should be no "clli" mangled + names), but other compilers produce this mangling, so handle it + by inserting the implied operator keyword. */ + write_id_str("operator ", dctl); + write_id_str(op_str, dctl); + } else { + bad_mangled_name(dctl); + } /* if */ + } /* if */ + write_id_str(close_str, dctl); + if (needs_parens) write_id_ch(')', dctl); + } else { + /* Assume it's an . */ + ptr = demangle_unresolved_name(ptr, dctl); + } /* if */ + return ptr; +} /* demangle_expression */ + + +static a_const_char *demangle_template_args(a_const_char *ptr, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 and output the demangled form. +Return a pointer to the character position following what was demangled. +A encodes a template argument list. The syntax is: + + ::= I + E + ::= # type or template + ::= X E # expression + ::= # simple expressions + ::= J * E # argument pack + +*/ +{ + a_boolean suppress = FALSE; + + if (*ptr == 'J') { + /* In pack expansion cases, suppress generation of angle brackets. */ + suppress = TRUE; + } /* if */ + /* Advance past the "I" or "J". */ + ptr++; + if (!suppress) write_id_ch('<', dctl); + for (;;) { + if (*ptr == 'X') { + /* An expression, X E. */ + ptr = demangle_expression(ptr+1, dctl); + ptr = advance_past('E', ptr, dctl); + } else if (*ptr == 'L') { + /* Literal or external name. */ + ptr = demangle_expr_primary(ptr, dctl); + } else if (*ptr == 'J' || + (*ptr == 'I' && emulate_gnu_abi_bugs)) { + /* Template argument pack. */ + ptr = demangle_template_args(ptr, dctl); + } else if (*ptr == 'E') { + /* No template arguments. */ + break; + } else { + /* Type template argument. */ + ptr = demangle_type(ptr, dctl); + } /* if */ + /* "E" ends the template argument list. */ + if (*ptr == 'E') break; + /* Stop on an error. */ + if (dctl->err_in_id) break; + /* Continuing, so put out a comma between template arguments. */ + write_id_str(", ", dctl); + } /* for */ + ptr = advance_past('E', ptr, dctl); + if (!suppress) write_id_ch('>', dctl); + return ptr; +} /* demangle_template_args */ + + +static a_const_char *demangle_nested_name_components( + a_const_char *ptr, + unsigned long num_levels, + a_boolean *is_no_return_name, + a_boolean *has_templ_arg_list, + char *ctor_dtor_kind, + a_const_char **last_component_name, + a_decode_control_block_ptr dctl) +/* +Demangle one or more name level components of an IA-64 . +Each level is either an unqualified name or a substitution, optionally +followed by a template argument list. ptr points to the beginning of +the , after the initial "N" and the if any. +If num_levels is zero, scan all components of the nested name, stopping +on the final "E"; otherwise, scan num_levels levels and then stop. +Note that a substitution counts as one level even if it represents +several. Return a pointer to the character position following what +was demangled. *is_no_return_name is returned TRUE if the final +component scanned is a function name of a kind that does not take a +return type (constructor, destructor, or conversion function). +*has_templ_arg_list is returned TRUE if the final component includes a +template argument list. If the final component is a constructor or +destructor name, *ctor_dtor_kind is set to the character identifying +the kind of constructor or destructor. If last_component_name is non-NULL, +*last_component_name will be set to the start position of the encoding +for the name of the last component. If the last component is a +substitution, the name of the last component in the substitution is used. +*/ +{ + a_const_char *prev_component_name = NULL; + a_const_char *first_component_start = ptr; + unsigned long level_num = 0; + + *is_no_return_name = FALSE; + *has_templ_arg_list = FALSE; + *ctor_dtor_kind = ' '; + for (;;) { + /* Demangle one level of the nested name. */ + a_boolean is_substitution = FALSE; + a_boolean suppress_qualification = FALSE; + level_num++; + *is_no_return_name = FALSE; + *has_templ_arg_list = FALSE; + if (*ptr == 'E' || *ptr == '\0') { + /* Error, unexpected end of nested name. */ + bad_mangled_name(dctl); + } else if (*ptr == 'S') { + /* A substitution. */ + is_substitution = TRUE; + ptr = demangle_substitution(ptr, 0, CVQ_NONE, + /*under_lhs_declarator=*/FALSE, + /*need_trailing_space=*/FALSE, + &prev_component_name, + (a_const_char **)NULL, + dctl); + /* A substitution cannot be the last thing; it must be followed + by another name or a template argument list. */ + if (*ptr == 'E') { + bad_mangled_name(dctl); + } /* if */ + } else if (*ptr == 'T') { + /* A . */ + ptr = demangle_template_param(ptr, dctl); + } else if (*ptr == 'D' && (ptr[1] == 't' || ptr[1] == 'T')) { + /* A . */ + ptr = demangle_type(ptr, dctl); + } else { + /* Not a substitution or template parameter, so an . */ + if (*ptr != 'C' && (*ptr != 'D' || ptr[1] == 'C')) { + /* Normal case, not a constructor or destructor name. */ + prev_component_name = ptr; + ptr = demangle_unqualified_name(ptr, is_no_return_name, dctl); + } else { + /* A constructor or destructor name (or their C++/CLI counterparts: + a static constructor or finalizer). Put out the class name again + (it's provided by prev_component_name). */ + *is_no_return_name = TRUE; + if (*ptr == 'D') { + if (ptr[1] == '7') { + /* A C++/CLI finalizer. */ + write_id_ch('!', dctl); + } else { + /* Some type of destructor. */ + write_id_ch('~', dctl); + } /* if */ + } /* if */ + if (prev_component_name == NULL || + *prev_component_name == 'S') { + /* The constructor or destructor code is the first thing in the + nested name or the previous name is a substitution (we're + supposed to have gotten the name from inside the + substitution). */ + bad_mangled_name(dctl); + } else { + a_boolean dummy; + /* Rescan and output the class name (no template argument list). */ + (void)demangle_unqualified_name(prev_component_name, &dummy, dctl); + /* Check that the second character of the constructor/destructor + name is a valid digit. */ + /* "C3" is for an allocating constructor (not generated by the + EDG Front End but part of the ABI spec). */ + /* "D7" is the code used by the EDG C++ Front End for C++/CLI + finalizers. It's not part of the ABI spec. */ + /* "C8" is the code used by the EDG C++ Front End for C++/CLI + static constructors. It's not part of the ABI spec. */ + /* "C9" is the code used by the EDG C++ Front End for the + common routine called by the various delegating constructor + entry points. It's not part of the ABI spec. */ + /* "D9" is the code used by the EDG C++ Front End for the + delegation destructor alternate entry point. It's not part + of the ABI spec. */ + if (ptr[1] == '1' || ptr[1] == '2' || ptr[1] == '9' || + (ptr[0] == 'C' ? (ptr[1] == '3' || ptr[1] == '8') : + (ptr[1] == '0' || ptr[1] == '7'))) { + /* Okay. */ + *ctor_dtor_kind = ptr[1]; + ptr += 2; + if (*ptr == 'B') { + /* A 'B' suffix for a is a GNU-specific extension + for an abi_tag attribute. */ + ptr = demangle_abi_tag_attribute(ptr, dctl); + } /* if */ + } else { + /* The second character of the constructor or destructor name + encoding is bad. */ + bad_mangled_name(dctl); + } /* if */ + } /* if */ + } /* if */ + if (*ptr == 'M') { + /* A . No further output is required (the + member's has been emitted above). */ + ptr++; + } /* if */ + } /* if */ + if (*ptr == 'I') { + /* A list. */ + /* Record a potential substitution on the template prefix up to + this point, but not if the entire prefix is a substitution. */ + if (!is_substitution) { + record_substitutable_entity(first_component_start, + subk_template_prefix, level_num-1, + /*parse_template_args=*/FALSE, dctl); + } /* if */ + /* Scan the template argument list. */ + ptr = demangle_template_args(ptr, dctl); + *has_templ_arg_list = TRUE; + is_substitution = FALSE; + } /* if */ + /* "E" marks the end of the list. */ + if (*ptr == 'E') break; + if (!is_substitution) { + /* Record a potential substitution on the prefix up to this point, + but not if the entire prefix is a substitution (without + template argument list). */ + record_substitutable_entity(first_component_start, subk_prefix, + level_num, + /*parse_template_args=*/FALSE, dctl); + } /* if */ + /* Stop on an error. */ + if (dctl->err_in_id) break; + /* Stop if we've done enough levels. */ + if (num_levels != 0 && level_num >= num_levels) break; + /* Going around again, so the part put out so far is a qualifier and + needs to be followed by "::". */ + if (!suppress_qualification) write_id_str("::", dctl); + } /* for */ + if (last_component_name != NULL) *last_component_name = prev_component_name; + return ptr; +} /* demangle_nested_name_components */ + + +static a_const_char *demangle_nested_name( + a_const_char *ptr, + a_func_block *func_block, + a_decode_control_block_ptr dctl) +/* +Demangle an IA-64 and output the demangled form. Return +a pointer to the character position following what was demangled. +A represents a qualified name, e.g., A::B::x. +The syntax is: + + ::= N [] [] + E + ::= N [] [] + E + ::= + ::= + ::= + ::= + ::= # empty + ::= + ::= + ::=