// C++ IA64 / g++ v3 demangler -*- C++ -*- // Copyright (C) 2003, 2004 Free Software Foundation, Inc. // Written by Carlo Wood <carlo@alinoe.com> // // 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 2, 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. // You should have received a copy of the GNU General Public License along // with this library; see the file COPYING. If not, write to the Free // Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, // USA. // As a special exception, you may use this file as part of a free software // library without restriction. Specifically, if other files instantiate // templates or use macros or inline functions from this file, or you compile // this file and link it with other files to produce an executable, this // file does not by itself cause the resulting executable to be covered by // the GNU General Public License. This exception does not however // invalidate any other reasons why the executable file might be covered by // the GNU General Public License. // This file implements demangling of "C++ ABI for Itanium"-mangled symbol // and type names as described in Revision 1.73 of the C++ ABI as can be found // at http://www.codesourcery.com/cxx-abi/abi.html#mangling #ifndef _DEMANGLER_H #define _DEMANGLER_H 1 #include <vector> #include <string> #include <ext/new_allocator.h> #ifndef _GLIBCXX_DEMANGLER_DEBUG #define _GLIBCXX_DEMANGLER_CWDEBUG 0 #define _GLIBCXX_DEMANGLER_DEBUG(x) #define _GLIBCXX_DEMANGLER_DOUT(cntrl, data) #define _GLIBCXX_DEMANGLER_DOUT_ENTERING(x) #define _GLIBCXX_DEMANGLER_DOUT_ENTERING2(x) #define _GLIBCXX_DEMANGLER_DOUT_ENTERING3(x) #define _GLIBCXX_DEMANGLER_RETURN return M_result #define _GLIBCXX_DEMANGLER_RETURN2 return M_result #define _GLIBCXX_DEMANGLER_RETURN3 #define _GLIBCXX_DEMANGLER_FAILURE \ do { M_result = false; return false; } while(0) #else #define _GLIBCXX_DEMANGLER_CWDEBUG 1 #endif namespace __gnu_cxx { namespace demangler { enum substitution_nt { type, template_template_param, nested_name_prefix, nested_name_template_prefix, unscoped_template_name }; struct substitution_st { int M_start_pos; substitution_nt M_type; int M_number_of_prefixes; substitution_st(int start_pos, substitution_nt type, int number_of_prefixes) : M_start_pos(start_pos), M_type(type), M_number_of_prefixes(number_of_prefixes) { } }; enum simple_qualifier_nt { complex_or_imaginary = 'G', pointer = 'P', reference = 'R' }; enum cv_qualifier_nt { cv_qualifier = 'K' }; enum param_qualifier_nt { vendor_extension = 'U', array = 'A', pointer_to_member = 'M' }; template<typename Tp, typename Allocator = __gnu_cxx::new_allocator<Tp> > class qualifier; template<typename Tp, typename Allocator = __gnu_cxx::new_allocator<Tp> > class qualifier_list; template<typename Tp, typename Allocator = __gnu_cxx::new_allocator<Tp> > class session; template<typename Tp, typename Allocator> class qualifier { typedef typename Allocator::template rebind<char>::other char_Allocator; typedef std::basic_string<char, std::char_traits<char>, char_Allocator> string_type; private: char M_qualifier1; char M_qualifier2; char M_qualifier3; mutable unsigned char M_cnt; string_type M_optional_type; int M_start_pos; bool M_part_of_substitution; public: qualifier(int start_pos, simple_qualifier_nt simple_qualifier, int inside_substitution) : M_qualifier1(simple_qualifier), M_start_pos(start_pos), M_part_of_substitution(inside_substitution) { } qualifier(int start_pos, cv_qualifier_nt, char const* start, int count, int inside_substitution) : M_qualifier1(start[0]), M_qualifier2((count > 1) ? start[1] : '\0'), M_qualifier3((count > 2) ? start[2] : '\0'), M_start_pos(start_pos), M_part_of_substitution(inside_substitution) { } qualifier(int start_pos, param_qualifier_nt param_qualifier, string_type optional_type, int inside_substitution) : M_qualifier1(param_qualifier), M_optional_type(optional_type), M_start_pos(start_pos), M_part_of_substitution(inside_substitution) { } int get_start_pos(void) const { return M_start_pos; } char first_qualifier(void) const { M_cnt = 1; return M_qualifier1; } char next_qualifier(void) const { return (++M_cnt == 2) ? M_qualifier2 : ((M_cnt == 3) ? M_qualifier3 : 0); } string_type const& get_optional_type(void) const { return M_optional_type; } bool part_of_substitution(void) const { return M_part_of_substitution; } #if _GLIBCXX_DEMANGLER_CWDEBUG friend std::ostream& operator<<(std::ostream& os, qualifier const& qual) { os << (char)qual.M_qualifier1; if (qual.M_qualifier1 == vendor_extension || qual.M_qualifier1 == array || qual.M_qualifier1 == pointer_to_member) os << " [" << qual.M_optional_type << ']'; else if (qual.M_qualifier1 == 'K' || qual.M_qualifier1 == 'V' || qual.M_qualifier1 == 'r') { if (qual.M_qualifier2) { os << (char)qual.M_qualifier2; if (qual.M_qualifier3) os << (char)qual.M_qualifier3; } } return os; } #endif }; template<typename Tp, typename Allocator> class qualifier_list { typedef typename Allocator::template rebind<char>::other char_Allocator; typedef std::basic_string<char, std::char_traits<char>, char_Allocator> string_type; private: mutable bool M_printing_suppressed; typedef qualifier<Tp, Allocator> qual; typedef typename Allocator::template rebind<qual>::other qual_Allocator; typedef std::vector<qual, qual_Allocator> qual_vector; qual_vector M_qualifier_starts; session<Tp, Allocator>& M_demangler; void decode_KVrA(string_type& prefix, string_type& postfix, int cvq, typename qual_vector:: const_reverse_iterator const& iter_array) const; public: qualifier_list(session<Tp, Allocator>& demangler_obj) : M_printing_suppressed(false), M_demangler(demangler_obj) { } void add_qualifier_start(simple_qualifier_nt simple_qualifier, int start_pos, int inside_substitution) { M_qualifier_starts. push_back(qualifier<Tp, Allocator>(start_pos, simple_qualifier, inside_substitution)); } void add_qualifier_start(cv_qualifier_nt cv_qualifier, int start_pos, int count, int inside_substitution) { M_qualifier_starts. push_back(qualifier<Tp, Allocator>(start_pos, cv_qualifier, &M_demangler.M_str[start_pos], count, inside_substitution)); } void add_qualifier_start(param_qualifier_nt param_qualifier, int start_pos, string_type optional_type, int inside_substitution) { M_qualifier_starts. push_back(qualifier<Tp, Allocator>(start_pos, param_qualifier, optional_type, inside_substitution)); } void decode_qualifiers(string_type& prefix, string_type& postfix, bool member_function_pointer_qualifiers) const; bool suppressed(void) const { return M_printing_suppressed; } void printing_suppressed(void) { M_printing_suppressed = true; } size_t size(void) const { return M_qualifier_starts.size(); } #if _GLIBCXX_DEMANGLER_CWDEBUG friend std::ostream& operator<<(std::ostream& os, qualifier_list const& list) { typename qual_vector::const_iterator iter = list.M_qualifier_starts.begin(); if (iter != list.M_qualifier_starts.end()) { os << "{ " << *iter; while (++iter != list.M_qualifier_starts.end()) os << ", " << *iter; os << " }"; } else os << "{ }"; return os; } #endif }; struct implementation_details { private: unsigned int M_style; public: // The following flags change the behaviour of the demangler. The // default behaviour is that none of these flags is set. static unsigned int const style_void = 1; // Default behaviour: int f() // Use (void) instead of (): int f(void) static unsigned int const style_literal = 2; // Default behaviour: (long)13, // (unsigned long long)19 // Use extensions 'u', 'l' and 'll' for integral // literals (as in template arguments): 13l, 19ull static unsigned int const style_literal_int = 4; // Default behaviour: 4 // Use also an explicit // cast for int in literals: (int)4 static unsigned int const style_compact_expr_ops = 8; // Default behaviour: (i) < (3), sizeof (int) // Don't output spaces around // operators in expressions: (i)<(3), sizeof(int) static unsigned int const style_sizeof_typename = 16; // Default behaviour: sizeof (X::t) // Put 'typename' infront of <nested-name> // types inside a 'sizeof': sizeof (typename X::t) public: implementation_details(unsigned int style_flags = 0) : M_style(style_flags) { } virtual ~implementation_details() { } bool get_style_void(void) const { return (M_style & style_void); } bool get_style_literal(void) const { return (M_style & style_literal); } bool get_style_literal_int(void) const { return (M_style & style_literal_int); } bool get_style_compact_expr_ops(void) const { return (M_style & style_compact_expr_ops); } bool get_style_sizeof_typename(void) const { return (M_style & style_sizeof_typename); } // This can be overridden by user implementations. virtual bool decode_real(char* /* output */, unsigned long* /* input */, size_t /* size_of_real */) const { return false; } }; template<typename Tp, typename Allocator> class session { public: friend class qualifier_list<Tp, Allocator>; typedef typename Allocator::template rebind<char>::other char_Allocator; typedef std::basic_string<char, std::char_traits<char>, char_Allocator> string_type; private: char const* M_str; int M_pos; int M_maxpos; bool M_result; int M_inside_template_args; int M_inside_type; int M_inside_substitution; bool M_saw_destructor; bool M_name_is_cdtor; bool M_name_is_template; bool M_name_is_conversion_operator; bool M_template_args_need_space; string_type M_function_name; typedef typename Allocator::template rebind<int>::other int_Allocator; typedef typename Allocator::template rebind<substitution_st>::other subst_Allocator; std::vector<int, int_Allocator> M_template_arg_pos; int M_template_arg_pos_offset; std::vector<substitution_st, subst_Allocator> M_substitutions_pos; implementation_details const& M_implementation_details; typedef typename Allocator::template rebind<qualifier_list<Allocator> >::other qualifier_list_Allocator; qualifier_list_Allocator M_qualifier_list_alloc; #if _GLIBCXX_DEMANGLER_CWDEBUG bool M_inside_add_substitution; #endif public: explicit session(char const* in, int len, implementation_details const& id = implementation_details()) : M_str(in), M_pos(0), M_maxpos(len - 1), M_result(true), M_inside_template_args(0), M_inside_type(0), M_inside_substitution(0), M_saw_destructor(false), M_name_is_cdtor(false), M_name_is_template(false), M_name_is_conversion_operator(false), M_template_args_need_space(false), M_template_arg_pos_offset(0), M_implementation_details(id) #if _GLIBCXX_DEMANGLER_CWDEBUG , M_inside_add_substitution(false) #endif { } static int decode_encoding(string_type& output, char const* input, int len, implementation_details const& id = implementation_details()); bool decode_type(string_type& output, qualifier_list<Tp, Allocator>* qualifiers = NULL) { string_type postfix; bool res = decode_type_with_postfix(output, postfix, qualifiers); output += postfix; return res; } bool remaining_input_characters(void) const { return current() != 0; } private: char current(void) const { return (M_pos > M_maxpos) ? 0 : M_str[M_pos]; } char next_peek(void) const { return (M_pos >= M_maxpos) ? 0 : M_str[M_pos + 1]; } char next(void) { return (M_pos >= M_maxpos) ? 0 : M_str[++M_pos]; } char eat_current(void) { return (M_pos > M_maxpos) ? 0 : M_str[M_pos++]; } void store(int& saved_pos) { saved_pos = M_pos; } void restore(int saved_pos) { M_pos = saved_pos; M_result = true; } void add_substitution(int start_pos, substitution_nt sub_type, int number_of_prefixes); bool decode_type_with_postfix(string_type& prefix, string_type& postfix, qualifier_list<Tp, Allocator>* qualifiers = NULL); bool decode_bare_function_type(string_type& output); bool decode_builtin_type(string_type& output); bool decode_call_offset(string_type& output); bool decode_class_enum_type(string_type& output); bool decode_expression(string_type& output); bool decode_literal(string_type& output); bool decode_local_name(string_type& output); bool decode_name(string_type& output, string_type& nested_name_qualifiers); bool decode_nested_name(string_type& output, string_type& qualifiers); bool decode_number(string_type& output); bool decode_operator_name(string_type& output); bool decode_source_name(string_type& output); bool decode_substitution(string_type& output, qualifier_list<Tp, Allocator>* qualifiers = NULL); bool decode_template_args(string_type& output); bool decode_template_param(string_type& output, qualifier_list<Tp, Allocator>* qualifiers = NULL); bool decode_unqualified_name(string_type& output); bool decode_unscoped_name(string_type& output); bool decode_non_negative_decimal_integer(string_type& output); bool decode_special_name(string_type& output); bool decode_real(string_type& output, size_t size_of_real); }; template<typename Tp, typename Allocator> #if !_GLIBCXX_DEMANGLER_CWDEBUG inline #endif void session<Tp, Allocator>::add_substitution(int start_pos, substitution_nt sub_type, int number_of_prefixes = 0) { if (!M_inside_substitution) { #if _GLIBCXX_DEMANGLER_CWDEBUG if (M_inside_add_substitution) return; #endif M_substitutions_pos. push_back(substitution_st(start_pos, sub_type, number_of_prefixes)); #if _GLIBCXX_DEMANGLER_CWDEBUG if (!DEBUGCHANNELS::dc::demangler.is_on()) return; string_type substitution_name("S"); int n = M_substitutions_pos.size() - 1; if (n > 0) substitution_name += (n <= 10) ? (char)(n + '0' - 1) : (char)(n + 'A' - 11); substitution_name += '_'; string_type subst; int saved_pos = M_pos; M_pos = start_pos; M_inside_add_substitution = true; _GLIBCXX_DEMANGLER_DEBUG( dc::demangler.off() ); switch(sub_type) { case type: decode_type(subst); break; case template_template_param: decode_template_param(subst); break; case nested_name_prefix: case nested_name_template_prefix: for (int cnt = number_of_prefixes; cnt > 0; --cnt) { if (current() == 'I') { subst += ' '; decode_template_args(subst); } else { if (cnt < number_of_prefixes) subst += "::"; if (current() == 'S') decode_substitution(subst); else if (current() == 'T') decode_template_param(subst); else decode_unqualified_name(subst); } } break; case unscoped_template_name: decode_unscoped_name(subst); break; } M_pos = saved_pos; _GLIBCXX_DEMANGLER_DEBUG( dc::demangler.on() ); _GLIBCXX_DEMANGLER_DOUT(dc::demangler, "Adding substitution " << substitution_name << " : " << subst << " (from " << location_ct((char*)__builtin_return_address(0) + builtin_return_address_offset) << " <- " << location_ct((char*)__builtin_return_address(1) + builtin_return_address_offset) << " <- " << location_ct((char*)__builtin_return_address(2) + builtin_return_address_offset) << ")."); M_inside_add_substitution = false; #endif } } // We don't want to depend on locale (or include <cctype> for that matter). // We also don't want to use "safe-ctype.h" because that headerfile is not // available to the users. inline bool isdigit(char c) { return c >= '0' && c <= '9'; } inline bool islower(char c) { return c >= 'a' && c <= 'z'; } inline bool isupper(char c) { return c >= 'A' && c <= 'Z'; } inline char tolower(char c) { return isupper(c) ? c - 'A' + 'a' : c; } // // <non-negative decimal integer> ::= 0 // ::= 1|2|3|4|5|6|7|8|9 [<digit>+] // <digit> ::= 0|1|2|3|4|5|6|7|8|9 // template<typename Tp, typename Allocator> bool session<Tp, Allocator>:: decode_non_negative_decimal_integer(string_type& output) { char c = current(); if (c == '0') { output += '0'; eat_current(); } else if (!isdigit(c)) M_result = false; else { do { output += c; } while (isdigit((c = next()))); } return M_result; } // <number> ::= [n] <non-negative decimal integer> // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_number(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_number"); if (current() != 'n') decode_non_negative_decimal_integer(output); else { output += '-'; eat_current(); decode_non_negative_decimal_integer(output); } _GLIBCXX_DEMANGLER_RETURN; } // <builtin-type> ::= v # void // ::= w # wchar_t // ::= b # bool // ::= c # char // ::= a # signed char // ::= h # unsigned char // ::= s # short // ::= t # unsigned short // ::= i # int // ::= j # unsigned int // ::= l # long // ::= m # unsigned long // ::= x # long long, __int64 // ::= y # unsigned long long, __int64 // ::= n # __int128 // ::= o # unsigned __int128 // ::= f # float // ::= d # double // ::= e # long double, __float80 // ::= g # __float128 // ::= z # ellipsis // ::= u <source-name> # vendor extended type // char const* const builtin_type_c[26] = { "signed char", // a "bool", // b "char", // c "double", // d "long double", // e "float", // f "__float128", // g "unsigned char", // h "int", // i "unsigned int", // j NULL, // k "long", // l "unsigned long", // m "__int128", // n "unsigned __int128", // o NULL, // p NULL, // q NULL, // r "short", // s "unsigned short", // t NULL, // u "void", // v "wchar_t", // w "long long", // x "unsigned long long", // y "..." // z }; // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_builtin_type(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_builtin_type"); char const* bt; if (!islower(current()) || !(bt = builtin_type_c[current() - 'a'])) _GLIBCXX_DEMANGLER_FAILURE; output += bt; eat_current(); _GLIBCXX_DEMANGLER_RETURN; } // <class-enum-type> ::= <name> // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_class_enum_type(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_class_enum_type"); string_type nested_name_qualifiers; if (!decode_name(output, nested_name_qualifiers)) _GLIBCXX_DEMANGLER_FAILURE; output += nested_name_qualifiers; _GLIBCXX_DEMANGLER_RETURN; } // <substitution> ::= // S <seq-id> _ // S_ // St # ::std:: // Sa # ::std::allocator // Sb # ::std::basic_string // Ss # ::std::basic_string<char, std::char_traits<char>, // std::allocator<char> > // Si # ::std::basic_istream<char, std::char_traits<char> > // So # ::std::basic_ostream<char, std::char_traits<char> > // Sd # ::std::basic_iostream<char, std::char_traits<char> > // // <seq-id> ::= // 0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z // [<seq-id>] # Base 36 number // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_substitution(string_type& output, qualifier_list<Tp, Allocator>* qualifiers) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_substitution"); unsigned int value = 0; char c = next(); if (c != '_') { switch(c) { case 'a': { output += "std::allocator"; if (!M_inside_template_args) { M_function_name = "allocator"; M_name_is_template = true; M_name_is_cdtor = false; M_name_is_conversion_operator = false; } eat_current(); if (qualifiers) qualifiers->printing_suppressed(); _GLIBCXX_DEMANGLER_RETURN; } case 'b': { output += "std::basic_string"; if (!M_inside_template_args) { M_function_name = "basic_string"; M_name_is_template = true; M_name_is_cdtor = false; M_name_is_conversion_operator = false; } eat_current(); if (qualifiers) qualifiers->printing_suppressed(); _GLIBCXX_DEMANGLER_RETURN; } case 'd': output += "std::iostream"; if (!M_inside_template_args) { M_function_name = "iostream"; M_name_is_template = true; M_name_is_cdtor = false; M_name_is_conversion_operator = false; } eat_current(); if (qualifiers) qualifiers->printing_suppressed(); _GLIBCXX_DEMANGLER_RETURN; case 'i': output += "std::istream"; if (!M_inside_template_args) { M_function_name = "istream"; M_name_is_template = true; M_name_is_cdtor = false; M_name_is_conversion_operator = false; } eat_current(); if (qualifiers) qualifiers->printing_suppressed(); _GLIBCXX_DEMANGLER_RETURN; case 'o': output += "std::ostream"; if (!M_inside_template_args) { M_function_name = "ostream"; M_name_is_template = true; M_name_is_cdtor = false; M_name_is_conversion_operator = false; } eat_current(); if (qualifiers) qualifiers->printing_suppressed(); _GLIBCXX_DEMANGLER_RETURN; case 's': output += "std::string"; if (!M_inside_template_args) { M_function_name = "string"; M_name_is_template = true; M_name_is_cdtor = false; M_name_is_conversion_operator = false; } eat_current(); if (qualifiers) qualifiers->printing_suppressed(); _GLIBCXX_DEMANGLER_RETURN; case 't': output += "std"; eat_current(); if (qualifiers) qualifiers->printing_suppressed(); _GLIBCXX_DEMANGLER_RETURN; default: for(;; c = next()) { if (isdigit(c)) value = value * 36 + c - '0'; else if (isupper(c)) value = value * 36 + c - 'A' + 10; else if (c == '_') break; else _GLIBCXX_DEMANGLER_FAILURE; } ++value; break; } } eat_current(); if (value >= M_substitutions_pos.size() || M_inside_type > 20) // Rather than core dump. _GLIBCXX_DEMANGLER_FAILURE; ++M_inside_substitution; int saved_pos = M_pos; substitution_st& substitution(M_substitutions_pos[value]); M_pos = substitution.M_start_pos; switch(substitution.M_type) { case type: decode_type(output, qualifiers); break; case template_template_param: decode_template_param(output, qualifiers); break; case nested_name_prefix: case nested_name_template_prefix: for (int cnt = substitution.M_number_of_prefixes; cnt > 0; --cnt) { if (current() == 'I') { if (M_template_args_need_space) output += ' '; M_template_args_need_space = false; if (!decode_template_args(output)) _GLIBCXX_DEMANGLER_FAILURE; } else { if (cnt < substitution.M_number_of_prefixes) output += "::"; if (current() == 'S') { if (!decode_substitution(output)) _GLIBCXX_DEMANGLER_FAILURE; } else if (!decode_unqualified_name(output)) _GLIBCXX_DEMANGLER_FAILURE; } } if (qualifiers) qualifiers->printing_suppressed(); break; case unscoped_template_name: decode_unscoped_name(output); if (qualifiers) qualifiers->printing_suppressed(); break; } M_pos = saved_pos; --M_inside_substitution; _GLIBCXX_DEMANGLER_RETURN; } // <template-param> ::= T_ # first template parameter // ::= T <parameter-2 non-negative number> _ // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_template_param(string_type& output, qualifier_list<Tp, Allocator>* qualifiers) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_template_parameter"); if (current() != 'T') _GLIBCXX_DEMANGLER_FAILURE; unsigned int value = 0; char c; if ((c = next()) != '_') { while(isdigit(c)) { value = value * 10 + c - '0'; c = next(); } ++value; } if (eat_current() != '_') _GLIBCXX_DEMANGLER_FAILURE; value += M_template_arg_pos_offset; if (value >= M_template_arg_pos.size()) _GLIBCXX_DEMANGLER_FAILURE; int saved_pos = M_pos; M_pos = M_template_arg_pos[value]; if (M_inside_type > 20) // Rather than core dump. _GLIBCXX_DEMANGLER_FAILURE; ++M_inside_substitution; if (current() == 'X') { eat_current(); decode_expression(output); } else if (current() == 'L') decode_literal(output); else decode_type(output, qualifiers); --M_inside_substitution; M_pos = saved_pos; _GLIBCXX_DEMANGLER_RETURN; } template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_real(string_type& output, size_t size_of_real) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_real"); unsigned long words[4]; // 32 bit per long, maximum of 128 bits. unsigned long* word = &words[0]; int saved_pos; store(saved_pos); // The following assumes that leading zeroes are also included in the // mangled name, I am not sure that is conforming to the C++-ABI, but // it is what g++ does. unsigned char nibble, c = current(); for(size_t word_cnt = size_of_real / 4; word_cnt > 0; --word_cnt) { for (int nibble_cnt = 0; nibble_cnt < 8; ++nibble_cnt) { // Translate character into nibble. if (c < '0' || c > 'f') _GLIBCXX_DEMANGLER_FAILURE; if (c <= '9') nibble = c - '0'; else if (c >= 'a') nibble = c - 'a' + 10; else _GLIBCXX_DEMANGLER_FAILURE; // Write nibble into word array. if (nibble_cnt == 0) *word = nibble << 28; else *word |= (nibble << (28 - 4 * nibble_cnt)); c = next(); } ++word; } char buf[24]; if (M_implementation_details.decode_real(buf, words, size_of_real)) { output += buf; _GLIBCXX_DEMANGLER_RETURN; } restore(saved_pos); output += '['; c = current(); for(size_t nibble_cnt = 0; nibble_cnt < 2 * size_of_real; ++nibble_cnt) { if (c < '0' || c > 'f' || (c > '9' && c < 'a')) _GLIBCXX_DEMANGLER_FAILURE; output += c; c = next(); } output += ']'; _GLIBCXX_DEMANGLER_RETURN; } template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_literal(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_literal"); eat_current(); // Eat the 'L'. if (current() == '_') { if (next() != 'Z') _GLIBCXX_DEMANGLER_FAILURE; eat_current(); if ((M_pos += decode_encoding(output, M_str + M_pos, M_maxpos - M_pos + 1, M_implementation_details)) < 0) _GLIBCXX_DEMANGLER_FAILURE; } else { // Special cases if (current() == 'b') { if (next() == '0') output += "false"; else output += "true"; eat_current(); _GLIBCXX_DEMANGLER_RETURN; } char c = current(); if ((c == 'i' || c == 'j' || c == 'l' || c == 'm' || c == 'x' || c == 'y') && M_implementation_details.get_style_literal()) eat_current(); else if (c == 'i' && !M_implementation_details.get_style_literal_int()) eat_current(); else { output += '('; if (!decode_type(output)) _GLIBCXX_DEMANGLER_FAILURE; output += ')'; } if (c >= 'd' && c <= 'g') { size_t size_of_real = (c == 'd') ? sizeof(double) : ((c == 'f') ? sizeof(float) : (c == 'e') ? sizeof(long double) : 16); if (!decode_real(output, size_of_real)) _GLIBCXX_DEMANGLER_FAILURE; } else if (!decode_number(output)) _GLIBCXX_DEMANGLER_FAILURE; if (M_implementation_details.get_style_literal()) { if (c == 'j' || c == 'm' || c == 'y') output += 'u'; if (c == 'l' || c == 'm') output += 'l'; if (c == 'x' || c == 'y') output += "ll"; } } _GLIBCXX_DEMANGLER_RETURN; } // <operator-name> ::= // nw # new // na # new[] // dl # delete // da # delete[] // ps # + (unary) // ng # - (unary) // ad # & (unary) // de # * (unary) // co # ~ // pl # + // mi # - // ml # * // dv # / // rm # % // an # & // or # | // eo # ^ // aS # = // pL # += // mI # -= // mL # *= // dV # /= // rM # %= // aN # &= // oR # |= // eO # ^= // ls # << // rs # >> // lS # <<= // rS # >>= // eq # == // ne # != // lt # < // gt # > // le # <= // ge # >= // nt # ! // aa # && // oo # || // pp # ++ // mm # -- // cm # , // pm # ->* // pt # -> // cl # () // ix # [] // qu # ? // st # sizeof (a type) // sz # sizeof (an expression) // cv <type> # (cast) // v <digit> <source-name> # vendor extended operator // // Symbol operator codes exist of two characters, we need to find a // quick hash so that their names can be looked up in a table. // // The puzzle :) // Shift the rows so that there is at most one character per column. // // A perfect solution (Oh no, it's THE MATRIX!): // horizontal // ....................................... offset + 'a' // a, a||d|||||||||n||||s|||||||||||||||||||| 0 // c, || |||||||lm o||| |||||||||||||||||||| 0 // d, || a|||e|| l|| ||||||v||||||||||||| 4 // e, || ||| || || |||o|q ||||||||||||| 8 // g, || ||| || || e|| | ||||||||t|||| 15 // i, || ||| || || || | |||||||| |||x 15 // l, |e ||| || st || | |||||||| ||| -2 // m, | |i| lm || | |||||||| ||| -2 // n, a e g t| w |||||||| ||| 1 // o, | ||||o||r ||| 16 // p, | ||lm |p st| 17 // q, | u| | | 6 // r, m s | | 9 // s, t z 12 // ....................................... // ^ ^__ second character // |___ first character // // Putting that solution in tables: char const offset_table_c [1 + CHAR_MAX - CHAR_MIN ] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, #if (CHAR_MIN < 0) // Add -CHAR_MIN extra zeroes (128): 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a b c d e f g h i j k 0, -97, 0, -97, -93, -89, 0, -82, 0, -82, 0, 0, // l m n o p q r s t u v -99, -99, -96, -81, -80, -91, -88, -85, 0, 0, 0, #else // a b c d e f g h i j k 0, 159, 0, 159, 163, 167, 0, 174, 0, 174, 0, 0, // l m n o p q r s t u v 157, 157, 160, 175, 176, 165, 168, 171, 0, 0, 0, #endif // ... more zeros }; enum xary_nt { unary, binary, trinary }; struct entry_st { char const* opcode; char const* symbol_name; xary_nt type; }; entry_st const symbol_name_table_c[39] = { { "aa", "operator&&", binary }, { "na", "operator new[]", unary }, { "le", "operator<=", binary }, { "ad", "operator&", unary }, { "da", "operator delete[]", unary }, { "ne", "operator!=", binary }, { "mi=", "operator-", binary }, { "ng", "operator-", unary }, { "de", "operator*", unary }, { "ml=", "operator*", binary }, { "mm", "operator--", unary }, { "cl", "operator()", unary }, { "cm", "operator,", binary }, { "an=", "operator&", binary }, { "co", "operator~", binary }, { "dl", "operator delete", unary }, { "ls=", "operator<<", binary }, { "lt", "operator<", binary }, { "as=", "operator", binary }, { "ge", "operator>=", binary }, { "nt", "operator!", unary }, { "rm=", "operator%", binary }, { "eo=", "operator^", binary }, { "nw", "operator new", unary }, { "eq", "operator==", binary }, { "dv=", "operator/", binary }, { "qu", "operator?", trinary }, { "rs=", "operator>>", binary }, { "pl=", "operator+", binary }, { "pm", "operator->*", binary }, { "oo", "operator||", binary }, { "st", "sizeof", unary }, { "pp", "operator++", unary }, { "or=", "operator|", binary }, { "gt", "operator>", binary }, { "ps", "operator+", unary }, { "pt", "operator->", binary }, { "sz", "sizeof", unary }, { "ix", "operator[]", unary } }; template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_operator_name(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_operator_name"); char opcode0 = current(); char opcode1 = tolower(next()); register char hash; if ((hash = offset_table_c[opcode0 - CHAR_MIN])) { hash += opcode1; if ( #if (CHAR_MIN < 0) hash >= 0 && #endif hash < 39) { int index = static_cast<int>(static_cast<unsigned char>(hash)); entry_st entry = symbol_name_table_c[index]; if (entry.opcode[0] == opcode0 && entry.opcode[1] == opcode1 && (opcode1 == current() || entry.opcode[2] == '=')) { output += entry.symbol_name; if (opcode1 != current()) output += '='; eat_current(); if (hash == 16 || hash == 17) M_template_args_need_space = true; _GLIBCXX_DEMANGLER_RETURN; } else if (opcode0 == 'c' && opcode1 == 'v') // casting operator { eat_current(); output += "operator "; if (current() == 'T') { // This is a templated cast operator. // It must be of the form "cvT_I...E". // Let M_template_arg_pos already point // to the template argument. M_template_arg_pos_offset = M_template_arg_pos.size(); M_template_arg_pos.push_back(M_pos + 3); } if (!decode_type(output)) _GLIBCXX_DEMANGLER_FAILURE; if (!M_inside_template_args) M_name_is_conversion_operator = true; _GLIBCXX_DEMANGLER_RETURN; } } } _GLIBCXX_DEMANGLER_FAILURE; } // // <expression> ::= <unary operator-name> <expression> // ::= <binary operator-name> <expression> <expression> // ::= <trinary operator-name> <expression> <expression> <expression> // ::= st <type> // ::= <template-param> // ::= sr <type> <unqualified-name> # dependent name // ::= sr <type> <unqualified-name> <template-args> # dependent template-id // ::= <expr-primary> // // <expr-primary> ::= L <type> <value number> E # integer literal // ::= L <type> <value float> E # floating literal // ::= L <mangled-name> E # external name // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_expression(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_expression"); if (current() == 'T') { if (!decode_template_param(output)) _GLIBCXX_DEMANGLER_FAILURE; _GLIBCXX_DEMANGLER_RETURN; } else if (current() == 'L') { if (!decode_literal(output)) _GLIBCXX_DEMANGLER_FAILURE; if (current() != 'E') _GLIBCXX_DEMANGLER_FAILURE; eat_current(); _GLIBCXX_DEMANGLER_RETURN; } else if (current() == 's') { char opcode1 = next(); if (opcode1 == 't' || opcode1 == 'z') { eat_current(); if (M_implementation_details.get_style_compact_expr_ops()) output += "sizeof("; else output += "sizeof ("; if (opcode1 == 't') { // I cannot think of a mangled name that is valid for both cases // when just replacing the 't' by a 'z' or vica versa, which // indicates that there is no ambiguity that dictates the need // for a seperate "st" case, except to be able catch invalid // mangled names. However there CAN be ambiguity in the demangled // name when there are both a type and a symbol of the same name, // which then leads to different encoding (of course) with // sizeof (type) or sizeof (expression) respectively, but that // ambiguity is not per se related to "sizeof" except that that // is the only place where both a type AND an expression are valid // in as part of a (template function) type. // // Example: // // struct B { typedef int t; }; // struct A : public B { static int t[2]; }; // template<int i, int j> struct C { typedef int q; }; // template<int i, typename T> // void f(typename C<sizeof (typename T::t), // sizeof (T::t)>::q) { } // void instantiate() { f<5, A>(0); } // // Leads to _Z1fILi5E1AEvN1CIXstN1T1tEEXszsrS2_1tEE1qE which // demangles as // void f<5, A>(C<sizeof (T::t), sizeof (T::t)>::q) // // This is ambiguity is very unlikely to happen and it is kind // of fuzzy to detect when adding a 'typename' makes sense. // if (M_implementation_details.get_style_sizeof_typename()) { // We can only get here inside a template parameter, // so this is syntactically correct if the given type is // a typedef. The only disadvantage is that it is inconsistent // with all other places where the 'typename' keyword should be // used and we don't. // With this, the above example will demangle as // void f<5, A>(C<sizeof (typename T::t), sizeof (T::t)>::q) if (current() == 'N' || // <nested-name> // This should be a safe bet. (current() == 'S' && next_peek() == 't')) // std::something, guess that // this involves a typedef. output += "typename "; } if (!decode_type(output)) _GLIBCXX_DEMANGLER_FAILURE; } else { if (!decode_expression(output)) _GLIBCXX_DEMANGLER_FAILURE; } output += ')'; _GLIBCXX_DEMANGLER_RETURN; } else if (current() == 'r') { eat_current(); if (!decode_type(output)) _GLIBCXX_DEMANGLER_FAILURE; output += "::"; if (!decode_unqualified_name(output)) _GLIBCXX_DEMANGLER_FAILURE; if (current() != 'I' || decode_template_args(output)) _GLIBCXX_DEMANGLER_RETURN; } } else { char opcode0 = current(); char opcode1 = tolower(next()); register char hash; if ((hash = offset_table_c[opcode0 - CHAR_MIN])) { hash += opcode1; if ( #if (CHAR_MIN < 0) hash >= 0 && #endif hash < 39) { int index = static_cast<int>(static_cast<unsigned char>(hash)); entry_st entry = symbol_name_table_c[index]; if (entry.opcode[0] == opcode0 && entry.opcode[1] == opcode1 && (opcode1 == current() || entry.opcode[2] == '=')) { char const* op = entry.symbol_name + 8; // Skip "operator". if (*op == ' ') // operator new and delete. ++op; if (entry.type == unary) output += op; bool is_eq = (opcode1 != current()); eat_current(); if (index == 34 && M_inside_template_args) // operator> output += '('; output += '('; if (!decode_expression(output)) _GLIBCXX_DEMANGLER_FAILURE; output += ')'; if (entry.type != unary) { if (!M_implementation_details.get_style_compact_expr_ops()) output += ' '; output += op; if (is_eq) output += '='; if (!M_implementation_details.get_style_compact_expr_ops()) output += ' '; output += '('; if (!decode_expression(output)) _GLIBCXX_DEMANGLER_FAILURE; output += ')'; if (index == 34 && M_inside_template_args) output += ')'; if (entry.type == trinary) { if (M_implementation_details.get_style_compact_expr_ops()) output += ":("; else output += " : ("; if (!decode_expression(output)) _GLIBCXX_DEMANGLER_FAILURE; output += ')'; } } _GLIBCXX_DEMANGLER_RETURN; } else if (opcode0 == 'c' && opcode1 == 'v') // casting operator. { eat_current(); output += '('; if (!decode_type(output)) _GLIBCXX_DEMANGLER_FAILURE; output += ")("; if (!decode_expression(output)) _GLIBCXX_DEMANGLER_FAILURE; output += ')'; _GLIBCXX_DEMANGLER_RETURN; } } } } _GLIBCXX_DEMANGLER_FAILURE; } // // <template-args> ::= I <template-arg>+ E // <template-arg> ::= <type> # type or template // ::= L <type> <value number> E # integer literal // ::= L <type> <value float> E # floating literal // ::= L <mangled-name> E # external name // ::= X <expression> E # expression template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_template_args(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_template_args"); if (eat_current() != 'I') _GLIBCXX_DEMANGLER_FAILURE; int prev_size = M_template_arg_pos.size(); ++M_inside_template_args; if (M_template_args_need_space) { output += ' '; M_template_args_need_space = false; } output += '<'; for(;;) { if (M_inside_template_args == 1 && !M_inside_type) M_template_arg_pos.push_back(M_pos); if (current() == 'X') { eat_current(); if (!decode_expression(output)) _GLIBCXX_DEMANGLER_FAILURE; if (current() != 'E') _GLIBCXX_DEMANGLER_FAILURE; eat_current(); } else if (current() == 'L') { if (!decode_literal(output)) _GLIBCXX_DEMANGLER_FAILURE; if (current() != 'E') _GLIBCXX_DEMANGLER_FAILURE; eat_current(); } else if (!decode_type(output)) _GLIBCXX_DEMANGLER_FAILURE; if (current() == 'E') break; output += ", "; } eat_current(); if (*(output.rbegin()) == '>') output += ' '; output += '>'; --M_inside_template_args; if (!M_inside_template_args && !M_inside_type) { M_name_is_template = true; M_template_arg_pos_offset = prev_size; } _GLIBCXX_DEMANGLER_RETURN; } // <bare-function-type> ::= // <signature type>+ # Types are parameter types. // // Note that the possible return type of the <bare-function-type> // has already been eaten before we call this function. This makes // our <bare-function-type> slightly different from the one in // the C++-ABI description. // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_bare_function_type(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_bare_function_type"); if (M_saw_destructor) { if (eat_current() != 'v' || (current() != 'E' && current() != 0)) _GLIBCXX_DEMANGLER_FAILURE; output += "()"; M_saw_destructor = false; _GLIBCXX_DEMANGLER_RETURN; } if (current() == 'v' && !M_implementation_details.get_style_void()) { eat_current(); if (current() != 'E' && current() != 0) _GLIBCXX_DEMANGLER_FAILURE; output += "()"; M_saw_destructor = false; _GLIBCXX_DEMANGLER_RETURN; } output += '('; M_template_args_need_space = false; if (!decode_type(output)) // Must have at least one parameter. _GLIBCXX_DEMANGLER_FAILURE; while (current() != 'E' && current() != 0) { output += ", "; if (!decode_type(output)) _GLIBCXX_DEMANGLER_FAILURE; } output += ')'; _GLIBCXX_DEMANGLER_RETURN; } // <type> ::= // <builtin-type> # Starts with a lower case character != r. // <function-type> # Starts with F // <class-enum-type> # Starts with N, S, C, D, Z, a digit or a lower // # case character. Since a lower case character // # would be an operator name, that would be an // # error. The S is a substitution or St // # (::std::). A 'C' would be a constructor and // # thus also an error. // <template-param> # Starts with T // <substitution> # Starts with S // <template-template-param> <template-args> # Starts with T or S, // # equivalent with the above. // // <array-type> # Starts with A // <pointer-to-member-type> # Starts with M // <CV-qualifiers> <type> # Starts with r, V or K // P <type> # pointer-to # Starts with P // R <type> # reference-to # Starts with R // C <type> # complex (C 2000) # Starts with C // G <type> # imaginary (C 2000)# Starts with G // U <source-name> <type> # vendor extended type qualifier, // # starts with U // // <template-template-param> ::= <template-param> // ::= <substitution> // My own analysis of how to decode qualifiers: // // F is a <function-type>, <T> is a <builtin-type>, <class-enum-type>, // <template-param> or <template-template-param> <template-args>. // <Q> represents a series of qualifiers (not G or C). // <C> is an unqualified type. // <R> is a qualified type. // <B> is the bare-function-type without return type. // <I> is the array index. // Substitutions: // <Q>M<C><Q2>F<R><B>E ==> R (C::*Q)B Q2 "<C>", "F<R><B>E" // (<R> and <B> recursive), // "M<C><Q2>F<R><B>E". // <Q>F<R><B>E ==> R (Q)B "<R>", "<B>" (<B> recursive) // and "F<R><B>E". // // Note that if <R> has postfix qualifiers (an array or function), then // those are added AFTER the (member) function type. For example: // <Q>FPA<R><B>E ==> R (*(Q)B) [], where the PA added the prefix // "(*" and the postfix ") []". // // <Q>G<T> ==> imaginary T Q "<T>", "G<T>" (<T> recursive). // <Q>C<T> ==> complex T Q "<T>", "C<T>" (<T> recursive). // <Q><T> ==> T Q "<T>" (<T> recursive). // // where <Q> is any of: // // <Q>P ==> *Q "P..." // <Q>R ==> &Q "R..." // <Q>[K|V|r]+ ==> [ const| volatile| restrict]+Q "KVr..." // <Q>U<S> ==> SQ "U<S>..." // <Q>M<C> ==> C::*Q "M<C>..." (<C> recurs.) // A<I> ==> [I] "A<I>..." (<I> recurs.) // <Q>A<I> ==> (Q) [I] "A<I>..." (<I> recurs.) // Note that when <Q> ends on an A<I2> then the brackets are omitted // and no space is written between the two: // A<I2>A<I> ==> [I2][I] // If <Q> ends on [KVr]+, which can happen in combination with // substitutions only, then special handling is required, see below. // // A <substitution> is handled with an input position switch during which // new substitutions are turned off. Because recursive handling of types // (and therefore the order in which substitutions must be generated) must // be done left to right, but the generation of Q needs processing right to // left, substitutions per <type> are generated by reading the input left // to right and marking the starts of all substitutions only - implicitly // finishing them at the end of the type. Then the output and real // substitutions are generated. // // The following comment was for the demangling of g++ version 3.0.x. The // mangling (and I believe even the ABI description) have been fixed now // (as of g++ version 3.1). // // g++ 3.0.x only: // The ABI specifies for pointer-to-member function types the format // <Q>M<T>F<R><B>E. In other words, the qualifier <Q2> (see above) is // implicitely contained in <T> instead of explicitly part of the M format. // I am convinced that this is a bug in the ABI. Unfortunately, this is // how we have to demangle things as it has a direct impact on the order // in which substitutions are stored. This ill-formed design results in // rather ill-formed demangler code too however :/ // // <Q2> is now explicitely part of the M format. // For some weird reason, g++ (3.2.1) does not add substitutions for // qualified member function pointers. I think that is another bug. // // In the case of // <Q>A<I> // where <Q> ends on [K|V|r]+ then that part should be processed as // if it was behind the A<I> instead of in front of it. This is // because a constant array of ints is normally always mangled as // an array of constant ints. KVr qualifiers can end up in front // of an array when the array is part of a substitution or template // parameter, but the demangling should still result in the same // syntax; thus KA2_i (const array of ints) must result in the same // demangling as A2_Ki (array of const ints). As a result we must // demangle ...[...[[KVr]+A<I0>][KVr]+A<I1>]...[KVr]+A<In>[KVr]+ // as A<I0>A<I1>...A<In>[KVr]+ where each K, V and r in the series // collapses to a single character at the right of the string. // For example: // VA9_KrA6_KVi --> A9_A6_KVri --> int volatile const restrict [9][6] // Note that substitutions are still added as usual (the translation // to A9_A6_KVri does not really happen). // // This decoding is achieved by delaying the decoding of any sequence // of [KVrA]'s and processing them together in the order: first the // short-circuited KVr part and then the arrays. static int const cvq_K = 1; // Saw at least one K static int const cvq_V = 2; // Saw at least one V static int const cvq_r = 4; // Saw at least one r static int const cvq_A = 8; // Saw at least one A static int const cvq_last = 16; // No remaining qualifiers. static int const cvq_A_cnt = 32; // Bit 5 and higher represent the // number of A's in the series. // In the function below, iter_array points to the first (right most) // A in the series, if any. template<typename Tp, typename Allocator> void qualifier_list<Tp, Allocator>::decode_KVrA( string_type& prefix, string_type& postfix, int cvq, typename qual_vector::const_reverse_iterator const& iter_array) const { _GLIBCXX_DEMANGLER_DOUT_ENTERING3("decode_KVrA"); if ((cvq & cvq_K)) prefix += " const"; if ((cvq & cvq_V)) prefix += " volatile"; if ((cvq & cvq_r)) prefix += " restrict"; if ((cvq & cvq_A)) { int n = cvq >> 5; for (typename qual_vector:: const_reverse_iterator iter = iter_array; iter != M_qualifier_starts.rend(); ++iter) { switch((*iter).first_qualifier()) { case 'K': case 'V': case 'r': break; case 'A': { string_type index = (*iter).get_optional_type(); if (--n == 0 && (cvq & cvq_last)) postfix = " [" + index + "]" + postfix; else if (n > 0) postfix = "[" + index + "]" + postfix; else { prefix += " ("; postfix = ") [" + index + "]" + postfix; } break; } default: _GLIBCXX_DEMANGLER_RETURN3; } } } _GLIBCXX_DEMANGLER_RETURN3; } template<typename Tp, typename Allocator> void qualifier_list<Tp, Allocator>::decode_qualifiers( string_type& prefix, string_type& postfix, bool member_function_pointer_qualifiers = false) const { _GLIBCXX_DEMANGLER_DOUT_ENTERING3("decode_qualifiers"); int cvq = 0; typename qual_vector::const_reverse_iterator iter_array; for(typename qual_vector:: const_reverse_iterator iter = M_qualifier_starts.rbegin(); iter != M_qualifier_starts.rend(); ++iter) { if (!member_function_pointer_qualifiers && !(*iter).part_of_substitution()) { int saved_inside_substitution = M_demangler.M_inside_substitution; M_demangler.M_inside_substitution = 0; M_demangler.add_substitution((*iter).get_start_pos(), type); M_demangler.M_inside_substitution = saved_inside_substitution; } char qualifier_char = (*iter).first_qualifier(); for(; qualifier_char; qualifier_char = (*iter).next_qualifier()) { switch(qualifier_char) { case 'P': if (cvq) { decode_KVrA(prefix, postfix, cvq, iter_array); cvq = 0; } prefix += "*"; break; case 'R': if (cvq) { decode_KVrA(prefix, postfix, cvq, iter_array); cvq = 0; } prefix += "&"; break; case 'K': cvq |= cvq_K; continue; case 'V': cvq |= cvq_V; continue; case 'r': cvq |= cvq_r; continue; case 'A': if (!(cvq & cvq_A)) { cvq |= cvq_A; iter_array = iter; } cvq += cvq_A_cnt; break; case 'M': if (cvq) { decode_KVrA(prefix, postfix, cvq, iter_array); cvq = 0; } prefix += " "; prefix += (*iter).get_optional_type(); prefix += "::*"; break; case 'U': if (cvq) { decode_KVrA(prefix, postfix, cvq, iter_array); cvq = 0; } prefix += " "; prefix += (*iter).get_optional_type(); break; case 'G': // Only here so we added a substitution. break; } break; } } if (cvq) decode_KVrA(prefix, postfix, cvq|cvq_last, iter_array); M_printing_suppressed = false; _GLIBCXX_DEMANGLER_RETURN3; } // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_type_with_postfix( string_type& prefix, string_type& postfix, qualifier_list<Tp, Allocator>* qualifiers) { _GLIBCXX_DEMANGLER_DOUT_ENTERING2("decode_type"); ++M_inside_type; bool recursive_template_param_or_substitution_call; if (!(recursive_template_param_or_substitution_call = qualifiers)) { qualifier_list<Allocator>* raw_qualifiers = M_qualifier_list_alloc.allocate(1); qualifiers = new (raw_qualifiers) qualifier_list<Allocator>(*this); } // First eat all qualifiers. bool failure = false; for(;;) // So we can use 'continue' to eat the next qualifier. { int start_pos = M_pos; switch(current()) { case 'P': qualifiers->add_qualifier_start(pointer, start_pos, M_inside_substitution); eat_current(); continue; case 'R': qualifiers->add_qualifier_start(reference, start_pos, M_inside_substitution); eat_current(); continue; case 'K': case 'V': case 'r': { char c; int count = 0; do { ++count; c = next(); } while(c == 'K' || c == 'V' || c == 'r'); qualifiers->add_qualifier_start(cv_qualifier, start_pos, count, M_inside_substitution); continue; } case 'U': { eat_current(); string_type source_name; if (!decode_source_name(source_name)) { failure = true; break; } qualifiers->add_qualifier_start(vendor_extension, start_pos, source_name, M_inside_substitution); continue; } case 'A': { // <array-type> ::= A <positive dimension number> _ <element type> // ::= A [<dimension expression>] _ <element type> // string_type index; int saved_pos; store(saved_pos); if (next() == 'n' || !decode_number(index)) { restore(saved_pos); if (next() != '_' && !decode_expression(index)) { failure = true; break; } } if (eat_current() != '_') { failure = true; break; } qualifiers->add_qualifier_start(array, start_pos, index, M_inside_substitution); continue; } case 'M': { // <pointer-to-member-type> ::= M <class type> <member type> // <Q>M<C> or <Q>M<C><Q2>F<R><B>E eat_current(); string_type class_type; if (!decode_type(class_type)) // Substitution: "<C>". { failure = true; break; } char c = current(); if (c == 'F' || c == 'K' || c == 'V' || c == 'r') // Must be CV-qualifiers and a member function pointer. { // <Q>M<C><Q2>F<R><B>E ==> R (C::*Q)B Q2 // substitutions: "<C>", "F<R><B>E" (<R> and <B> // recursive), "M<C><Q2>F<R><B>E". int count = 0; int Q2_start_pos = M_pos; while(c == 'K' || c == 'V' || c == 'r') // Decode <Q2>. { ++count; c = next(); } qualifier_list<Tp, Allocator> class_type_qualifiers(*this); if (count) class_type_qualifiers. add_qualifier_start(cv_qualifier, Q2_start_pos, count, M_inside_substitution); string_type member_function_qualifiers; // It is unclear why g++ doesn't add a substitution for // "<Q2>F<R><B>E" as it should I think. string_type member_function_qualifiers_postfix; class_type_qualifiers. decode_qualifiers(member_function_qualifiers, member_function_qualifiers_postfix, true); member_function_qualifiers += member_function_qualifiers_postfix; // I don't think this substitution is actually ever used. int function_pos = M_pos; if (eat_current() != 'F') { failure = true; break; } // Return type. // Constructors, destructors and conversion operators don't // have a return type, but seem to never get here. string_type return_type_postfix; if (!decode_type_with_postfix(prefix, return_type_postfix)) // substitution: <R> recursive { failure = true; break; } prefix += " ("; prefix += class_type; prefix += "::*"; string_type bare_function_type; if (!decode_bare_function_type(bare_function_type) || eat_current() != 'E') // Substitution: <B> recursive. { failure = true; break; } // substitution: "F<R><B>E". add_substitution(function_pos, type); // substitution: "M<C><Q2>F<R><B>E". add_substitution(start_pos, type); // substitution: all qualified types if any. qualifiers->decode_qualifiers(prefix, postfix); postfix += ")"; postfix += bare_function_type; postfix += member_function_qualifiers; postfix += return_type_postfix; goto decode_type_exit; } qualifiers->add_qualifier_start(pointer_to_member, start_pos, class_type, M_inside_substitution); continue; } default: break; } break; } if (!failure) { // <Q>G<T> ==> imaginary T Q // substitutions: "<T>", "G<T>" (<T> recursive). // <Q>C<T> ==> complex T Q // substitutions: "<T>", "C<T>" (<T> recursive). if (current() == 'C' || current() == 'G') { prefix += current() == 'C' ? "complex " : "imaginary "; qualifiers->add_qualifier_start(complex_or_imaginary, M_pos, M_inside_substitution); eat_current(); } int start_pos = M_pos; switch(current()) { case 'F': { // <function-type> ::= F [Y] <bare-function-type> E // // Note that g++ never generates the 'Y', but we try to // demangle it anyway. bool extern_C = (next() == 'Y'); if (extern_C) eat_current(); // <Q>F<R><B>E ==> R (Q)B // substitution: "<R>", "<B>" (<B> recursive) and "F<R><B>E". // Return type. string_type return_type_postfix; if (!decode_type_with_postfix(prefix, return_type_postfix)) // Substitution: "<R>". { failure = true; break; } // Only array and function (pointer) types have a postfix. // In that case we don't want the space but expect something // like prefix is "int (*" and postfix is ") [1]". // We do want the space if this pointer is qualified. if (return_type_postfix.size() == 0 || (prefix.size() > 0 && *prefix.rbegin() != '*')) prefix += ' '; prefix += '('; string_type bare_function_type; if (!decode_bare_function_type(bare_function_type) // substitution: "<B>" (<B> recursive). || eat_current() != 'E') { failure = true; break; } add_substitution(start_pos, type); // Substitution: "F<R><B>E". qualifiers->decode_qualifiers(prefix, postfix); // substitution: all qualified types, if any. postfix += ")"; if (extern_C) postfix += " [extern \"C\"] "; postfix += bare_function_type; postfix += return_type_postfix; break; } case 'T': if (!decode_template_param(prefix, qualifiers)) { failure = true; break; } if (current() == 'I') { add_substitution(start_pos, template_template_param); // substitution: "<template-template-param>". if (!decode_template_args(prefix)) { failure = true; break; } } if (!recursive_template_param_or_substitution_call && qualifiers->suppressed()) { add_substitution(start_pos, type); // substitution: "<template-param>" or // "<template-template-param> <template-args>". qualifiers->decode_qualifiers(prefix, postfix); // substitution: all qualified types, if any. } break; case 'S': if (M_pos >= M_maxpos) { failure = true; break; } if (M_str[M_pos + 1] != 't') { if (!decode_substitution(prefix, qualifiers)) { failure = true; break; } if (current() == 'I') { if (!decode_template_args(prefix)) { failure = true; break; } if (!recursive_template_param_or_substitution_call && qualifiers->suppressed()) add_substitution(start_pos, type); // Substitution: // "<template-template-param> <template-args>". } if (!recursive_template_param_or_substitution_call && qualifiers->suppressed()) qualifiers->decode_qualifiers(prefix, postfix); // Substitution: all qualified types, if any. break; } /* Fall-through for St */ case 'N': case 'Z': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // <Q><T> ==> T Q // substitutions: "<T>" (<T> recursive). if (!decode_class_enum_type(prefix)) { failure = true; break; } if (!recursive_template_param_or_substitution_call) { add_substitution(start_pos, type); // substitution: "<class-enum-type>". qualifiers->decode_qualifiers(prefix, postfix); // substitution: all qualified types, if any. } else qualifiers->printing_suppressed(); break; default: // <Q><T> ==> T Q // substitutions: "<T>" (<T> recursive). if (!decode_builtin_type(prefix)) { failure = true; break; } // If decode_type was called from decode_template_param then we // need to suppress calling qualifiers here in order to get a // substitution added anyway (for the <template-param>). if (!recursive_template_param_or_substitution_call) qualifiers->decode_qualifiers(prefix, postfix); else qualifiers->printing_suppressed(); break; } } decode_type_exit: --M_inside_type; if (!recursive_template_param_or_substitution_call) { qualifiers->~qualifier_list<Allocator>(); M_qualifier_list_alloc.deallocate(qualifiers, 1); } if (failure) _GLIBCXX_DEMANGLER_FAILURE; _GLIBCXX_DEMANGLER_RETURN2; } // <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E // ::= N [<CV-qualifiers>] <template-prefix> <template-args> E // // <prefix> ::= <prefix> <unqualified-name> // ::= <template-prefix> <template-args> // ::= <template-param> // ::= # empty // ::= <substitution> // // <template-prefix> ::= <prefix> <template unqualified-name> // ::= <template-param> // ::= <substitution> // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_nested_name(string_type& output, string_type& qualifiers) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_nested_name"); if (current() != 'N' || M_pos >= M_maxpos) _GLIBCXX_DEMANGLER_FAILURE; // <CV-qualifiers> ::= [r] [V] [K] # restrict (C99), volatile, const char const* qualifiers_start = &M_str[M_pos + 1]; for (char c = next(); c == 'K' || c == 'V' || c == 'r'; c = next()); for (char const* qualifier_ptr = &M_str[M_pos - 1]; qualifier_ptr >= qualifiers_start; --qualifier_ptr) switch(*qualifier_ptr) { case 'K': qualifiers += " const"; break; case 'V': qualifiers += " volatile"; break; case 'r': qualifiers += " restrict"; break; } int number_of_prefixes = 0; int substitution_start = M_pos; for(;;) { ++number_of_prefixes; if (current() == 'S') { if (!decode_substitution(output)) _GLIBCXX_DEMANGLER_FAILURE; } else if (current() == 'I') { if (!decode_template_args(output)) _GLIBCXX_DEMANGLER_FAILURE; if (current() != 'E') { // substitution: "<template-prefix> <template-args>". add_substitution(substitution_start, nested_name_prefix, number_of_prefixes); } } else { if (current() == 'T') { if (!decode_template_param(output)) _GLIBCXX_DEMANGLER_FAILURE; } else if (!decode_unqualified_name(output)) _GLIBCXX_DEMANGLER_FAILURE; if (current() != 'E') { // substitution: "<prefix> <unqualified-name>" or // "<prefix> <template unqualified-name>". add_substitution(substitution_start, (current() == 'I') ? nested_name_template_prefix : nested_name_prefix, number_of_prefixes); } } if (current() == 'E') { eat_current(); _GLIBCXX_DEMANGLER_RETURN; } if (current() != 'I') output += "::"; else if (M_template_args_need_space) output += ' '; M_template_args_need_space = false; } _GLIBCXX_DEMANGLER_FAILURE; } // <local-name> := Z <function encoding> E <entity name> [<discriminator>] // := Z <function encoding> E s [<discriminator>] // <discriminator> := _ <non-negative number> // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_local_name(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_local_name"); if (current() != 'Z' || M_pos >= M_maxpos) _GLIBCXX_DEMANGLER_FAILURE; if ((M_pos += decode_encoding(output, M_str + M_pos + 1, M_maxpos - M_pos, M_implementation_details) + 1) < 0 || eat_current() != 'E') _GLIBCXX_DEMANGLER_FAILURE; output += "::"; if (current() == 's') { eat_current(); output += "string literal"; } else { string_type nested_name_qualifiers; if (!decode_name(output, nested_name_qualifiers)) _GLIBCXX_DEMANGLER_FAILURE; output += nested_name_qualifiers; } string_type discriminator; if (current() == '_' && next() != 'n' && !decode_number(discriminator)) _GLIBCXX_DEMANGLER_FAILURE; _GLIBCXX_DEMANGLER_RETURN; } // <source-name> ::= <positive length number> <identifier> // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_source_name(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_source_name"); int length = current() - '0'; if (length < 1 || length > 9) _GLIBCXX_DEMANGLER_FAILURE; while(isdigit(next())) length = 10 * length + current() - '0'; char const* ptr = &M_str[M_pos]; if (length > 11 && !strncmp(ptr, "_GLOBAL_", 8) && ptr[9] == 'N' && ptr[8] == ptr[10]) { output += "(anonymous namespace)"; if ((M_pos += length) > M_maxpos + 1) _GLIBCXX_DEMANGLER_FAILURE; } else while(length--) { if (current() == 0) _GLIBCXX_DEMANGLER_FAILURE; output += eat_current(); } _GLIBCXX_DEMANGLER_RETURN; } // <unqualified-name> ::= <operator-name> # Starts with lower case. // ::= <ctor-dtor-name> # Starts with 'C' or 'D'. // ::= <source-name> # Starts with a digit. // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_unqualified_name(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_unqualified_name"); if (M_inside_template_args) { if (!decode_source_name(output)) _GLIBCXX_DEMANGLER_FAILURE; } else if (isdigit(current())) { bool recursive_unqualified_name = (&M_function_name == &output); // This can be a recursive call when we are decoding // an <operator-name> that is a cast operator for a some // <unqualified-name>; for example "operator Foo()". // In that case this is thus not a ctor or dtor and we // are not interested in updating M_function_name. if (!recursive_unqualified_name) M_function_name.clear(); M_name_is_template = false; M_name_is_cdtor = false; M_name_is_conversion_operator = false; if (!decode_source_name(M_function_name)) _GLIBCXX_DEMANGLER_FAILURE; if (!recursive_unqualified_name) output += M_function_name; } else if (islower(current())) { M_function_name.clear(); M_name_is_template = false; M_name_is_cdtor = false; M_name_is_conversion_operator = false; if (!decode_operator_name(M_function_name)) _GLIBCXX_DEMANGLER_FAILURE; output += M_function_name; } else if (current() == 'C' || current() == 'D') { // <ctor-dtor-name> ::= // C1 # complete object (in-charge) constructor // C2 # base object (not-in-charge) constructor // C3 # complete object (in-charge) allocating constructor // D0 # deleting (in-charge) destructor // D1 # complete object (in-charge) destructor // D2 # base object (not-in-charge) destructor // if (current() == 'C') { char c = next(); if (c < '1' || c > '3') _GLIBCXX_DEMANGLER_FAILURE; } else { char c = next(); if (c < '0' || c > '2') _GLIBCXX_DEMANGLER_FAILURE; output += '~'; M_saw_destructor = true; } M_name_is_cdtor = true; eat_current(); output += M_function_name; } else _GLIBCXX_DEMANGLER_FAILURE; _GLIBCXX_DEMANGLER_RETURN; } // <unscoped-name> ::= // <unqualified-name> # Starts not with an 'S' // St <unqualified-name> # ::std:: // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_unscoped_name(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_unscoped_name"); if (current() == 'S') { if (next() != 't') _GLIBCXX_DEMANGLER_FAILURE; eat_current(); output += "std::"; } decode_unqualified_name(output); _GLIBCXX_DEMANGLER_RETURN; } // <name> ::= // <nested-name> # Starts with 'N' // <unscoped-template-name> <template-args> # idem // <local-name> # Starts with 'Z' // <unscoped-name> # Starts with 'S', 'C', 'D', // # a digit or a lower case // # character. // // <unscoped-template-name> ::= <unscoped-name> // ::= <substitution> template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_name(string_type& output, string_type& nested_name_qualifiers) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_name"); int substitution_start = M_pos; if (current() == 'S' && (M_pos >= M_maxpos || M_str[M_pos + 1] != 't')) { if (!decode_substitution(output)) _GLIBCXX_DEMANGLER_FAILURE; } else if (current() == 'N') { decode_nested_name(output, nested_name_qualifiers); _GLIBCXX_DEMANGLER_RETURN; } else if (current() == 'Z') { decode_local_name(output); _GLIBCXX_DEMANGLER_RETURN; } else if (!decode_unscoped_name(output)) _GLIBCXX_DEMANGLER_FAILURE; if (current() == 'I') { // Must have been an <unscoped-template-name>. add_substitution(substitution_start, unscoped_template_name); if (!decode_template_args(output)) _GLIBCXX_DEMANGLER_FAILURE; } M_template_args_need_space = false; _GLIBCXX_DEMANGLER_RETURN; } // <call-offset> ::= h <nv-offset> _ // ::= v <v-offset> _ // <nv-offset> ::= <offset number> // non-virtual base override // // <v-offset> ::= <offset number> _ <virtual offset number> // virtual base override, with vcall offset template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_call_offset(string_type& #if _GLIBCXX_DEMANGLER_CWDEBUG output #endif ) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_call_offset"); if (current() == 'h') { string_type dummy; eat_current(); if (decode_number(dummy) && current() == '_') { eat_current(); _GLIBCXX_DEMANGLER_RETURN; } } else if (current() == 'v') { string_type dummy; eat_current(); if (decode_number(dummy) && current() == '_') { eat_current(); if (decode_number(dummy) && current() == '_') { eat_current(); _GLIBCXX_DEMANGLER_RETURN; } } } _GLIBCXX_DEMANGLER_FAILURE; } // // <special-name> ::= // TV <type> # virtual table // TT <type> # VTT structure (construction // vtable index). // TI <type> # typeinfo structure // TS <type> # typeinfo name (null-terminated // byte string). // GV <object name> # Guard variable for one-time // initialization of static objects in // a local scope. // T <call-offset> <base encoding># base is the nominal target function // of thunk. // Tc <call-offset> <call-offset> <base encoding> # base is the nominal // target function of thunk; first // call-offset is 'this' adjustment; // second call-offset is result // adjustment // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_special_name(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_special_name"); if (current() == 'G') { if (next() != 'V') _GLIBCXX_DEMANGLER_FAILURE; output += "guard variable for "; string_type nested_name_qualifiers; eat_current(); if (!decode_name(output, nested_name_qualifiers)) _GLIBCXX_DEMANGLER_FAILURE; output += nested_name_qualifiers; _GLIBCXX_DEMANGLER_RETURN; } else if (current() != 'T') _GLIBCXX_DEMANGLER_FAILURE; switch(next()) { case 'V': output += "vtable for "; eat_current(); decode_type(output); _GLIBCXX_DEMANGLER_RETURN; case 'T': output += "VTT for "; eat_current(); decode_type(output); _GLIBCXX_DEMANGLER_RETURN; case 'I': output += "typeinfo for "; eat_current(); decode_type(output); _GLIBCXX_DEMANGLER_RETURN; case 'S': output += "typeinfo name for "; eat_current(); decode_type(output); _GLIBCXX_DEMANGLER_RETURN; case 'c': output += "covariant return thunk to "; if (!decode_call_offset(output) || !decode_call_offset(output) || (M_pos += decode_encoding(output, M_str + M_pos, M_maxpos - M_pos + 1, M_implementation_details)) < 0) _GLIBCXX_DEMANGLER_FAILURE; _GLIBCXX_DEMANGLER_RETURN; case 'C': // GNU extension? { string_type first; output += "construction vtable for "; eat_current(); if (!decode_type(first)) _GLIBCXX_DEMANGLER_FAILURE; while(isdigit(current())) eat_current(); if (eat_current() != '_') _GLIBCXX_DEMANGLER_FAILURE; if (!decode_type(output)) _GLIBCXX_DEMANGLER_FAILURE; output += "-in-"; output += first; _GLIBCXX_DEMANGLER_RETURN; } default: if (current() == 'v') output += "virtual thunk to "; else output += "non-virtual thunk to "; if (!decode_call_offset(output) || (M_pos += decode_encoding(output, M_str + M_pos, M_maxpos - M_pos + 1, M_implementation_details)) < 0) _GLIBCXX_DEMANGLER_FAILURE; _GLIBCXX_DEMANGLER_RETURN; } } // <encoding> ::= // <function name> <bare-function-type> # Starts with 'C', 'D', 'N', // 'S', a digit or a lower case // character. // <data name> # Idem. // <special-name> # Starts with 'T' or 'G'. template<typename Tp, typename Allocator> int session<Tp, Allocator>::decode_encoding(string_type& output, char const* in, int len, implementation_details const& id) { #if _GLIBCXX_DEMANGLER_CWDEBUG _GLIBCXX_DEMANGLER_DOUT(dc::demangler, "Output thus far: \"" << output << '"'); string_type input(in, len > 0x40000000 ? strlen(in) : len); _GLIBCXX_DEMANGLER_DOUT( dc::demangler, "Entering decode_encoding(\"" << input << "\")"); #endif if (len <= 0) return INT_MIN; session<Tp, Allocator> demangler_session(in, len, id); string_type nested_name_qualifiers; int saved_pos; demangler_session.store(saved_pos); if (demangler_session.decode_special_name(output)) return demangler_session.M_pos; demangler_session.restore(saved_pos); string_type name; if (!demangler_session.decode_name(name, nested_name_qualifiers)) return INT_MIN; if (demangler_session.current() == 0 || demangler_session.current() == 'E') { output += name; output += nested_name_qualifiers; return demangler_session.M_pos; } // Must have been a <function name>. string_type return_type_postfix; if (demangler_session.M_name_is_template && !(demangler_session.M_name_is_cdtor || demangler_session.M_name_is_conversion_operator)) { // Return type of function if (!demangler_session.decode_type_with_postfix(output, return_type_postfix)) return INT_MIN; output += ' '; } output += name; if (!demangler_session.decode_bare_function_type(output)) return INT_MIN; output += nested_name_qualifiers; output += return_type_postfix; return demangler_session.M_pos; } } // namespace demangler // Public interface template<typename Tp, typename Allocator> struct demangle { typedef typename Allocator::template rebind<char>::other char_Allocator; typedef std::basic_string<char, std::char_traits<char>, char_Allocator> string_type; static string_type symbol(char const* in, demangler::implementation_details const& id); static string_type type(char const* in, demangler::implementation_details const& id); }; // demangle::symbol() // // Demangle `input' which should be a mangled function name as for // instance returned by nm(1). template<typename Tp, typename Allocator> typename demangle<Tp, Allocator>::string_type demangle<Tp, Allocator>::symbol(char const* input, demangler::implementation_details const& id) { // <mangled-name> ::= _Z <encoding> // <mangled-name> ::= _GLOBAL_ _<type>_ <disambiguation part> // <type> can be I or D (GNU extension) typedef demangler::session<Tp, Allocator> demangler_type; string_type result; bool failure = (input[0] != '_'); if (!failure) { if (input[1] == 'G') { if (!strncmp(input, "_GLOBAL__", 9) && (input[9] == 'D' || input[9] == 'I') && input[10] == '_') { if (input[9] == 'D') result.assign("global destructors keyed to ", 28); else result.assign("global constructors keyed to ", 29); // Output the disambiguation part as-is. result += input + 11; } else failure = true; } else if (input[1] == 'Z') { int cnt = demangler_type::decode_encoding(result, input + 2, INT_MAX, id); if (cnt < 0 || input[cnt + 2] != 0) failure = true; } else failure = true; } // Failure to demangle, return the mangled name. if (failure) result.assign(input, strlen(input)); return result; } // demangle::type() // Demangle `input' which must be a zero terminated mangled type // name as for instance returned by std::type_info::name(). template<typename Tp, typename Allocator> typename demangle<Tp, Allocator>::string_type demangle<Tp, Allocator>::type(char const* input, demangler::implementation_details const& id) { std::basic_string<char, std::char_traits<char>, Allocator> result; if (input == NULL) result = "(null)"; else { demangler::session<Tp, Allocator> demangler_session(input, INT_MAX, id); if (!demangler_session.decode_type(result) || demangler_session.remaining_input_characters()) { // Failure to demangle, return the mangled name. result = input; } } return result; } } // namespace __gnu_cxx #endif // __DEMANGLE_H