// $Header: /cvsroot/libcw/src/libcwd/demangle3.cc,v 1.10 2001/09/23 04:13:50 libcw Exp $
//
// Copyright (C) 2001, by
//
// Carlo Wood, Run on IRC <carlo@alinoe.com>
// RSA-1024 0x624ACAD5 1997-01-26 Sign & Encrypt
// Fingerprint16 = 32 EC A7 B6 AC DB 65 A6 F6 F6 55 DD 1C DC FF 61
//
// This file may be distributed under the terms of the Q Public License
// version 1.0 as appearing in the file LICENSE.QPL included in the
// packaging of this file.
//
//
// void demangle_type(char const* in, std::string& out);
//
// where, `in' is a mangled type name as returned by typeid(OBJECT).name(),
// `in' does not have to be null terminated. When `in' is NULL then the string "(null)" is returned.
//
// void demangle_symbol(char const* in, std::string& out);
//
// where, `in' is a mangled symbol name as returned by asymbol::name (where asymbol is defined by libbfd),
// which is the same as `location_st::func'. Note that `in' must be null terminated. When `in' is NULL
// then the string "(null)" is returned.
//
// Currently this file has been tested with gcc-3.0.
//
// The description of how the mangling is done in the new ABI was found on
// http://reality.sgi.com/dehnert_engr/cxx/abi.html#mangling-type:
//
#undef CPPFILTCOMPATIBLE
//#include "sys.h"
#ifdef __GNUC__
#if __GXX_ABI_VERSION > 0
#include <limits>
#include <vector>
#include <string>
#include <ctype.h>
#include "demangle.h"
#undef CWASSERT
#undef NEW
#define CWASSERT(x)
#define NEW(x) new x
#define RETURN return M_result
#define FAILURE do { M_result = false; return false; } while(0)
namespace libcw {
namespace debug {
namespace {
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) { }
};
class demangler_ct {
friend class qualifiers_ct;
private:
char const* M_str;
int M_pos;
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;
bool M_decoding_pointer_to_member_class_type;
std::string M_function_name;
std::vector<int> M_template_arg_pos;
int M_template_arg_pos_offset;
std::vector<substitution_st> M_substitutions_pos;
public:
explicit demangler_ct(char const* in) :
M_str(in), M_pos(0), 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_decoding_pointer_to_member_class_type(false), M_template_arg_pos_offset(0)
{ }
private:
inline char current(void) const { return M_str[M_pos]; }
inline char next(void) { return M_str[++M_pos]; }
inline char eat_current(void) { return M_str[M_pos++]; }
struct demangler_ct& skip(int count) { M_pos += count; return *this; }
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 = 0)
{
if (!M_inside_substitution)
{
M_substitutions_pos.push_back(substitution_st(start_pos, sub_type, number_of_prefixes));
}
}
private:
bool decode_bare_function_type(std::string& output);
bool decode_builtin_type(std::string& output);
bool decode_call_offset(std::string& output);
bool decode_class_enum_type(std::string& output);
bool decode_expression(std::string& output);
bool decode_literal(std::string& output);
bool decode_local_name(std::string& output);
bool decode_name(std::string& output, std::string& nested_name_qualifiers);
bool decode_nested_name(std::string& output, std::string& qualifiers);
bool decode_number(std::string& output);
bool decode_operator_name(std::string& output);
bool decode_source_name(std::string& output);
bool decode_substitution(std::string& output, qualifiers_ct* qualifiers = NULL);
bool decode_template_args(std::string& output);
bool decode_template_param(std::string& output, qualifiers_ct* qualifiers = NULL);
bool decode_unqualified_name(std::string& output);
bool decode_unscoped_name(std::string& output);
inline bool decode_decimal_integer(std::string& output);
inline bool decode_special_name(std::string& output);
public:
static int decode_encoding(char const*, std::string&);
bool decode_type(std::string& output, qualifiers_ct* qualifiers = NULL);
};
enum simple_qualifier_nt {
complex_or_imaginary = 'G',
pointer = 'P',
reference = 'R'
};
enum cv_qualifier_nt {
cv_qualifier = 'K'
};
enum param_qualifier_nt {
vendor_extention = 'U',
array = 'A',
pointer_to_member = 'M'
};
class qualifier_ct {
private:
char M_qualifier1;
char M_qualifier2;
char M_qualifier3;
mutable unsigned char cnt;
std::string M_optional_type;
int M_start_pos;
bool M_part_of_substitution;
public:
qualifier_ct(int start_pos, simple_qualifier_nt qualifier, int inside_substitution) :
M_qualifier1(qualifier),
M_start_pos(start_pos),
M_part_of_substitution(inside_substitution) { }
qualifier_ct(int start_pos, cv_qualifier_nt qualifier, 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_ct(int start_pos, param_qualifier_nt qualifier, std::string optional_type, int inside_substitution) :
M_qualifier1(qualifier),
M_optional_type(optional_type),
M_start_pos(start_pos),
M_part_of_substitution(inside_substitution) { }
int start_pos(void) const { return M_start_pos; }
char first_qualifier(void) const { cnt = 1; return M_qualifier1; }
char next_qualifier(void) const { return (++cnt == 2) ? M_qualifier2 : (cnt == 3) ? M_qualifier3 : 0; }
std::string const& optional_type(void) const { return M_optional_type; }
bool part_of_substitution(void) const { return M_part_of_substitution; }
};
class qualifiers_ct {
private:
bool M_printing_suppressed;
std::vector<qualifier_ct> M_qualifier_starts;
demangler_ct& M_demangler;
public:
qualifiers_ct(demangler_ct& demangler) : M_printing_suppressed(false), M_demangler(demangler) { }
void add_qualifier_start(simple_qualifier_nt qualifier, int start_pos, int inside_substitution)
{ M_qualifier_starts.push_back(qualifier_ct(start_pos, qualifier, inside_substitution)); }
void add_qualifier_start(cv_qualifier_nt qualifier, int start_pos, int count, int inside_substitution)
{ M_qualifier_starts.push_back(qualifier_ct(start_pos, qualifier, &M_demangler.M_str[start_pos], count, inside_substitution)); }
void add_qualifier_start(param_qualifier_nt qualifier, int start_pos, std::string optional_type, int inside_substitution)
{ M_qualifier_starts.push_back(qualifier_ct(start_pos, qualifier, optional_type, inside_substitution)); }
void decode_qualifiers(std::string& output);
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(); }
};
//
// <decimal-integer> ::= 0
// ::= 1|2|3|4|5|6|7|8|9 [<digit>+]
// <digit> ::= 0|1|2|3|4|5|6|7|8|9
//
// {anonymous}::
inline bool demangler_ct::decode_decimal_integer(std::string& 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] <decimal-integer>
//
// {anonymous}::
bool demangler_ct::decode_number(std::string& output)
{
if (current() != 'n')
decode_decimal_integer(output);
else
{
output += '-';
eat_current();
decode_decimal_integer(output);
}
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* builtin_type[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
};
//
// {anonymous}::
bool demangler_ct::decode_builtin_type(std::string& output)
{
char const* bt;
if (!islower(current()) || !(bt = builtin_type[current() - 'a']))
FAILURE;
output += bt;
eat_current();
RETURN;
}
// <class-enum-type> ::= <name>
//
// {anonymous}::
bool demangler_ct::decode_class_enum_type(std::string& output)
{
std::string nested_name_qualifiers;
if (!decode_name(output, nested_name_qualifiers))
FAILURE;
output += nested_name_qualifiers;
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
//
// {anonymous}::
bool demangler_ct::decode_substitution(std::string& output, qualifiers_ct* qualifiers = NULL)
{
CWASSERT( current() == 'S');
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();
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();
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();
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();
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();
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();
RETURN;
case 't':
output += "std";
eat_current();
if (qualifiers)
qualifiers->printing_suppressed();
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
FAILURE;
}
++value;
break;
}
}
eat_current();
if (value >= M_substitutions_pos.size() ||
M_inside_type > 20) // Rather than core dump.
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))
FAILURE;
}
else
{
if (cnt < substitution.M_number_of_prefixes)
output += "::";
if (current() == 'S')
{
if (!decode_substitution(output))
FAILURE;
}
else if (!decode_unqualified_name(output))
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;
RETURN;
}
// <template-param> ::= T_ # first template parameter
// ::= T <parameter-2 non-negative number> _
//
// {anonymous}::
bool demangler_ct::decode_template_param(std::string& output, qualifiers_ct* qualifiers = NULL)
{
if (current() != 'T')
FAILURE;
unsigned int value = 0;
char c;
if ((c = next()) != '_')
{
while(isdigit(c))
{
value = value * 10 + c - '0';
c = next();
}
++value;
}
if (eat_current() != '_')
FAILURE;
value += M_template_arg_pos_offset;
if (value >= M_template_arg_pos.size())
FAILURE;
int saved_pos = M_pos;
M_pos = M_template_arg_pos[value];
if (M_inside_type > 20) // Rather than core dump.
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;
RETURN;
}
bool demangler_ct::decode_literal(std::string& output)
{
eat_current(); // Eat the 'L'.
if (current() == '_')
{
if (next() != 'Z')
FAILURE;
eat_current();
if ((M_pos += decode_encoding(M_str + M_pos, output)) < 0)
FAILURE;
}
else
{
// Special cases
if (current() == 'b')
{
if (next() == '0')
output += "false";
else
output += "true";
eat_current();
RETURN;
}
char c = current();
if (c == 'i' || c == 'j' || c == 'l' || c == 'm' || c == 'x' || c == 'y')
eat_current();
else
{
output += '(';
if (!decode_type(output))
FAILURE;
output += ')';
}
if (!decode_number(output))
FAILURE;
if (c == 'j' || c == 'm' || c == 'y')
output += 'u';
if (c == 'l' || c == 'm')
output += 'l';
if (c == 'x' || c == 'y')
output += "ll";
}
RETURN;
}
// <operator-name> ::= nw # new
// ::= na # new[]
// ::= dl # delete
// ::= da # delete[]
// ::= 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 # ?
// ::= sz # sizeof
// ::= sr # scope resolution (::), see below
// ::= 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:
// horizontal
// ..................................... offset + 'a'
// a, ||a||d|||||||||n||||s|||||||||||||||||| 2
// c, || || ||lm|o||| |||| |||||||||||||||||| -3
// d, || a| |e | ||l |||| |||v|||||||||||||| 3
// e, || | | o q| |||| ||| |||||||||||||| -4
// g, |e | | | t||| ||| |||||||||||||| -3
// i, | | | | ||| ||| ||||||||||x||| 12
// l, | | | e ||| ||| ||st|||||| ||| 9
// m, | | | ||| ||| |i lm|||| ||| 18
// n, a e g ||t |w| | |||| ||| 0
// o, || | | | ||o| r|| 19
// p, lm p | t || | || 6
// q, | || u || 14
// r, | |m |s 20
// s, r z | 6
// .....................................
// ^ ^__ second character
// |___ first character
//
// Putting that solution in tables:
// {anonymous}::
char offset_table[1 + CHAR_MAX - CHAR_MIN /* ascii value of first character */ ] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 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 l m n o p q r s t u v
0, -95, 0,-100, -94,-101, 0,-100, 0, -85, 0, 0, -88, -79, -97, -78, -91, -83, -77, -91, 0, 0, 0,
#else
0, 161, 0, 156, 162, 155, 0, 156, 0, 171, 0, 0, 168, 177, 159, 178, 165, 173, 179, 165, 0, 0, 0,
#endif
// ... more zeros
};
// {anonymous}::
struct entry_st { char const* opcode; char const* symbol_name; bool unary; } symbol_name_table[39] = {
{ "na", "operator new[]", true },
{ "ge", "operator>=", false },
{ "aa", "operator&&", false },
{ "da", "operator delete[]", true },
{ "ne", "operator!=", false },
{ "ad", "operator&", true }, // unary
{ "ng", "operator-", true }, // unary
{ "de", "operator*", true }, // unary
{ "cl", "operator()", true },
{ "cm", "operator,", false },
{ "eo=", "operator^", false },
{ "co", "operator~", false },
{ "eq", "operator==", false },
{ "le", "operator<=", false },
{ "dl", "operator delete", true },
{ "an=", "operator&", false },
{ "gt", "operator>", false },
{ "pl=", "operator+", false },
{ "pm", "operator->*", false },
{ "nt", "operator!", true },
{ "as=", "operator", false },
{ "pp", "operator++", true },
{ "nw", "operator new", true },
{ "sr", "::", true },
{ "dv=", "operator/", false },
{ "pt", "operator->", false },
{ "mi=", "operator-", false },
{ "ls=", "operator<<", false },
{ "lt", "operator<", false },
{ "ml=", "operator*", false },
{ "mm", "operator--", true },
{ "sz", "sizeof", true },
{ "rm=", "operator%", false },
{ "oo", "operator||", false },
{ "qu", "operator?", false },
{ "ix", "operator[]", true },
{ "or=", "operator|", false },
{ "", NULL, false },
{ "rs=", "operator>>", false }
};
// {anonymous}::
bool demangler_ct::decode_operator_name(std::string& output)
{
char opcode0 = current();
char opcode1 = tolower(next());
register char hash;
if ((hash = offset_table[opcode0 - CHAR_MIN]))
{
hash += opcode1;
if (
#if (CHAR_MIN<0)
hash >= 0 &&
#endif
hash < 39)
{
entry_st entry = symbol_name_table[hash];
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 == 27 || hash == 28)
M_template_args_need_space = true;
RETURN;
}
else if (opcode0 == 'c' && opcode1 == 'v')
{
eat_current();
output += "operator ";
if (!decode_type(output))
FAILURE;
if (!M_inside_template_args)
M_name_is_conversion_operator = true;
RETURN;
}
}
}
FAILURE;
}
//
// <expression> ::= <unary operator-name> <expression>
// ::= <binary operator-name> <expression> <expression>
// ::= <expr-primary>
//
// <expr-primary> ::= <template-param> # Starts with a T
// ::= L <type> <value number> E # literal
// ::= L <mangled-name> E # external name
//
// {anonymous}::
bool demangler_ct::decode_expression(std::string& output)
{
if (current() == 'T')
{
if (!decode_template_param(output))
FAILURE;
RETURN;
}
else if (current() == 'L')
{
if (!decode_literal(output))
FAILURE;
if (current() != 'E')
FAILURE;
eat_current();
RETURN;
}
else
{
char opcode0 = current();
char opcode1 = tolower(next());
register char hash;
if ((hash = offset_table[opcode0 - CHAR_MIN]))
{
hash += opcode1;
if (
#if (CHAR_MIN<0)
hash >= 0 &&
#endif
hash < 39)
{
entry_st entry = symbol_name_table[hash];
if (entry.opcode[0] == opcode0 && entry.opcode[1] == opcode1 && (opcode1 == current() || entry.opcode[2] == '='))
{
char const* p = entry.symbol_name;
if (!strncmp("operator", p, 8))
p += 8;
if (*p == ' ')
++p;
if (entry.unary)
output += p;
bool is_eq = (opcode1 != current());
eat_current();
output += '(';
if (!decode_expression(output))
FAILURE;
output += ')';
if (!entry.unary)
{
output += ' ';
output += p;
if (is_eq)
output += '=';
output += ' ';
output += '(';
if (!decode_expression(output))
FAILURE;
output += ')';
}
RETURN;
}
}
}
}
FAILURE;
}
//
// <template-args> ::= I <template-arg>+ E
// <template-arg> ::= <type> # type or template
// ::= L <type> <value number> E # literal
// ::= L_Z <encoding> E # external name
// ::= X <expression> E # expression
// {anonymous}::
bool demangler_ct::decode_template_args(std::string& output)
{
if (eat_current() != 'I')
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))
FAILURE;
if (current() != 'E')
FAILURE;
eat_current();
}
else if (current() == 'L')
{
if (!decode_literal(output))
FAILURE;
if (current() != 'E')
FAILURE;
eat_current();
}
else if (!decode_type(output))
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;
}
RETURN;
}
// <bare-function-type> ::= <signature type>+ # types are parameter types
//
// {anonymous}::
bool demangler_ct::decode_bare_function_type(std::string& output)
{
if (M_saw_destructor)
{
if (eat_current() != 'v' || (current() != 'E' && current() != 0))
FAILURE;
output += "()";
M_saw_destructor = false;
RETURN;
}
output += '(';
M_template_args_need_space = false;
if (!decode_type(output)) // Must have at least one parameter
FAILURE;
while (current() != 'E' && current() != 0)
{
output += ", ";
if (!decode_type(output))
FAILURE;
}
output += ')';
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 pair (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<Q2><C>F<R><B>E ==> R (C::*Q)B Q2 "<C>", "<Q2><C>", "F<R><B>E" (<R> and <B> recursive), "M<Q2><C>F<R><B>E".
// <Q>F<R><B>E ==> R (Q)B "<R>", "<B>" (<B> recursive) and "F<R><B>E".
// <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>..."
// A<I> ==> [I] "A<I>..." (<I> recursive).
// <Q>A<I> ==> (Q) [I] "A<I>..." (<I> recursive).
// <Q>M<C> ==> C::*Q "M<C>..." (<C> recursive).
//
// 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 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 :/
//
void qualifiers_ct::decode_qualifiers(std::string& output)
{
std::string postfix;
for(std::vector<qualifier_ct>::reverse_iterator iter = M_qualifier_starts.rbegin(); iter != M_qualifier_starts.rend();)
{
if (!(*iter).part_of_substitution())
{
int saved_inside_substitution = M_demangler.M_inside_substitution;
M_demangler.M_inside_substitution = 0;
M_demangler.add_substitution((*iter).start_pos(), type);
M_demangler.M_inside_substitution = saved_inside_substitution;
}
char qualifier = (*iter).first_qualifier();
for(; qualifier; qualifier = (*iter).next_qualifier())
{
switch(qualifier)
{
case 'P':
output += "*";
break;
case 'R':
output += "&";
break;
case 'K':
output += " const";
continue;
case 'V':
output += " volatile";
continue;
case 'r':
output += " restrict";
continue;
case 'A':
{
std::string index = (*iter).optional_type();
if (++iter != M_qualifier_starts.rend())
{
output += " (";
postfix = ") [" + index + "]" + postfix;
}
else
{
output += " [";
output += index;
output += "]";
}
break;
}
case 'M':
output += (*iter).optional_type();
output += "::*";
break;
case 'U':
output += " ";
output += (*iter).optional_type();
break;
case 'G': // Only here so we added a substitution.
break;
}
break;
}
if (qualifier != 'A')
++iter;
}
output += postfix;
M_printing_suppressed = false;
}
//
// {anonymous}::
bool demangler_ct::decode_type(std::string& output, qualifiers_ct* qualifiers = NULL)
{
++M_inside_type;
bool recursive_template_param_or_substitution_call;
if (!(recursive_template_param_or_substitution_call = qualifiers))
qualifiers = NEW( qualifiers_ct(*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();
std::string source_name;
if (!decode_source_name(source_name))
{
failure = true;
break;
}
qualifiers->add_qualifier_start(vendor_extention, start_pos, source_name, M_inside_substitution);
continue;
}
case 'A':
{
// <array-type> ::= A <positive dimension number> _ <element type>
// ::= A [<dimension expression>] _ <element type>
//
std::string 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();
qualifiers_ct* class_type_qualifiers = NEW( qualifiers_ct(*this) );
M_decoding_pointer_to_member_class_type = true;
std::string class_type;
if (!decode_type(class_type, class_type_qualifiers) || !class_type_qualifiers->suppressed())
{
delete class_type_qualifiers;
failure = true;
break;
}
M_decoding_pointer_to_member_class_type = false;
if (M_str[start_pos + 1] != 'S' || M_str[start_pos + 2] == 't')
add_substitution(start_pos + 1, type); // substitution: "<C>".
if (class_type_qualifiers->size() > 0) // Must be CV-qualifiers and a member function pointer.
{
// <Q>M<Q2><C>F<R><B>E ==> R (C::*Q)B Q2 "<C>", "<Q2><C>", "F<R><B>E" (<R> and <B> recursive), "M<Q2><C>F<R><B>E".
std::string member_function_qualifiers;
class_type_qualifiers->decode_qualifiers(member_function_qualifiers); // substitution(s): "<Q2><C>".
delete class_type_qualifiers;
if (eat_current() != 'F')
{
failure = true;
break;
}
// Return type. FIXME: constructors, destructors and conversion operators don't have a return type.
if (!decode_type(output)) // substitution: "F<R><B>E" (<R> and <B> recursive).
{
failure = true;
break;
}
output += " (";
output += class_type;
output += "::*";
std::string bare_function_type;
if (!decode_bare_function_type(bare_function_type) || eat_current() != 'E')
{
failure = true;
break;
}
add_substitution(start_pos, type); // substitution: "M<Q2><C>F<R><B>E".
qualifiers->decode_qualifiers(output); // substitution: all qualified types if any.
output += ")";
output += bare_function_type;
output += member_function_qualifiers;
goto decode_type_exit;
}
delete class_type_qualifiers;
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 "<T>", "G<T>" (<T> recursive).
// <Q>C<T> ==> complex T Q "<T>", "C<T>" (<T> recursive).
if (current() == 'C' || current() == 'G')
{
output += 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':
{
// <Q>F<R><B>E ==> R (Q)B "<R>", "<B>" (<B> recursive) and "F<R><B>E".
eat_current();
// Return type. FIXME: constructors, destructors and conversion operators don't have a return type.
if (!decode_type(output)) // substitution: "<R>".
{
failure = true;
break;
}
output += " (";
std::string bare_function_type;
if (!decode_bare_function_type(bare_function_type) || eat_current() != 'E') // substitution: "<B>" (<B> recursive).
{
failure = true;
break;
}
add_substitution(start_pos, type); // substitution: "F<R><B>E"
qualifiers->decode_qualifiers(output); // substitution: all qualified types, if any.
output += ")";
output += bare_function_type;
break;
}
case 'T':
if (!decode_template_param(output, qualifiers))
{
failure = true;
break;
}
if (current() == 'I')
{
add_substitution(start_pos, template_template_param); // substitution: "<template-template-param>".
if (!decode_template_args(output))
{
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>".
if (!M_decoding_pointer_to_member_class_type)
qualifiers->decode_qualifiers(output); // substitution: all qualified types, if any.
}
break;
case 'S':
if (M_str[M_pos + 1] != 't')
{
if (!decode_substitution(output, qualifiers))
{
failure = true;
break;
}
if (current() == 'I')
{
if (!decode_template_args(output))
{
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() && !M_decoding_pointer_to_member_class_type)
qualifiers->decode_qualifiers(output); // 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 "<T>" (<T> recursive).
if (!decode_class_enum_type(output))
{
failure = true;
break;
}
if (!recursive_template_param_or_substitution_call)
{
add_substitution(start_pos, type); // substitution: "<class-enum-type>".
qualifiers->decode_qualifiers(output); // substitution: all qualified types, if any.
}
else
qualifiers->printing_suppressed();
break;
default:
// <Q><T> ==> T Q "<T>" (<T> recursive).
if (!decode_builtin_type(output))
{
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(output);
else
qualifiers->printing_suppressed();
break;
}
}
decode_type_exit:
--M_inside_type;
if (!recursive_template_param_or_substitution_call)
delete qualifiers;
if (failure)
FAILURE;
RETURN;
}
// <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>
// ::= # empty
// ::= <substitution>
//
// <template-prefix> ::= <prefix> <template unqualified-name>
// ::= <substitution>
//
// {anonymous}::
bool demangler_ct::decode_nested_name(std::string& output, std::string& qualifiers)
{
if (current() != 'N')
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))
FAILURE;
}
else if (current() == 'I')
{
if (!decode_template_args(output))
FAILURE;
if (current() != 'E')
add_substitution(substitution_start, nested_name_prefix, number_of_prefixes);
// substitution: "<template-prefix> <template-args>".
}
else
{
if (!decode_unqualified_name(output))
FAILURE;
if (current() != 'E')
add_substitution(substitution_start, (current() == 'I') ? nested_name_template_prefix : nested_name_prefix, number_of_prefixes);
// substitution: "<prefix> <unqualified-name>" or
// "<prefix> <template unqualified-name>".
}
if (current() == 'E')
{
eat_current();
RETURN;
}
if (current() != 'I')
output += "::";
else if (M_template_args_need_space)
output += ' ';
M_template_args_need_space = false;
}
FAILURE;
}
// <local-name> := Z <function encoding> E <entity name> [<discriminator>]
// := Z <function encoding> E s [<discriminator>]
// <discriminator> := _ <non-negative number>
//
// {anonymous}::
bool demangler_ct::decode_local_name(std::string& output)
{
if (current() != 'Z')
FAILURE;
if ((M_pos += decode_encoding(M_str + M_pos + 1, output) + 1) < 0 || eat_current() != 'E')
FAILURE;
output += "::";
if (current() == 's')
{
eat_current();
output += "string literal";
}
else
{
std::string nested_name_qualifiers;
if (!decode_name(output, nested_name_qualifiers))
FAILURE;
output += nested_name_qualifiers;
}
std::string discriminator;
if (current() == '_' && next() != 'n' && !decode_number(discriminator))
FAILURE;
RETURN;
}
// <source-name> ::= <positive length number> <identifier>
//
// {anonymous}::
bool demangler_ct::decode_source_name(std::string& output)
{
int length = current() - '0';
if (length < 1 || length > 9)
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)";
skip(length);
}
else
while(length--)
{
if (current() == 0)
FAILURE;
output += eat_current();
}
RETURN;
}
// <unqualified-name> ::= <operator-name> # Starts with lower case
// ::= <ctor-dtor-name> # Starts with 'C' or 'D'
// ::= <source-name> # Starts with a digit
//
// {anonymous}::
bool demangler_ct::decode_unqualified_name(std::string& output)
{
if (isdigit(current()))
{
if (!M_inside_template_args)
{
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))
FAILURE;
output += M_function_name;
}
else if (!decode_source_name(output))
FAILURE;
RETURN;
}
if (islower(current()))
{
if (!M_inside_template_args)
{
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))
FAILURE;
output += M_function_name;
}
RETURN;
}
if (current() == 'C' || current() == 'D')
{
if (M_inside_template_args)
FAILURE;
// <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
//
// {anonymous}::
if (current() == 'C')
{
char c = next();
if (c < '1' || c > '3')
FAILURE;
}
else
{
char c = next();
if (c < '0' || c > '2')
FAILURE;
output += '~';
M_saw_destructor = true;
}
M_name_is_cdtor = true;
eat_current();
output += M_function_name;
RETURN;
}
FAILURE;
}
// <unscoped-name> ::= <unqualified-name> # Starts not with an 'S'
// ::= St <unqualified-name> # ::std::
//
// {anonymous}::
bool demangler_ct::decode_unscoped_name(std::string& output)
{
if (current() == 'S')
{
if (next() != 't')
FAILURE;
eat_current();
output += "std::";
}
decode_unqualified_name(output);
RETURN;
}
// <name> ::= <nested-name> # Starts with 'N'
// ::= <unscoped-name> # Starts with 'S', 'C', 'D', a digit or a lower case character.
// ::= <unscoped-template-name> <template-args> # idem
// ::= <local-name> # Starts with 'Z'
//
// <unscoped-template-name> ::= <unscoped-name>
// ::= <substitution>
// {anonymous}::
bool demangler_ct::decode_name(std::string& output, std::string& nested_name_qualifiers)
{
int substitution_start = M_pos;
if (current() == 'S' && M_str[M_pos + 1] != 't')
{
if (!decode_substitution(output))
FAILURE;
}
else if (current() == 'N')
{
decode_nested_name(output, nested_name_qualifiers);
RETURN;
}
else if (current() == 'Z')
{
decode_local_name(output);
RETURN;
}
else if (!decode_unscoped_name(output))
FAILURE;
if (current() == 'I')
{
// Must have been an <unscoped-template-name>.
add_substitution(substitution_start, unscoped_template_name);
if (!decode_template_args(output))
FAILURE;
}
M_template_args_need_space = false;
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
//
// {anonymous}::
bool demangler_ct::decode_call_offset(std::string& output)
{
if (current() == 'h')
{
std::string dummy;
eat_current();
if (decode_number(dummy) && current() == '_')
{
eat_current();
RETURN;
}
}
else if (current() == 'v')
{
std::string dummy;
eat_current();
if (decode_number(dummy) && current() == '_')
{
eat_current();
if (decode_number(dummy) && current() == '_')
{
eat_current();
RETURN;
}
}
}
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
//
// {anonymous}::
inline bool demangler_ct::decode_special_name(std::string& output)
{
if (current() == 'G')
{
if (next() != 'V')
FAILURE;
output += "guard variable for ";
std::string nested_name_qualifiers;
eat_current();
if (!decode_name(output, nested_name_qualifiers))
FAILURE;
output += nested_name_qualifiers;
RETURN;
}
else if (current() != 'T')
FAILURE;
switch(next())
{
case 'V':
output += "vtable for ";
eat_current();
decode_type(output);
RETURN;
case 'T':
output += "VTT for ";
eat_current();
decode_type(output);
RETURN;
case 'I':
output += "typeinfo for ";
eat_current();
decode_type(output);
RETURN;
case 'S':
output += "typeinfo name for ";
eat_current();
decode_type(output);
RETURN;
case 'c':
output += "covariant return thunk to ";
if (!decode_call_offset(output)
|| !decode_call_offset(output)
|| (M_pos += decode_encoding(M_str + M_pos, output)) < 0)
FAILURE;
RETURN;
case 'C': // GNU extention?
{
std::string first;
output += "construction vtable for ";
eat_current();
if (!decode_type(first))
FAILURE;
while(isdigit(current()))
eat_current();
if (eat_current() != '_')
FAILURE;
if (!decode_type(output))
FAILURE;
output += "-in-";
output += first;
RETURN;
}
default:
if (current() == 'v')
output += "virtual thunk to ";
else
output += "non-virtual thunk to ";
if (!decode_call_offset(output) || (M_pos += decode_encoding(M_str + M_pos, output)) < 0)
FAILURE;
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'.
//
// {anonymous}::
int demangler_ct::decode_encoding(char const* in, std::string& output)
{
demangler_ct demangler(in);
std::string nested_name_qualifiers;
int saved_pos;
demangler.store(saved_pos);
if (demangler.decode_special_name(output))
return demangler.M_pos;
demangler.restore(saved_pos);
std::string name;
if (!demangler.decode_name(name, nested_name_qualifiers))
return INT_MIN;
if (demangler.current() == 0 || demangler.current() == 'E')
{
output += name;
output += nested_name_qualifiers;
return demangler.M_pos;
}
// Must have been a <function name>.
if (demangler.M_name_is_template && !(demangler.M_name_is_cdtor || demangler.M_name_is_conversion_operator))
{
if (!demangler.decode_type(output)) // Return type of function
return INT_MIN;
output += ' ';
}
output += name;
if (!demangler.decode_bare_function_type(output))
return INT_MIN;
output += nested_name_qualifiers;
return demangler.M_pos;
}
} // namespace
//
// demangle_symbol
//
// Demangle `input' and append to `output'.
// `input' should be a mangled_function_name as for instance returned
// by `libcw::debug::pc_mangled_function_name'.
//
void demangle_symbol(char const* input, std::string& output)
{
if (input == NULL)
{
output += "(null)";
return;
}
//
// <mangled-name> ::= _Z <encoding>
//
int pos;
if (input[0] != '_' || input[1] != 'Z' || (pos = demangler_ct::decode_encoding(input + 2, output) + 2) < 0 || input[pos] != 0)
output.assign(input, strlen(input)); // Failure to demangle, return the mangled name.
}
//
// demangle_type
//
// Demangle `input' and append to `output'.
// `input' should be a mangled type as for returned
// by the stdc++ `typeinfo::name()'.
//
void demangle_type(char const* in, std::string& output)
{
if (in == NULL)
{
output += "(null)";
return;
}
demangler_ct demangler(in);
if (!demangler.decode_type(output))
output.assign(in, strlen(in));
}
} // namespace debug
} // namespace libcw
#include <iostream>
using namespace ::libcw::debug;
char *demangle_type(const char *str)
{
std::string out;
::libcw::debug::demangle_type(str, out);
int n = out.size();
//char *result = new char[n+1];
static char *result = new char[200];
for (int i = 0; i < n; i++)
result[i] = out[i];
result[n] = '\0';
return result;
}
#endif // __GXX_ABI_VERSION > 0
#endif // __GNUC__