| // Copyright (c) 2006, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| // |
| // Author: Satoru Takabayashi |
| // |
| // For reference check out: |
| // http://www.codesourcery.com/public/cxx-abi/abi.html#mangling |
| // |
| // Note that we only have partial C++0x support yet. |
| |
| // DynamoRIO inclusion notes: |
| // |
| // Retreived from http://code.google.com/p/google-glog/ at r91, and altered |
| // since to better fit our use cases in drsyms. |
| // |
| // This file is more or less straight C99, but because eventually we want to use |
| // this for demangling Cygwin names, we will need to compile this with MSVC, |
| // which only accepts C89. Rather than rewrite this file to make it conform to |
| // C89, we continue to compile this as C++. It does *not* use STL or even libc, |
| // so it won't create resource conflicts with the application. |
| // |
| // We also left this file with 2 space indentation to allow us to integrate |
| // future modifications from Google. |
| |
| #include <stdio.h> // for NULL |
| #include "demangle.h" |
| |
| _START_GOOGLE_NAMESPACE_ |
| |
| typedef struct { |
| const char *abbrev; |
| const char *real_name; |
| } AbbrevPair; |
| |
| // List of operators from Itanium C++ ABI. |
| static const AbbrevPair kOperatorList[] = { |
| { "nw", "new" }, |
| { "na", "new[]" }, |
| { "dl", "delete" }, |
| { "da", "delete[]" }, |
| { "ps", "+" }, |
| { "ng", "-" }, |
| { "ad", "&" }, |
| { "de", "*" }, |
| { "co", "~" }, |
| { "pl", "+" }, |
| { "mi", "-" }, |
| { "ml", "*" }, |
| { "dv", "/" }, |
| { "rm", "%" }, |
| { "an", "&" }, |
| { "or", "|" }, |
| { "eo", "^" }, |
| { "aS", "=" }, |
| { "pL", "+=" }, |
| { "mI", "-=" }, |
| { "mL", "*=" }, |
| { "dV", "/=" }, |
| { "rM", "%=" }, |
| { "aN", "&=" }, |
| { "oR", "|=" }, |
| { "eO", "^=" }, |
| { "ls", "<<" }, |
| { "rs", ">>" }, |
| { "lS", "<<=" }, |
| { "rS", ">>=" }, |
| { "eq", "==" }, |
| { "ne", "!=" }, |
| { "lt", "<" }, |
| { "gt", ">" }, |
| { "le", "<=" }, |
| { "ge", ">=" }, |
| { "nt", "!" }, |
| { "aa", "&&" }, |
| { "oo", "||" }, |
| { "pp", "++" }, |
| { "mm", "--" }, |
| { "cm", "," }, |
| { "pm", "->*" }, |
| { "pt", "->" }, |
| { "cl", "()" }, |
| { "ix", "[]" }, |
| { "qu", "?" }, |
| { "st", "sizeof" }, |
| { "sz", "sizeof" }, |
| { NULL, NULL }, |
| }; |
| |
| // List of builtin types from Itanium C++ ABI. |
| static const AbbrevPair kBuiltinTypeList[] = { |
| { "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" }, |
| { "y", "unsigned long long" }, |
| { "n", "__int128" }, |
| { "o", "unsigned __int128" }, |
| { "f", "float" }, |
| { "d", "double" }, |
| { "e", "long double" }, |
| { "g", "__float128" }, |
| { "z", "ellipsis" }, |
| { NULL, NULL } |
| }; |
| |
| // List of substitutions Itanium C++ ABI. |
| static const AbbrevPair kSubstitutionList[] = { |
| { "St", "" }, |
| { "Sa", "allocator" }, |
| { "Sb", "basic_string" }, |
| // std::basic_string<char, std::char_traits<char>,std::allocator<char> > |
| { "Ss", "string"}, |
| // std::basic_istream<char, std::char_traits<char> > |
| { "Si", "istream" }, |
| // std::basic_ostream<char, std::char_traits<char> > |
| { "So", "ostream" }, |
| // std::basic_iostream<char, std::char_traits<char> > |
| { "Sd", "iostream" }, |
| { NULL, NULL } |
| }; |
| |
| // State needed for demangling. |
| typedef struct { |
| const char *mangled_cur; // Cursor of mangled name. |
| const char *mangled_end; // End of mangled name. |
| char *out_cur; // Cursor of output string. |
| const char *out_begin; // Beginning of output string. |
| const char *out_end; // End of output string. |
| const char *prev_name; // For constructors/destructors. |
| int prev_name_length; // For constructors/destructors. |
| int nest_level; // For nested names. |
| int number; // Remember the previous number. |
| bool append; // Append flag. |
| bool overflowed; // True if output gets overflowed. |
| unsigned short options; // Bitfield of options passed to initial Demangle call. |
| } State; |
| |
| // Forward declarations of utility functions. |
| static void MaybeAppendWithLength(State *state, const char * const str, |
| const int length); |
| |
| // We don't use strlen() in libc since it's not guaranteed to be async |
| // signal safe. |
| static size_t StrLen(const char *str) { |
| size_t len = 0; |
| while (*str != '\0') { |
| ++str; |
| ++len; |
| } |
| return len; |
| } |
| |
| // Returns true if "str" has "prefix" as a prefix. |
| static bool StrPrefix(const char *str, const char *prefix) { |
| size_t i = 0; |
| while (str[i] != '\0' && prefix[i] != '\0' && |
| str[i] == prefix[i]) { |
| ++i; |
| } |
| return prefix[i] == '\0'; // Consumed everything in "prefix". |
| } |
| |
| static void InitState(State *state, const char *mangled, |
| char *out, int out_size) { |
| state->mangled_cur = mangled; |
| state->mangled_end = mangled + StrLen(mangled); |
| state->out_cur = out; |
| state->out_begin = out; |
| state->out_end = out + out_size; |
| state->prev_name = NULL; |
| state->prev_name_length = -1; |
| state->nest_level = -1; |
| state->number = -1; |
| state->append = true; |
| state->overflowed = false; |
| } |
| |
| // Calculates the remaining length of the mangled name. |
| static int RemainingLength(State *state) { |
| return (int) (state->mangled_end - state->mangled_cur); |
| } |
| |
| // Returns true and advances "mangled_cur" if we find "c" at |
| // "mangled_cur" position. |
| static bool ParseChar(State *state, const char c) { |
| if (RemainingLength(state) >= 1 && *state->mangled_cur == c) { |
| ++state->mangled_cur; |
| return true; |
| } |
| return false; |
| } |
| |
| // Returns true and advances "mangled_cur" if we find "two_chars" at |
| // "mangled_cur" position. |
| static bool ParseTwoChar(State *state, const char *two_chars) { |
| if (RemainingLength(state) >= 2 && |
| state->mangled_cur[0] == two_chars[0] && |
| state->mangled_cur[1] == two_chars[1]) { |
| state->mangled_cur += 2; |
| return true; |
| } |
| return false; |
| } |
| |
| // Returns true and advances "mangled_cur" if we find any character in |
| // "char_class" at "mangled_cur" position. |
| static bool ParseCharClass(State *state, const char *char_class) { |
| if (state->mangled_cur == state->mangled_end) { |
| return false; |
| } |
| const char *p = char_class; |
| for (; *p != '\0'; ++p) { |
| if (*state->mangled_cur == *p) { |
| state->mangled_cur += 1; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| // This function is used for handling an optional non-terminal. |
| static bool Optional(bool status) { |
| return true; |
| } |
| |
| // This function is used for handling <non-terminal>+ syntax. |
| typedef bool (*ParseFunc)(State *); |
| static bool OneOrMore(ParseFunc parse_func, State *state) { |
| if (parse_func(state)) { |
| MaybeAppendWithLength(state, ", ", 2); |
| while (parse_func(state)) { |
| MaybeAppendWithLength(state, ", ", 2); |
| } |
| if (state->append) { |
| state->out_cur -= 2; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| // This function is used for handling <non-terminal>* syntax. The function |
| // always returns true and must be followed by a termination symbol or a |
| // terminating sequence not handled by parse_func (e.g. ParseChar(state, 'E')). |
| static bool ZeroOrMore(ParseFunc parse_func, State *state) { |
| while (parse_func(state)) { |
| MaybeAppendWithLength(state, ", ", 2); |
| } |
| if (state->append) { |
| state->out_cur -= 2; |
| } |
| return true; |
| } |
| |
| // Append "str" at "out_cur". If there is an overflow, "overflowed" |
| // is set to true for later use. The output string is ensured to |
| // always terminate with '\0' as long as there is no overflow. |
| static void Append(State *state, const char * const str, const int length) { |
| int i; |
| for (i = 0; i < length; ++i) { |
| if (state->out_cur + 1 < state->out_end) { // +1 for '\0' |
| *state->out_cur = str[i]; |
| } else { |
| state->overflowed = true; |
| } |
| // Update out_cur regardless of overflow to track how much space is needed. |
| ++state->out_cur; |
| } |
| if (!state->overflowed) { |
| *state->out_cur = '\0'; // Terminate it with '\0' |
| } |
| } |
| |
| // We don't use equivalents in libc to avoid locale issues. |
| static bool IsLower(char c) { |
| return c >= 'a' && c <= 'z'; |
| } |
| |
| static bool IsAlpha(char c) { |
| return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); |
| } |
| |
| static bool IsDigit(char c) { |
| return c >= '0' && c <= '9'; |
| } |
| |
| // Returns true if "str" is a function clone suffix. These suffixes are used |
| // by GCC 4.5.x and later versions to indicate functions which have been |
| // cloned during optimization. We treat any sequence (.<alpha>+.<digit>+)+ as |
| // a function clone suffix. |
| static bool IsFunctionCloneSuffix(const char *str) { |
| size_t i = 0; |
| while (str[i] != '\0') { |
| // Consume a single .<alpha>+.<digit>+ sequence. |
| if (str[i] != '.' || !IsAlpha(str[i + 1])) { |
| return false; |
| } |
| i += 2; |
| while (IsAlpha(str[i])) { |
| ++i; |
| } |
| if (str[i] != '.' || !IsDigit(str[i + 1])) { |
| return false; |
| } |
| i += 2; |
| while (IsDigit(str[i])) { |
| ++i; |
| } |
| } |
| return true; // Consumed everything in "str". |
| } |
| |
| // Append "str" with some tweaks, iff "append" state is true. |
| // Returns true so that it can be placed in "if" conditions. |
| static void MaybeAppendWithLength(State *state, const char * const str, |
| const int length) { |
| if (state->append && length > 0) { |
| // Append a space if the output buffer ends with '<' and "str" |
| // starts with '<' to avoid <<<. |
| if (str[0] == '<' && |
| state->out_begin < state->out_cur && |
| // If we've overflowed, we can't check if the buffer ends in '<'. |
| // Assume it does to get an upper bound on the size needed to hold the |
| // demangled name. |
| (state->out_cur > state->out_end || |
| state->out_cur[-1] == '<')) { |
| Append(state, " ", 1); |
| } |
| // Remember the last identifier name for ctors/dtors. |
| if (IsAlpha(str[0]) || str[0] == '_') { |
| // We point prev_name at str here instead of state->out_cur because |
| // we might not have space in the output buffer for str. This relies on |
| // us never appending temporary strings, which is true currently, as we |
| // append either string literals, strings in tables, or strings embedded |
| // in the mangled name. |
| state->prev_name = str; |
| state->prev_name_length = length; |
| } |
| Append(state, str, length); |
| } |
| } |
| |
| // A convenient wrapper arount MaybeAppendWithLength(). |
| static bool MaybeAppend(State *state, const char * const str) { |
| if (state->append) { |
| int length = (int) StrLen(str); |
| MaybeAppendWithLength(state, str, length); |
| } |
| return true; |
| } |
| |
| // This function is used for handling nested names. |
| static bool EnterNestedName(State *state) { |
| state->nest_level = 0; |
| return true; |
| } |
| |
| // This function is used for handling nested names. |
| static bool LeaveNestedName(State *state, int prev_value) { |
| state->nest_level = prev_value; |
| return true; |
| } |
| |
| // Disable the append mode not to print function parameters, etc. |
| static bool DisableAppend(State *state) { |
| state->append = false; |
| return true; |
| } |
| |
| // Restore the append mode to the previous state. |
| static bool RestoreAppend(State *state, bool prev_value) { |
| state->append = prev_value; |
| return true; |
| } |
| |
| // Increase the nest level for nested names. |
| static void MaybeIncreaseNestLevel(State *state) { |
| if (state->nest_level > -1) { |
| ++state->nest_level; |
| } |
| } |
| |
| // Appends :: for nested names if necessary. |
| static void MaybeAppendSeparator(State *state) { |
| if (state->nest_level >= 1) { |
| MaybeAppend(state, "::"); |
| } |
| } |
| |
| // Cancel the last separator if necessary. |
| static void MaybeCancelLastSeparator(State *state) { |
| if (state->nest_level >= 1 && state->append && |
| state->out_begin <= state->out_cur - 2) { |
| state->out_cur -= 2; |
| if (state->out_cur < state->out_end) { |
| *state->out_cur = '\0'; |
| } |
| } |
| } |
| |
| // Returns true if identifier pointed by "mangled_cur" is anonymous |
| // namespace. |
| static bool IdentifierIsAnonymousNamespace(State *state) { |
| const char anon_prefix[] = "_GLOBAL__N_"; |
| return (state->number > (int)sizeof(anon_prefix) - 1 && // Should be longer. |
| StrPrefix(state->mangled_cur, anon_prefix)); |
| } |
| |
| // Forward declarations of our parsing functions. |
| static bool ParseMangledName(State *state); |
| static bool ParseEncoding(State *state); |
| static bool ParseName(State *state); |
| static bool ParseUnscopedName(State *state); |
| static bool ParseUnscopedTemplateName(State *state); |
| static bool ParseNestedName(State *state); |
| static bool ParsePrefix(State *state); |
| static bool ParseUnqualifiedName(State *state); |
| static bool ParseSourceName(State *state); |
| static bool ParseLocalSourceName(State *state); |
| static bool ParseNumber(State *state); |
| static bool ParseFloatNumber(State *state); |
| static bool ParseSeqId(State *state); |
| static bool ParseIdentifier(State *state); |
| static bool ParseOperatorName(State *state); |
| static bool ParseSpecialName(State *state); |
| static bool ParseCallOffset(State *state); |
| static bool ParseNVOffset(State *state); |
| static bool ParseVOffset(State *state); |
| static bool ParseCtorDtorName(State *state); |
| static bool ParseType(State *state); |
| static bool ParseCVQualifiers(State *state); |
| static bool ParseBuiltinType(State *state); |
| static bool ParseFunctionType(State *state); |
| static bool ParseBareFunctionType(State *state); |
| static bool ParseClassEnumType(State *state); |
| static bool ParseArrayType(State *state); |
| static bool ParsePointerToMemberType(State *state); |
| static bool ParseTemplateParam(State *state); |
| static bool ParseTemplateTemplateParam(State *state); |
| static bool ParseTemplateArgs(State *state); |
| static bool ParseTemplateArg(State *state); |
| static bool ParseExpression(State *state); |
| static bool ParseExprPrimary(State *state); |
| static bool ParseLocalName(State *state); |
| static bool ParseDiscriminator(State *state); |
| static bool ParseSubstitution(State *state); |
| |
| // Implementation note: the following code is a straightforward |
| // translation of the Itanium C++ ABI defined in BNF with a couple of |
| // exceptions. |
| // |
| // - Support GNU extensions not defined in the Itanium C++ ABI |
| // - <prefix> and <template-prefix> are combined to avoid infinite loop |
| // - Reorder patterns to shorten the code |
| // - Reorder patterns to give greedier functions precedence |
| // We'll mark "Less greedy than" for these cases in the code |
| // |
| // Each parsing function changes the state and returns true on |
| // success. Otherwise, don't change the state and returns false. To |
| // ensure that the state isn't changed in the latter case, we save the |
| // original state before we call more than one parsing functions |
| // consecutively with &&, and restore the state if unsuccessful. See |
| // ParseEncoding() as an example of this convention. We follow the |
| // convention throughout the code. |
| // |
| // Originally we tried to do demangling without following the full ABI |
| // syntax but it turned out we needed to follow the full syntax to |
| // parse complicated cases like nested template arguments. Note that |
| // implementing a full-fledged demangler isn't trivial (libiberty's |
| // cp-demangle.c has +4300 lines). |
| // |
| // Note that (foo) in <(foo) ...> is a modifier to be ignored. |
| // |
| // Reference: |
| // - Itanium C++ ABI |
| // <http://www.codesourcery.com/cxx-abi/abi.html#mangling> |
| |
| // <mangled-name> ::= _Z <encoding> |
| static bool ParseMangledName(State *state) { |
| if (ParseTwoChar(state, "_Z") && ParseEncoding(state)) { |
| // Drop trailing function clone suffix, if any. |
| if (IsFunctionCloneSuffix(state->mangled_cur)) { |
| state->mangled_cur = state->mangled_end; |
| } |
| // Append trailing version suffix if any. |
| // ex. _Z3foo@@GLIBCXX_3.4 |
| if (state->mangled_cur < state->mangled_end && |
| state->mangled_cur[0] == '@') { |
| MaybeAppend(state, state->mangled_cur); |
| state->mangled_cur = state->mangled_end; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| // <encoding> ::= <(function) name> <bare-function-type> |
| // ::= <(data) name> |
| // ::= <special-name> |
| static bool ParseEncoding(State *state) { |
| State copy = *state; |
| if (ParseName(state) && ParseBareFunctionType(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseName(state) || ParseSpecialName(state)) { |
| return true; |
| } |
| return false; |
| } |
| |
| // <name> ::= <nested-name> |
| // ::= <unscoped-template-name> <template-args> |
| // ::= <unscoped-name> |
| // ::= <local-name> |
| static bool ParseName(State *state) { |
| if (ParseNestedName(state) || ParseLocalName(state)) { |
| return true; |
| } |
| |
| State copy = *state; |
| if (ParseUnscopedTemplateName(state) && |
| ParseTemplateArgs(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| // Less greedy than <unscoped-template-name> <template-args>. |
| if (ParseUnscopedName(state)) { |
| return true; |
| } |
| return false; |
| } |
| |
| // <unscoped-name> ::= <unqualified-name> |
| // ::= St <unqualified-name> |
| static bool ParseUnscopedName(State *state) { |
| if (ParseUnqualifiedName(state)) { |
| return true; |
| } |
| |
| State copy = *state; |
| if (ParseTwoChar(state, "St") && |
| MaybeAppend(state, "std::") && |
| ParseUnqualifiedName(state)) { |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <unscoped-template-name> ::= <unscoped-name> |
| // ::= <substitution> |
| static bool ParseUnscopedTemplateName(State *state) { |
| return ParseUnscopedName(state) || ParseSubstitution(state); |
| } |
| |
| // <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E |
| // ::= N [<CV-qualifiers>] <template-prefix> <template-args> E |
| static bool ParseNestedName(State *state) { |
| State copy = *state; |
| if (ParseChar(state, 'N') && |
| EnterNestedName(state) && |
| Optional(ParseCVQualifiers(state)) && |
| ParsePrefix(state) && |
| LeaveNestedName(state, copy.nest_level) && |
| ParseChar(state, 'E')) { |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // This part is tricky. If we literally translate them to code, we'll |
| // end up infinite loop. Hence we merge them to avoid the case. |
| // |
| // <prefix> ::= <prefix> <unqualified-name> |
| // ::= <template-prefix> <template-args> |
| // ::= <template-param> |
| // ::= <substitution> |
| // ::= # empty |
| // <template-prefix> ::= <prefix> <(template) unqualified-name> |
| // ::= <template-param> |
| // ::= <substitution> |
| static bool ParsePrefix(State *state) { |
| bool has_something = false; |
| while (true) { |
| MaybeAppendSeparator(state); |
| if (ParseTemplateParam(state) || |
| ParseSubstitution(state) || |
| ParseUnscopedName(state)) { |
| has_something = true; |
| MaybeIncreaseNestLevel(state); |
| continue; |
| } |
| MaybeCancelLastSeparator(state); |
| if (has_something && ParseTemplateArgs(state)) { |
| return ParsePrefix(state); |
| } else { |
| break; |
| } |
| } |
| return true; |
| } |
| |
| // <unqualified-name> ::= <operator-name> |
| // ::= <ctor-dtor-name> |
| // ::= <source-name> |
| // ::= <local-source-name> |
| static bool ParseUnqualifiedName(State *state) { |
| return (ParseOperatorName(state) || |
| ParseCtorDtorName(state) || |
| ParseSourceName(state) || |
| ParseLocalSourceName(state)); |
| } |
| |
| // <source-name> ::= <positive length number> <identifier> |
| static bool ParseSourceName(State *state) { |
| State copy = *state; |
| if (ParseNumber(state) && ParseIdentifier(state)) { |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <local-source-name> ::= L <source-name> [<discriminator>] |
| // |
| // References: |
| // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 |
| // http://gcc.gnu.org/viewcvs?view=rev&revision=124467 |
| static bool ParseLocalSourceName(State *state) { |
| State copy = *state; |
| if (ParseChar(state, 'L') && ParseSourceName(state) && |
| Optional(ParseDiscriminator(state))) { |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <number> ::= [n] <non-negative decimal integer> |
| static bool ParseNumber(State *state) { |
| int sign = 1; |
| if (ParseChar(state, 'n')) { |
| sign = -1; |
| } |
| const char *p = state->mangled_cur; |
| int number = 0; |
| for (;p < state->mangled_end; ++p) { |
| if (IsDigit(*p)) { |
| number = number * 10 + (*p - '0'); |
| } else { |
| break; |
| } |
| } |
| if (p != state->mangled_cur) { // Conversion succeeded. |
| state->mangled_cur = p; |
| state->number = number * sign; |
| return true; |
| } |
| return false; |
| } |
| |
| // Floating-point literals are encoded using a fixed-length lowercase |
| // hexadecimal string. |
| static bool ParseFloatNumber(State *state) { |
| const char *p = state->mangled_cur; |
| int number = 0; |
| for (;p < state->mangled_end; ++p) { |
| if (IsDigit(*p)) { |
| number = number * 16 + (*p - '0'); |
| } else if (*p >= 'a' && *p <= 'f') { |
| number = number * 16 + (*p - 'a' + 10); |
| } else { |
| break; |
| } |
| } |
| if (p != state->mangled_cur) { // Conversion succeeded. |
| state->mangled_cur = p; |
| state->number = number; |
| return true; |
| } |
| return false; |
| } |
| |
| // The <seq-id> is a sequence number in base 36, |
| // using digits and upper case letters |
| static bool ParseSeqId(State *state) { |
| const char *p = state->mangled_cur; |
| int number = 0; |
| for (;p < state->mangled_end; ++p) { |
| if (IsDigit(*p)) { |
| number = number * 36 + (*p - '0'); |
| } else if (*p >= 'A' && *p <= 'Z') { |
| number = number * 36 + (*p - 'A' + 10); |
| } else { |
| break; |
| } |
| } |
| if (p != state->mangled_cur) { // Conversion succeeded. |
| state->mangled_cur = p; |
| state->number = number; |
| return true; |
| } |
| return false; |
| } |
| |
| // <identifier> ::= <unqualified source code identifier> |
| static bool ParseIdentifier(State *state) { |
| if (state->number == -1 || |
| RemainingLength(state) < state->number) { |
| return false; |
| } |
| if (IdentifierIsAnonymousNamespace(state)) { |
| MaybeAppend(state, "(anonymous namespace)"); |
| } else { |
| MaybeAppendWithLength(state, state->mangled_cur, state->number); |
| } |
| state->mangled_cur += state->number; |
| state->number = -1; // Reset the number. |
| return true; |
| } |
| |
| // <operator-name> ::= nw, and other two letters cases |
| // ::= cv <type> # (cast) |
| // ::= v <digit> <source-name> # vendor extended operator |
| static bool ParseOperatorName(State *state) { |
| if (RemainingLength(state) < 2) { |
| return false; |
| } |
| // First check with "cv" (cast) case. |
| State copy = *state; |
| if (ParseTwoChar(state, "cv") && |
| MaybeAppend(state, "operator ") && |
| EnterNestedName(state) && |
| ParseType(state) && |
| LeaveNestedName(state, copy.nest_level)) { |
| return true; |
| } |
| *state = copy; |
| |
| // Then vendor extended operators. |
| if (ParseChar(state, 'v') && ParseCharClass(state, "0123456789") && |
| ParseSourceName(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| // Other operator names should start with a lower alphabet followed |
| // by a lower/upper alphabet. |
| if (!(IsLower(state->mangled_cur[0]) && |
| IsAlpha(state->mangled_cur[1]))) { |
| return false; |
| } |
| // We may want to perform a binary search if we really need speed. |
| const AbbrevPair *p; |
| for (p = kOperatorList; p->abbrev != NULL; ++p) { |
| if (state->mangled_cur[0] == p->abbrev[0] && |
| state->mangled_cur[1] == p->abbrev[1]) { |
| MaybeAppend(state, "operator"); |
| if (IsLower(*p->real_name)) { // new, delete, etc. |
| MaybeAppend(state, " "); |
| } |
| MaybeAppend(state, p->real_name); |
| state->mangled_cur += 2; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| // <special-name> ::= TV <type> |
| // ::= TT <type> |
| // ::= TI <type> |
| // ::= TS <type> |
| // ::= Tc <call-offset> <call-offset> <(base) encoding> |
| // ::= GV <(object) name> |
| // ::= T <call-offset> <(base) encoding> |
| // G++ extensions: |
| // ::= TC <type> <(offset) number> _ <(base) type> |
| // ::= TF <type> |
| // ::= TJ <type> |
| // ::= GR <name> |
| // ::= GA <encoding> |
| // ::= Th <call-offset> <(base) encoding> |
| // ::= Tv <call-offset> <(base) encoding> |
| // |
| // Note: we don't care much about them since they don't appear in |
| // stack traces. The are special data. |
| static bool ParseSpecialName(State *state) { |
| State copy = *state; |
| if (ParseChar(state, 'T') && |
| ParseCharClass(state, "VTIS") && |
| ParseType(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseTwoChar(state, "Tc") && ParseCallOffset(state) && |
| ParseCallOffset(state) && ParseEncoding(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseTwoChar(state, "GV") && |
| ParseName(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseChar(state, 'T') && ParseCallOffset(state) && |
| ParseEncoding(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| // G++ extensions |
| if (ParseTwoChar(state, "TC") && ParseType(state) && |
| ParseNumber(state) && ParseChar(state, '_') && |
| DisableAppend(state) && |
| ParseType(state)) { |
| RestoreAppend(state, copy.append); |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseChar(state, 'T') && ParseCharClass(state, "FJ") && |
| ParseType(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseTwoChar(state, "GR") && ParseName(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseTwoChar(state, "GA") && ParseEncoding(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseChar(state, 'T') && ParseCharClass(state, "hv") && |
| ParseCallOffset(state) && ParseEncoding(state)) { |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <call-offset> ::= h <nv-offset> _ |
| // ::= v <v-offset> _ |
| static bool ParseCallOffset(State *state) { |
| State copy = *state; |
| if (ParseChar(state, 'h') && |
| ParseNVOffset(state) && ParseChar(state, '_')) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseChar(state, 'v') && |
| ParseVOffset(state) && ParseChar(state, '_')) { |
| return true; |
| } |
| *state = copy; |
| |
| return false; |
| } |
| |
| // <nv-offset> ::= <(offset) number> |
| static bool ParseNVOffset(State *state) { |
| return ParseNumber(state); |
| } |
| |
| // <v-offset> ::= <(offset) number> _ <(virtual offset) number> |
| static bool ParseVOffset(State *state) { |
| State copy = *state; |
| if (ParseNumber(state) && ParseChar(state, '_') && |
| ParseNumber(state)) { |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <ctor-dtor-name> ::= C1 | C2 | C3 |
| // ::= D0 | D1 | D2 |
| static bool ParseCtorDtorName(State *state) { |
| State copy = *state; |
| if (ParseChar(state, 'C') && |
| ParseCharClass(state, "123")) { |
| const char * const prev_name = state->prev_name; |
| const int prev_name_length = state->prev_name_length; |
| MaybeAppendWithLength(state, prev_name, prev_name_length); |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseChar(state, 'D') && |
| ParseCharClass(state, "012")) { |
| const char * const prev_name = state->prev_name; |
| const int prev_name_length = state->prev_name_length; |
| MaybeAppend(state, "~"); |
| MaybeAppendWithLength(state, prev_name, prev_name_length); |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <type> ::= <CV-qualifiers> <type> |
| // ::= P <type> # pointer-to |
| // ::= R <type> # reference-to |
| // ::= O <type> # rvalue reference-to (C++0x) |
| // ::= C <type> # complex pair (C 2000) |
| // ::= G <type> # imaginary (C 2000) |
| // ::= U <source-name> <type> # vendor extended type qualifier |
| // ::= <builtin-type> |
| // ::= <function-type> |
| // ::= <class-enum-type> |
| // ::= <array-type> |
| // ::= <pointer-to-member-type> |
| // ::= <template-template-param> <template-args> |
| // ::= <template-param> |
| // ::= <substitution> |
| // ::= Dp <type> # pack expansion of (C++0x) |
| // ::= Dt <expression> E # decltype of an id-expression or class |
| // # member access (C++0x) |
| // ::= DT <expression> E # decltype of an expression (C++0x) |
| // |
| static bool ParseType(State *state) { |
| // We should check CV-qualifers, and PRGC things first. |
| State copy = *state; |
| if (ParseCVQualifiers(state) && ParseType(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseCharClass(state, "OPRCG") && ParseType(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseTwoChar(state, "Dp") && ParseType(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseChar(state, 'D') && ParseCharClass(state, "tT") && |
| ParseExpression(state) && ParseChar(state, 'E')) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseChar(state, 'U') && ParseSourceName(state) && |
| ParseType(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseBuiltinType(state) || |
| ParseFunctionType(state) || |
| ParseClassEnumType(state) || |
| ParseArrayType(state) || |
| ParsePointerToMemberType(state) || |
| ParseSubstitution(state)) { |
| return true; |
| } |
| |
| if (ParseTemplateTemplateParam(state) && |
| ParseTemplateArgs(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| // Less greedy than <template-template-param> <template-args>. |
| if (ParseTemplateParam(state)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // <CV-qualifiers> ::= [r] [V] [K] |
| // We don't allow empty <CV-qualifiers> to avoid infinite loop in |
| // ParseType(). |
| static bool ParseCVQualifiers(State *state) { |
| int num_cv_qualifiers = 0; |
| num_cv_qualifiers += ParseChar(state, 'r'); |
| num_cv_qualifiers += ParseChar(state, 'V'); |
| num_cv_qualifiers += ParseChar(state, 'K'); |
| return num_cv_qualifiers > 0; |
| } |
| |
| // <builtin-type> ::= v, etc. |
| // ::= u <source-name> |
| static bool ParseBuiltinType(State *state) { |
| const AbbrevPair *p; |
| for (p = kBuiltinTypeList; p->abbrev != NULL; ++p) { |
| if (state->mangled_cur[0] == p->abbrev[0]) { |
| MaybeAppend(state, p->real_name); |
| ++state->mangled_cur; |
| return true; |
| } |
| } |
| |
| State copy = *state; |
| if (ParseChar(state, 'u') && ParseSourceName(state)) { |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <function-type> ::= F [Y] <bare-function-type> E |
| static bool ParseFunctionType(State *state) { |
| State copy = *state; |
| if (ParseChar(state, 'F') && Optional(ParseChar(state, 'Y')) && |
| ParseBareFunctionType(state) && ParseChar(state, 'E')) { |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <bare-function-type> ::= <(signature) type>+ |
| static bool ParseBareFunctionType(State *state) { |
| State copy = *state; |
| // i#1351: we do not want the empty "()" |
| if (!(state->options & DEMANGLE_KEEP_OVERLOADS)) { |
| DisableAppend(state); |
| } |
| MaybeAppend(state, "("); |
| if (OneOrMore(ParseType, state)) { |
| MaybeAppend(state, ")"); |
| if (!(state->options & DEMANGLE_KEEP_OVERLOADS)) { |
| RestoreAppend(state, copy.append); |
| } |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <class-enum-type> ::= <name> |
| static bool ParseClassEnumType(State *state) { |
| return ParseName(state); |
| } |
| |
| // <array-type> ::= A <(positive dimension) number> _ <(element) type> |
| // ::= A [<(dimension) expression>] _ <(element) type> |
| static bool ParseArrayType(State *state) { |
| State copy = *state; |
| if (ParseChar(state, 'A') && ParseNumber(state) && |
| ParseChar(state, '_') && ParseType(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseChar(state, 'A') && Optional(ParseExpression(state)) && |
| ParseChar(state, '_') && ParseType(state)) { |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <pointer-to-member-type> ::= M <(class) type> <(member) type> |
| static bool ParsePointerToMemberType(State *state) { |
| State copy = *state; |
| if (ParseChar(state, 'M') && ParseType(state) && |
| ParseType(state)) { |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <template-param> ::= T_ |
| // ::= T <parameter-2 non-negative number> _ |
| static bool ParseTemplateParam(State *state) { |
| if (ParseTwoChar(state, "T_")) { |
| MaybeAppend(state, "?"); // We don't support template substitutions. |
| return true; |
| } |
| |
| State copy = *state; |
| if (ParseChar(state, 'T') && ParseNumber(state) && |
| ParseChar(state, '_')) { |
| MaybeAppend(state, "?"); // We don't support template substitutions. |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| |
| // <template-template-param> ::= <template-param> |
| // ::= <substitution> |
| static bool ParseTemplateTemplateParam(State *state) { |
| return (ParseTemplateParam(state) || |
| ParseSubstitution(state)); |
| } |
| |
| // <template-args> ::= I <template-arg>+ E |
| static bool ParseTemplateArgs(State *state) { |
| State copy = *state; |
| MaybeAppend(state, "<"); |
| if (!(state->options & DEMANGLE_KEEP_TEMPLATES)) { |
| DisableAppend(state); |
| } |
| if (ParseChar(state, 'I') && |
| OneOrMore(ParseTemplateArg, state) && |
| ParseChar(state, 'E')) { |
| if (!(state->options & DEMANGLE_KEEP_TEMPLATES)) { |
| RestoreAppend(state, copy.append); |
| } |
| MaybeAppend(state, ">"); |
| return true; |
| } |
| *state = copy; |
| if (state->out_cur < state->out_end) { |
| *state->out_cur = '\0'; // Cancel append. |
| } |
| return false; |
| } |
| |
| // <template-arg> ::= <type> |
| // ::= <expr-primary> |
| // ::= I <template-arg>* E # argument pack |
| // ::= X <expression> E |
| static bool ParseTemplateArg(State *state) { |
| State copy = *state; |
| if (ParseChar(state, 'I') && |
| ZeroOrMore(ParseTemplateArg, state) && |
| ParseChar(state, 'E')) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseType(state) || |
| ParseExprPrimary(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseChar(state, 'X') && ParseExpression(state) && |
| ParseChar(state, 'E')) { |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <expression> ::= <template-param> |
| // ::= <expr-primary> |
| // ::= <unary operator-name> <expression> |
| // ::= <binary operator-name> <expression> <expression> |
| // ::= <trinary operator-name> <expression> <expression> |
| // <expression> |
| // ::= st <type> |
| // ::= sr <type> <unqualified-name> <template-args> |
| // ::= sr <type> <unqualified-name> |
| static bool ParseExpression(State *state) { |
| if (ParseTemplateParam(state) || ParseExprPrimary(state)) { |
| return true; |
| } |
| |
| State copy = *state; |
| if (ParseOperatorName(state) && |
| ParseExpression(state) && |
| ParseExpression(state) && |
| ParseExpression(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseOperatorName(state) && |
| ParseExpression(state) && |
| ParseExpression(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseOperatorName(state) && |
| ParseExpression(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseTwoChar(state, "st") && ParseType(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseTwoChar(state, "sr") && ParseType(state) && |
| ParseUnqualifiedName(state) && |
| ParseTemplateArgs(state)) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseTwoChar(state, "sr") && ParseType(state) && |
| ParseUnqualifiedName(state)) { |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <expr-primary> ::= L <type> <(value) number> E |
| // ::= L <type> <(value) float> E |
| // ::= L <mangled-name> E |
| // // A bug in g++'s C++ ABI version 2 (-fabi-version=2). |
| // ::= LZ <encoding> E |
| static bool ParseExprPrimary(State *state) { |
| State copy = *state; |
| if (ParseChar(state, 'L') && ParseType(state) && |
| ParseNumber(state) && |
| ParseChar(state, 'E')) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseChar(state, 'L') && ParseType(state) && |
| ParseFloatNumber(state) && |
| ParseChar(state, 'E')) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseChar(state, 'L') && ParseMangledName(state) && |
| ParseChar(state, 'E')) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseTwoChar(state, "LZ") && ParseEncoding(state) && |
| ParseChar(state, 'E')) { |
| return true; |
| } |
| *state = copy; |
| |
| return false; |
| } |
| |
| // <local-name> := Z <(function) encoding> E <(entity) name> |
| // [<discriminator>] |
| // := Z <(function) encoding> E s [<discriminator>] |
| static bool ParseLocalName(State *state) { |
| State copy = *state; |
| if (ParseChar(state, 'Z') && ParseEncoding(state) && |
| ParseChar(state, 'E') && MaybeAppend(state, "::") && |
| ParseName(state) && Optional(ParseDiscriminator(state))) { |
| return true; |
| } |
| *state = copy; |
| |
| if (ParseChar(state, 'Z') && ParseEncoding(state) && |
| ParseTwoChar(state, "Es") && Optional(ParseDiscriminator(state))) { |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <discriminator> := _ <(non-negative) number> |
| static bool ParseDiscriminator(State *state) { |
| State copy = *state; |
| if (ParseChar(state, '_') && ParseNumber(state)) { |
| return true; |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // <substitution> ::= S_ |
| // ::= S <seq-id> _ |
| // ::= St, etc. |
| static bool ParseSubstitution(State *state) { |
| if (ParseTwoChar(state, "S_")) { |
| MaybeAppend(state, "?"); // We don't support substitutions. |
| return true; |
| } |
| |
| State copy = *state; |
| if (ParseChar(state, 'S') && ParseSeqId(state) && |
| ParseChar(state, '_')) { |
| MaybeAppend(state, "?"); // We don't support substitutions. |
| return true; |
| } |
| *state = copy; |
| |
| // Expand abbreviations like "St" => "std". |
| if (ParseChar(state, 'S')) { |
| const AbbrevPair *p; |
| for (p = kSubstitutionList; p->abbrev != NULL; ++p) { |
| if (state->mangled_cur[0] == p->abbrev[1]) { |
| MaybeAppend(state, "std"); |
| if (p->real_name[0] != '\0') { |
| MaybeAppend(state, "::"); |
| MaybeAppend(state, p->real_name); |
| } |
| state->mangled_cur += 1; |
| return true; |
| } |
| } |
| } |
| *state = copy; |
| return false; |
| } |
| |
| // The demangler entry point. |
| int Demangle(const char *mangled, char *out, int out_size, unsigned short options) { |
| State state; |
| InitState(&state, mangled, out, out_size); |
| state.options = options; |
| if (!ParseMangledName(&state)) |
| return 0; |
| // Always null terminate. |
| out[out_size-1] = '\0'; |
| // Return the size necessary to avoid truncation. |
| return (int)((size_t)state.out_cur - (size_t)state.out_begin); |
| } |
| |
| _END_GOOGLE_NAMESPACE_ |