📄 demangle.h
字号:
_GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_expression"); if (current() == 'T') { if (!decode_template_param(output)) _GLIBCXX_DEMANGLER_FAILURE; _GLIBCXX_DEMANGLER_RETURN; } else if (current() == 'L') { if (!decode_literal(output)) _GLIBCXX_DEMANGLER_FAILURE; if (current() != 'E') _GLIBCXX_DEMANGLER_FAILURE; eat_current(); _GLIBCXX_DEMANGLER_RETURN; } else if (current() == 's') { char opcode1 = next(); if (opcode1 == 't' || opcode1 == 'z') { eat_current(); if (M_implementation_details.get_style_compact_expr_ops()) output += "sizeof("; else output += "sizeof ("; if (opcode1 == 't') { // I cannot think of a mangled name that is valid for both cases // when just replacing the 't' by a 'z' or vica versa, which // indicates that there is no ambiguity that dictates the need // for a seperate "st" case, except to be able catch invalid // mangled names. However there CAN be ambiguity in the demangled // name when there are both a type and a symbol of the same name, // which then leads to different encoding (of course) with // sizeof (type) or sizeof (expression) respectively, but that // ambiguity is not per se related to "sizeof" except that that // is the only place where both a type AND an expression are valid // in as part of a (template function) type. // // Example: // // struct B { typedef int t; }; // struct A : public B { static int t[2]; }; // template<int i, int j> struct C { typedef int q; }; // template<int i, typename T> // void f(typename C<sizeof (typename T::t), // sizeof (T::t)>::q) { } // void instantiate() { f<5, A>(0); } // // Leads to _Z1fILi5E1AEvN1CIXstN1T1tEEXszsrS2_1tEE1qE which // demangles as // void f<5, A>(C<sizeof (T::t), sizeof (T::t)>::q) // // This is ambiguity is very unlikely to happen and it is kind // of fuzzy to detect when adding a 'typename' makes sense. // if (M_implementation_details.get_style_sizeof_typename()) { // We can only get here inside a template parameter, // so this is syntactically correct if the given type is // a typedef. The only disadvantage is that it is inconsistent // with all other places where the 'typename' keyword should be // used and we don't. // With this, the above example will demangle as // void f<5, A>(C<sizeof (typename T::t), sizeof (T::t)>::q) if (current() == 'N' || // <nested-name> // This should be a safe bet. (current() == 'S' && next_peek() == 't')) // std::something, guess that // this involves a typedef. output += "typename "; } if (!decode_type(output)) _GLIBCXX_DEMANGLER_FAILURE; } else { if (!decode_expression(output)) _GLIBCXX_DEMANGLER_FAILURE; } output += ')'; _GLIBCXX_DEMANGLER_RETURN; } else if (current() == 'r') { eat_current(); if (!decode_type(output)) _GLIBCXX_DEMANGLER_FAILURE; output += "::"; if (!decode_unqualified_name(output)) _GLIBCXX_DEMANGLER_FAILURE; if (current() != 'I' || decode_template_args(output)) _GLIBCXX_DEMANGLER_RETURN; } } else { char opcode0 = current(); char opcode1 = tolower(next()); register char hash; if ((hash = offset_table_c[opcode0 - CHAR_MIN])) { hash += opcode1; if (#if (CHAR_MIN < 0) hash >= 0 &&#endif hash < 39) { int index = static_cast<int>(static_cast<unsigned char>(hash)); entry_st entry = symbol_name_table_c[index]; if (entry.opcode[0] == opcode0 && entry.opcode[1] == opcode1 && (opcode1 == current() || entry.opcode[2] == '=')) { char const* op = entry.symbol_name + 8; // Skip "operator". if (*op == ' ') // operator new and delete. ++op; if (entry.type == unary) output += op; bool is_eq = (opcode1 != current()); eat_current(); if (index == 34 && M_inside_template_args) // operator> output += '('; output += '('; if (!decode_expression(output)) _GLIBCXX_DEMANGLER_FAILURE; output += ')'; if (entry.type != unary) { if (!M_implementation_details.get_style_compact_expr_ops()) output += ' '; output += op; if (is_eq) output += '='; if (!M_implementation_details.get_style_compact_expr_ops()) output += ' '; output += '('; if (!decode_expression(output)) _GLIBCXX_DEMANGLER_FAILURE; output += ')'; if (index == 34 && M_inside_template_args) output += ')'; if (entry.type == trinary) { if (M_implementation_details.get_style_compact_expr_ops()) output += ":("; else output += " : ("; if (!decode_expression(output)) _GLIBCXX_DEMANGLER_FAILURE; output += ')'; } } _GLIBCXX_DEMANGLER_RETURN; } else if (opcode0 == 'c' && opcode1 == 'v') // casting operator. { eat_current(); output += '('; if (!decode_type(output)) _GLIBCXX_DEMANGLER_FAILURE; output += ")("; if (!decode_expression(output)) _GLIBCXX_DEMANGLER_FAILURE; output += ')'; _GLIBCXX_DEMANGLER_RETURN; } } } } _GLIBCXX_DEMANGLER_FAILURE; } // // <template-args> ::= I <template-arg>+ E // <template-arg> ::= <type> # type or template // ::= L <type> <value number> E # integer literal // ::= L <type> <value float> E # floating literal // ::= L <mangled-name> E # external name // ::= X <expression> E # expression template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_template_args(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_template_args"); if (eat_current() != 'I') _GLIBCXX_DEMANGLER_FAILURE; int prev_size = M_template_arg_pos.size(); ++M_inside_template_args; if (M_template_args_need_space) { output += ' '; M_template_args_need_space = false; } output += '<'; for(;;) { if (M_inside_template_args == 1 && !M_inside_type) M_template_arg_pos.push_back(M_pos); if (current() == 'X') { eat_current(); if (!decode_expression(output)) _GLIBCXX_DEMANGLER_FAILURE; if (current() != 'E') _GLIBCXX_DEMANGLER_FAILURE; eat_current(); } else if (current() == 'L') { if (!decode_literal(output)) _GLIBCXX_DEMANGLER_FAILURE; if (current() != 'E') _GLIBCXX_DEMANGLER_FAILURE; eat_current(); } else if (!decode_type(output)) _GLIBCXX_DEMANGLER_FAILURE; if (current() == 'E') break; output += ", "; } eat_current(); if (*(output.rbegin()) == '>') output += ' '; output += '>'; --M_inside_template_args; if (!M_inside_template_args && !M_inside_type) { M_name_is_template = true; M_template_arg_pos_offset = prev_size; } _GLIBCXX_DEMANGLER_RETURN; } // <bare-function-type> ::= // <signature type>+ # Types are parameter types. // // Note that the possible return type of the <bare-function-type> // has already been eaten before we call this function. This makes // our <bare-function-type> slightly different from the one in // the C++-ABI description. // template<typename Tp, typename Allocator> bool session<Tp, Allocator>::decode_bare_function_type(string_type& output) { _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_bare_function_type"); if (M_saw_destructor) { if (eat_current() != 'v' || (current() != 'E' && current() != 0)) _GLIBCXX_DEMANGLER_FAILURE; output += "()"; M_saw_destructor = false; _GLIBCXX_DEMANGLER_RETURN; } if (current() == 'v' && !M_implementation_details.get_style_void()) { eat_current(); if (current() != 'E' && current() != 0) _GLIBCXX_DEMANGLER_FAILURE; output += "()"; M_saw_destructor = false; _GLIBCXX_DEMANGLER_RETURN; } output += '('; M_template_args_need_space = false; if (!decode_type(output)) // Must have at least one parameter. _GLIBCXX_DEMANGLER_FAILURE; while (current() != 'E' && current() != 0) { output += ", "; if (!decode_type(output)) _GLIBCXX_DEMANGLER_FAILURE; } output += ')'; _GLIBCXX_DEMANGLER_RETURN; } // <type> ::= // <builtin-type> # Starts with a lower case character != r. // <function-type> # Starts with F // <class-enum-type> # Starts with N, S, C, D, Z, a digit or a lower // # case character. Since a lower case character // # would be an operator name, that would be an // # error. The S is a substitution or St // # (::std::). A 'C' would be a constructor and // # thus also an error. // <template-param> # Starts with T // <substitution> # Starts with S // <template-template-param> <template-args> # Starts with T or S, // # equivalent with the above. // // <array-type> # Starts with A // <pointer-to-member-type> # Starts with M // <CV-qualifiers> <type> # Starts with r, V or K // P <type> # pointer-to # Starts with P // R <type> # reference-to # Starts with R // C <type> # complex (C 2000) # Starts with C // G <type> # imaginary (C 2000)# Starts with G // U <source-name> <type> # vendor extended type qualifier, // # starts with U // // <template-template-param> ::= <template-param> // ::= <substitution> // My own analysis of how to decode qualifiers: // // F is a <function-type>, <T> is a <builtin-type>, <class-enum-type>, // <template-param> or <template-template-param> <template-args>. // <Q> represents a series of qualifiers (not G or C). // <C> is an unqualified type. // <R> is a qualified type. // <B> is the bare-function-type without return type. // <I> is the array index. // Substitutions: // <Q>M<C><Q2>F<R><B>E ==> R (C::*Q)B Q2 "<C>", "F<R><B>E" // (<R> and <B> recursive), // "M<C><Q2>F<R><B>E". // <Q>F<R><B>E ==> R (Q)B "<R>", "<B>" (<B> recursive) // and "F<R><B>E". // // Note that if <R> has postfix qualifiers (an array or function), then // those are added AFTER the (member) function type. For example: // <Q>FPA<R><B>E ==> R (*(Q)B) [], where the PA added the prefix // "(*" and the postfix ") []". // // <Q>G<T> ==> imaginary T Q "<T>", "G<T>" (<T> recursive). // <Q>C<T> ==> complex T Q "<T>", "C<T>" (<T> recursive). // <Q><T> ==> T Q "<T>" (<T> recursive). // // where <Q> is any of: // // <Q>P ==> *Q "P..." // <Q>R ==> &Q "R..." // <Q>[K|V|r]+ ==> [ const| volatile| restrict]+Q "KVr..." // <Q>U<S> ==> SQ "U<S>..." // <Q>M<C> ==> C::*Q "M<C>..." (<C> recurs.) // A<I> ==> [I] "A<I>..." (<I> recurs.) // <Q>A<I> ==> (Q) [I] "A<I>..." (<I> recurs.) // Note that when <Q> ends on an A<I2> then the brackets are omitted // and no space is written between the two: // A<I2>A<I> ==> [I2][I] // If <Q> ends on [KVr]+, which can happen in combination with // substitutions only, then special handling is required, see below. // // A <substitution> is handled with an input position switch during which // new substitutions are turned off. Because recursive handling of types // (and therefore the order in which substitutions must be generated) must // be done left to right, but the generation of Q needs processing right to // left, substitutions per <type> are generated by reading the input left // to right and marking the starts of all substitutions only - implicitly // finishing them at the end of the type. Then the output and real // substitutions are generated. // // The following comment was for the demangling of g++ version 3.0.x. The // mangling (and I believe even the ABI description) have been fixed now // (as of g++ version 3.1). // // g++ 3.0.x only: // The ABI specifies for pointer-to-member function types the format // <Q>M<T>F<R><B>E. In other words, the qualifier <Q2> (see above) is // implicitely contained in <T> instead of explicitly part of the M format. // I am convinced that this is a bug in the ABI. Unfortunately, this is // how we have to demangle things as it has a direct impact on the order // in which substitutions are stored. This ill-formed design results in // rather ill-formed demangler code too however :/ // // <Q2> is now explicitely part of the M format. // For some weird reason, g++ (3.2.1) does not add substitutions for // qualified member function pointers. I think that is another bug. // // In the case of // <Q>A<I> // where <Q> ends on [K|V|r]+ then that part should be processed as // if it was behind the A<I> instead of in front of it. This is // because a constant array of ints is normally always mangled as // an array of constant ints. KVr qualifiers can end up in front // of an array when the array is part of a substitution or template // parameter, but the demangling should still result in the same // syntax; thus KA2_i (const array of ints) must result in the same // demangling as A2_Ki (array of const ints). As a result we must // demangle ...[...[[KVr]+A<I0>][KVr]+A<I1>]...[KVr]+A<In>[KVr]+ // as A<I0>A<I1>...A<In>[KVr]+ where each K, V and r in the series // collapses to a single character at the right of the string. // For example: // VA9_KrA6_KVi --> A9_A6_KVri --> int volatile const restrict [9][6] // Note that substitutions are still added as usual (the translation // to A9_A6_KVri does not really happen). // // This decoding is achieved by delaying the decoding of any sequence // of [KVrA]'s and processing them together in the order: first the // short-circuited KVr part and then the arrays. static int const cvq_K = 1; // Saw at least one K static int const cvq_V = 2; // Saw at least one V static int const cvq_r = 4; // Saw at least one r static int const cvq_A = 8; // Saw at least one A static int const cvq_last = 16; // No remaining qualifiers. static int const cvq_A_cnt = 32; // Bit 5 and higher represent the // number of A's in the series. // In the function below, iter_array points to the first (right most) // A in the series, if any. template<typename Tp, typename Allocator> void qualifier_list<Tp, Allocator>::decode_KVrA( string_type& prefix, string_type& postfix, int cvq, typename qual_vector::const_reverse_iterator const& iter_array) const { _GLIBCXX_DEMANGLER_DOUT_ENTERING3("decode_KVrA"); if ((cvq & cvq_K)) prefix += " const"; if ((cvq & cvq_V)) prefix += " volatile"; if ((cvq & cvq_r)) prefix += " restrict"; if ((cvq & cvq_A)) { int n = cvq >> 5; for (typename qual_vector:: const_reverse_iterator iter = iter_array; iter != M_qualifier_starts.rend(); ++iter) { switch((*iter).first_qualifier()) { case 'K': case 'V': case 'r': break; case 'A':
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -