#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:
static unsigned int const style_void = 1;
static unsigned int const style_literal = 2;
static unsigned int const style_literal_int = 4;
static unsigned int const style_compact_expr_ops = 8;
static unsigned int const style_sizeof_typename = 16;
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); }
virtual bool decode_real(char* , unsigned long* ,
size_t ) 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
}
}
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; }
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;
}
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;
}
char const* const builtin_type_c[26] =
{
"signed char", "bool", "char", "double", "long double", "float", "__float128", "unsigned char", "int", "unsigned int", NULL, "long", "unsigned long", "__int128", "unsigned __int128", NULL, NULL, NULL, "short", "unsigned short", NULL, "void", "wchar_t", "long long", "unsigned long long", "..." };
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;
}
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;
}
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) _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<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) _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]; unsigned long* word = &words[0];
int saved_pos;
store(saved_pos);
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)
{
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;
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(); 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
{
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;
}
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)
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,
0, -97, 0, -97, -93, -89, 0, -82, 0, -82, 0, 0,
-99, -99, -96, -81, -80, -91, -88, -85, 0, 0, 0,
#else
0, 159, 0, 159, 163, 167, 0, 174, 0, 174, 0, 0,
157, 157, 160, 175, 176, 165, 168, 171, 0, 0, 0,
#endif
};
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') {
eat_current();
output += "operator ";
if (current() == 'T')
{
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;
}
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')
{
if (M_implementation_details.get_style_sizeof_typename())
{
if (current() == 'N' || (current() == 'S' &&
next_peek() == 't')) 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; if (*op == ' ') ++op;
if (entry.type == unary)
output += op;
bool is_eq = (opcode1 != current());
eat_current();
if (index == 34 && M_inside_template_args) 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') {
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<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;
}
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)) _GLIBCXX_DEMANGLER_FAILURE;
while (current() != 'E' && current() != 0)
{
output += ", ";
if (!decode_type(output))
_GLIBCXX_DEMANGLER_FAILURE;
}
output += ')';
_GLIBCXX_DEMANGLER_RETURN;
}
static int const cvq_K = 1; static int const cvq_V = 2; static int const cvq_r = 4; static int const cvq_A = 8; static int const cvq_last = 16; static int const cvq_A_cnt = 32; 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': 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);
}
bool failure = false;
for(;;) {
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':
{
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':
{
eat_current();
string_type class_type;
if (!decode_type(class_type)) {
failure = true;
break;
}
char c = current();
if (c == 'F' || c == 'K' || c == 'V' || c == 'r')
{
int count = 0;
int Q2_start_pos = M_pos;
while(c == 'K' || c == 'V' || c == 'r') {
++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;
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;
int function_pos = M_pos;
if (eat_current() != 'F')
{
failure = true;
break;
}
string_type return_type_postfix;
if (!decode_type_with_postfix(prefix, return_type_postfix))
{
failure = true;
break;
}
prefix += " (";
prefix += class_type;
prefix += "::*";
string_type bare_function_type;
if (!decode_bare_function_type(bare_function_type)
|| eat_current() != 'E') {
failure = true;
break;
}
add_substitution(function_pos, type);
add_substitution(start_pos, type);
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)
{
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':
{
bool extern_C = (next() == 'Y');
if (extern_C)
eat_current();
string_type return_type_postfix;
if (!decode_type_with_postfix(prefix, return_type_postfix))
{
failure = true;
break;
}
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)
|| eat_current() != 'E')
{
failure = true;
break;
}
add_substitution(start_pos, type); qualifiers->decode_qualifiers(prefix, postfix);
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);
if (!decode_template_args(prefix))
{
failure = true;
break;
}
}
if (!recursive_template_param_or_substitution_call
&& qualifiers->suppressed())
{
add_substitution(start_pos, type);
qualifiers->decode_qualifiers(prefix, postfix);
}
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);
}
if (!recursive_template_param_or_substitution_call
&& qualifiers->suppressed())
qualifiers->decode_qualifiers(prefix, postfix);
break;
}
case 'N':
case 'Z':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (!decode_class_enum_type(prefix))
{
failure = true;
break;
}
if (!recursive_template_param_or_substitution_call)
{
add_substitution(start_pos, type);
qualifiers->decode_qualifiers(prefix, postfix);
}
else
qualifiers->printing_suppressed();
break;
default:
if (!decode_builtin_type(prefix))
{
failure = true;
break;
}
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;
}
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;
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')
{
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')
{
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;
}
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;
}
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;
}
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);
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')
{
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;
}
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;
}
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')
{
add_substitution(substitution_start, unscoped_template_name);
if (!decode_template_args(output))
_GLIBCXX_DEMANGLER_FAILURE;
}
M_template_args_need_space = false;
_GLIBCXX_DEMANGLER_RETURN;
}
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;
}
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': {
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;
}
}
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;
}
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))
{
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;
}
}
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);
};
template<typename Tp, typename Allocator>
typename demangle<Tp, Allocator>::string_type
demangle<Tp, Allocator>::symbol(char const* input,
demangler::implementation_details const& id)
{
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);
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;
}
if (failure)
result.assign(input, strlen(input));
return result;
}
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())
{
result = input;
}
}
return result;
}
}
#endif // __DEMANGLE_H