📄 demangle.h
字号:
{
output += entry.symbol_name;
if (opcode1 != current())
output += '=';
eat_current();
if (hash == 16 || hash == 17)
M_template_args_need_space = true;
_GLIBCXX_DEMANGLER_RETURN;
}
else if (opcode0 == 'c' && opcode1 == 'v') // casting operator
{
eat_current();
output += "operator ";
if (current() == 'T')
{
// This is a templated cast operator.
// It must be of the form "cvT_I...E".
// Let M_template_arg_pos already point
// to the template argument.
M_template_arg_pos_offset = M_template_arg_pos.size();
M_template_arg_pos.push_back(M_pos + 3);
}
if (!decode_type(output))
_GLIBCXX_DEMANGLER_FAILURE;
if (!M_inside_template_args)
M_name_is_conversion_operator = true;
_GLIBCXX_DEMANGLER_RETURN;
}
}
}
_GLIBCXX_DEMANGLER_FAILURE;
}
//
// <expression> ::= <unary operator-name> <expression>
// ::= <binary operator-name> <expression> <expression>
// ::= <trinary operator-name> <expression> <expression> <expression>
// ::= st <type>
// ::= <template-param>
// ::= sr <type> <unqualified-name> # dependent name
// ::= sr <type> <unqualified-name> <template-args> # dependent template-id
// ::= <expr-primary>
//
// <expr-primary> ::= L <type> <value number> E # integer literal
// ::= L <type> <value float> E # floating literal
// ::= L <mangled-name> E # external name
//
template<typename Tp, typename Allocator>
bool
session<Tp, Allocator>::decode_expression(string_type& output)
{
_GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_expression");
if (current() == 'T')
{
if (!decode_template_param(output))
_GLIBCXX_DEMANGLER_FAILURE;
_GLIBCXX_DEMANGLER_RETURN;
}
else if (current() == 'L')
{
if (!decode_literal(output))
_GLIBCXX_DEMANGLER_FAILURE;
if (current() != 'E')
_GLIBCXX_DEMANGLER_FAILURE;
eat_current();
_GLIBCXX_DEMANGLER_RETURN;
}
else if (current() == 's')
{
char opcode1 = next();
if (opcode1 == 't' || opcode1 == 'z')
{
eat_current();
if (M_implementation_details.get_style_compact_expr_ops())
output += "sizeof(";
else
output += "sizeof (";
if (opcode1 == 't')
{
// I cannot think of a mangled name that is valid for both cases
// when just replacing the 't' by a 'z' or vica versa, which
// indicates that there is no ambiguity that dictates the need
// for a seperate "st" case, except to be able catch invalid
// mangled names. However there CAN be ambiguity in the demangled
// name when there are both a type and a symbol of the same name,
// which then leads to different encoding (of course) with
// sizeof (type) or sizeof (expression) respectively, but that
// ambiguity is not per se related to "sizeof" except that that
// is the only place where both a type AND an expression are valid
// in as part of a (template function) type.
//
// Example:
//
// struct B { typedef int t; };
// struct A : public B { static int t[2]; };
// template<int i, int j> struct C { typedef int q; };
// template<int i, typename T>
// void f(typename C<sizeof (typename T::t),
// sizeof (T::t)>::q) { }
// void instantiate() { f<5, A>(0); }
//
// Leads to _Z1fILi5E1AEvN1CIXstN1T1tEEXszsrS2_1tEE1qE which
// demangles as
// void f<5, A>(C<sizeof (T::t), sizeof (T::t)>::q)
//
// This is ambiguity is very unlikely to happen and it is kind
// of fuzzy to detect when adding a 'typename' makes sense.
//
if (M_implementation_details.get_style_sizeof_typename())
{
// We can only get here inside a template parameter,
// so this is syntactically correct if the given type is
// a typedef. The only disadvantage is that it is inconsistent
// with all other places where the 'typename' keyword should be
// used and we don't.
// With this, the above example will demangle as
// void f<5, A>(C<sizeof (typename T::t), sizeof (T::t)>::q)
if (current() == 'N' || // <nested-name>
// This should be a safe bet.
(current() == 'S' &&
next_peek() == 't')) // std::something, guess that
// this involves a typedef.
output += "typename ";
}
if (!decode_type(output))
_GLIBCXX_DEMANGLER_FAILURE;
}
else
{
if (!decode_expression(output))
_GLIBCXX_DEMANGLER_FAILURE;
}
output += ')';
_GLIBCXX_DEMANGLER_RETURN;
}
else if (current() == 'r')
{
eat_current();
if (!decode_type(output))
_GLIBCXX_DEMANGLER_FAILURE;
output += "::";
if (!decode_unqualified_name(output))
_GLIBCXX_DEMANGLER_FAILURE;
if (current() != 'I' || decode_template_args(output))
_GLIBCXX_DEMANGLER_RETURN;
}
}
else
{
char opcode0 = current();
char opcode1 = tolower(next());
register char hash;
if ((hash = offset_table_c[opcode0 - CHAR_MIN]))
{
hash += opcode1;
if (
#if (CHAR_MIN < 0)
hash >= 0 &&
#endif
hash < 39)
{
int index = static_cast<int>(static_cast<unsigned char>(hash));
entry_st entry = symbol_name_table_c[index];
if (entry.opcode[0] == opcode0 && entry.opcode[1] == opcode1
&& (opcode1 == current() || entry.opcode[2] == '='))
{
char const* op = entry.symbol_name + 8; // Skip "operator".
if (*op == ' ') // operator new and delete.
++op;
if (entry.type == unary)
output += op;
bool is_eq = (opcode1 != current());
eat_current();
if (index == 34 && M_inside_template_args) // operator>
output += '(';
output += '(';
if (!decode_expression(output))
_GLIBCXX_DEMANGLER_FAILURE;
output += ')';
if (entry.type != unary)
{
if (!M_implementation_details.get_style_compact_expr_ops())
output += ' ';
output += op;
if (is_eq)
output += '=';
if (!M_implementation_details.get_style_compact_expr_ops())
output += ' ';
output += '(';
if (!decode_expression(output))
_GLIBCXX_DEMANGLER_FAILURE;
output += ')';
if (index == 34 && M_inside_template_args)
output += ')';
if (entry.type == trinary)
{
if (M_implementation_details.get_style_compact_expr_ops())
output += ":(";
else
output += " : (";
if (!decode_expression(output))
_GLIBCXX_DEMANGLER_FAILURE;
output += ')';
}
}
_GLIBCXX_DEMANGLER_RETURN;
}
else if (opcode0 == 'c' &&
opcode1 == 'v') // casting operator.
{
eat_current();
output += '(';
if (!decode_type(output))
_GLIBCXX_DEMANGLER_FAILURE;
output += ")(";
if (!decode_expression(output))
_GLIBCXX_DEMANGLER_FAILURE;
output += ')';
_GLIBCXX_DEMANGLER_RETURN;
}
}
}
}
_GLIBCXX_DEMANGLER_FAILURE;
}
//
// <template-args> ::= I <template-arg>+ E
// <template-arg> ::= <type> # type or template
// ::= L <type> <value number> E # integer literal
// ::= L <type> <value float> E # floating literal
// ::= L <mangled-name> E # external name
// ::= X <expression> E # expression
template<typename Tp, typename Allocator>
bool
session<Tp, Allocator>::decode_template_args(string_type& output)
{
_GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_template_args");
if (eat_current() != 'I')
_GLIBCXX_DEMANGLER_FAILURE;
int prev_size = M_template_arg_pos.size();
++M_inside_template_args;
if (M_template_args_need_space)
{
output += ' ';
M_template_args_need_space = false;
}
output += '<';
for(;;)
{
if (M_inside_template_args == 1 && !M_inside_type)
M_template_arg_pos.push_back(M_pos);
if (current() == 'X')
{
eat_current();
if (!decode_expression(output))
_GLIBCXX_DEMANGLER_FAILURE;
if (current() != 'E')
_GLIBCXX_DEMANGLER_FAILURE;
eat_current();
}
else if (current() == 'L')
{
if (!decode_literal(output))
_GLIBCXX_DEMANGLER_FAILURE;
if (current() != 'E')
_GLIBCXX_DEMANGLER_FAILURE;
eat_current();
}
else if (!decode_type(output))
_GLIBCXX_DEMANGLER_FAILURE;
if (current() == 'E')
break;
output += ", ";
}
eat_current();
if (*(output.rbegin()) == '>')
output += ' ';
output += '>';
--M_inside_template_args;
if (!M_inside_template_args && !M_inside_type)
{
M_name_is_template = true;
M_template_arg_pos_offset = prev_size;
}
_GLIBCXX_DEMANGLER_RETURN;
}
// <bare-function-type> ::=
// <signature type>+ # Types are parameter types.
//
// Note that the possible return type of the <bare-function-type>
// has already been eaten before we call this function. This makes
// our <bare-function-type> slightly different from the one in
// the C++-ABI description.
//
template<typename Tp, typename Allocator>
bool
session<Tp, Allocator>::decode_bare_function_type(string_type& output)
{
_GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_bare_function_type");
if (M_saw_destructor)
{
if (eat_current() != 'v' || (current() != 'E' && current() != 0))
_GLIBCXX_DEMANGLER_FAILURE;
output += "()";
M_saw_destructor = false;
_GLIBCXX_DEMANGLER_RETURN;
}
if (current() == 'v' && !M_implementation_details.get_style_void())
{
eat_current();
if (current() != 'E' && current() != 0)
_GLIBCXX_DEMANGLER_FAILURE;
output += "()";
M_saw_destructor = false;
_GLIBCXX_DEMANGLER_RETURN;
}
output += '(';
M_template_args_need_space = false;
if (!decode_type(output)) // Must have at least one parameter.
_GLIBCXX_DEMANGLER_FAILURE;
while (current() != 'E' && current() != 0)
{
output += ", ";
if (!decode_type(output))
_GLIBCXX_DEMANGLER_FAILURE;
}
output += ')';
_GLIBCXX_DEMANGLER_RETURN;
}
// <type> ::=
// <builtin-type> # Starts with a lower case character != r.
// <function-type> # Starts with F
// <class-enum-type> # Starts with N, S, C, D, Z, a digit or a lower
// # case character. Since a lower case character
// # would be an operator name, that would be an
// # error. The S is a substitution or St
// # (::std::). A 'C' would be a constructor and
// # thus also an error.
// <template-param> # Starts with T
// <substitution> # Starts with S
// <template-template-param> <template-args> # Starts with T or S,
// # equivalent with the above.
//
// <array-type> # Starts with A
// <pointer-to-member-type> # Starts with M
// <CV-qualifiers> <type> # Starts with r, V or K
// P <type> # pointer-to # Starts with P
// R <type> # reference-to # Starts with R
// C <type> # complex (C 2000) # Starts with C
// G <type> # imaginary (C 2000)# Starts with G
// U <source-name> <type> # vendor extended type qualifier,
// # starts with U
//
// <template-template-param> ::= <template-param>
// ::= <substitution>
// My own analysis of how to decode qualifiers:
//
// F is a <function-type>, <T> is a <builtin-type>, <class-enum-type>,
// <template-param> or <template-template-param> <template-args>.
// <Q> represents a series of qualifiers (not G or C).
// <C> is an unqualified type.
// <R> is a qualified type.
// <B> is the bare-function-type without return type.
// <I> is the array index.
// Substitutions:
// <Q>M<C><Q2>F<R><B>E ==> R (C::*Q)B Q2 "<C>", "F<R><B>E"
// (<R> and <B> recursive),
// "M<C><Q2>F<R><B>E".
// <Q>F<R><B>E ==> R (Q)B "<R>", "<B>" (<B> recursive)
// and "F<R><B>E".
//
// Note that if <R> has postfix qualifiers (an array or function), then
// those are added AFTER the (member) function type. For example:
// <Q>FPA<R><B>E ==> R (*(Q)B) [], where the PA added the prefix
// "(*" and the postfix ") []".
//
// <Q>G<T> ==> imaginary T Q "<T>", "G<T>" (<T> recursive).
// <Q>C<T> ==> complex T Q "<T>", "C<T>" (<T> recursive).
// <Q><T> ==> T Q "<T>" (<T> recursive).
//
// where <Q> is any of:
//
// <Q>P ==> *Q "P..."
// <Q>R ==> &Q "R..."
// <Q>[K|V|r]+ ==> [ const| volatile| restrict]+Q "KVr..."
// <Q>U<S> ==> SQ "U<S>..."
// <Q>M<C> ==> C::*Q "M<C>..." (<C> recurs.)
// A<I> ==> [I] "A<I>..." (<I> recurs.)
// <Q>A<I> ==> (Q) [I] "A<I>..." (<I> recurs.)
// Note that when <Q> ends on an A<I2> then the brackets are omitted
// and no space is written between the two:
// A<I2>A<I> ==> [I2][I]
// If <Q> ends on [KVr]+, which can happen in combination with
// substitutions only, then special handling is required, see below.
//
// A <substitution> is handled with an input position switch during which
// new substitutions are turned off. Because recursive handling of types
// (and therefore the order in which substitutions must be generated) must
// be done left to right, but the generation of Q needs processing right to
// left, substitutions per <type> are generated by reading the input left
// to right and marking the starts of all substitutions only - implicitly
// finishing them at the end of the type. Then the output and real
// substitutions are generated.
//
// The following comment was for the demangling of g++ version 3.0.x. The
// mangling (and I believe even the ABI description) have been fixed now
// (as of g++ version 3.1).
//
// g++ 3.0.x only:
// The ABI specifies for pointer-to-member function types the format
// <Q>M<T>F<R><B>E. In other words, the qualifier <Q2> (see above) is
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -