| /* Demangler for the Rust programming language | |
| Copyright (C) 2016-2022 Free Software Foundation, Inc. | |
| Written by David Tolnay (dtolnay@gmail.com). | |
| Rewritten by Eduard-Mihai Burtescu (eddyb@lyken.rs) for v0 support. | |
| This file is part of the libiberty library. | |
| Libiberty is free software; you can redistribute it and/or | |
| modify it under the terms of the GNU Library General Public | |
| License as published by the Free Software Foundation; either | |
| version 2 of the License, or (at your option) any later version. | |
| In addition to the permissions in the GNU Library General Public | |
| License, the Free Software Foundation gives you unlimited permission | |
| to link the compiled version of this file into combinations with other | |
| programs, and to distribute those combinations without any restriction | |
| coming from the use of this file. (The Library Public License | |
| restrictions do apply in other respects; for example, they cover | |
| modification of the file, and distribution when not linked into a | |
| combined executable.) | |
| Libiberty is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| Library General Public License for more details. | |
| You should have received a copy of the GNU Library General Public | |
| License along with libiberty; see the file COPYING.LIB. | |
| If not, see <http://www.gnu.org/licenses/>. */ | |
| #ifdef HAVE_CONFIG_H | |
| #include "config.h" | |
| #endif | |
| #include "safe-ctype.h" | |
| #include <inttypes.h> | |
| #include <sys/types.h> | |
| #include <string.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #ifdef HAVE_STRING_H | |
| #include <string.h> | |
| #else | |
| extern size_t strlen(const char *s); | |
| extern int strncmp(const char *s1, const char *s2, size_t n); | |
| extern void *memset(void *s, int c, size_t n); | |
| #endif | |
| #include <demangle.h> | |
| #include "libiberty.h" | |
| struct rust_demangler | |
| { | |
| const char *sym; | |
| size_t sym_len; | |
| void *callback_opaque; | |
| demangle_callbackref callback; | |
| /* Position of the next character to read from the symbol. */ | |
| size_t next; | |
| /* Non-zero if any error occurred. */ | |
| int errored; | |
| /* Non-zero if nothing should be printed. */ | |
| int skipping_printing; | |
| /* Non-zero if printing should be verbose (e.g. include hashes). */ | |
| int verbose; | |
| /* Rust mangling version, with legacy mangling being -1. */ | |
| int version; | |
| /* Recursion depth. */ | |
| unsigned int recursion; | |
| /* Maximum number of times demangle_path may be called recursively. */ | |
| #define RUST_MAX_RECURSION_COUNT 1024 | |
| #define RUST_NO_RECURSION_LIMIT ((unsigned int) -1) | |
| uint64_t bound_lifetime_depth; | |
| }; | |
| /* Parsing functions. */ | |
| static char | |
| peek (const struct rust_demangler *rdm) | |
| { | |
| if (rdm->next < rdm->sym_len) | |
| return rdm->sym[rdm->next]; | |
| return 0; | |
| } | |
| static int | |
| eat (struct rust_demangler *rdm, char c) | |
| { | |
| if (peek (rdm) == c) | |
| { | |
| rdm->next++; | |
| return 1; | |
| } | |
| else | |
| return 0; | |
| } | |
| static char | |
| next (struct rust_demangler *rdm) | |
| { | |
| char c = peek (rdm); | |
| if (!c) | |
| rdm->errored = 1; | |
| else | |
| rdm->next++; | |
| return c; | |
| } | |
| static uint64_t | |
| parse_integer_62 (struct rust_demangler *rdm) | |
| { | |
| char c; | |
| uint64_t x; | |
| if (eat (rdm, '_')) | |
| return 0; | |
| x = 0; | |
| while (!eat (rdm, '_') && !rdm->errored) | |
| { | |
| c = next (rdm); | |
| x *= 62; | |
| if (ISDIGIT (c)) | |
| x += c - '0'; | |
| else if (ISLOWER (c)) | |
| x += 10 + (c - 'a'); | |
| else if (ISUPPER (c)) | |
| x += 10 + 26 + (c - 'A'); | |
| else | |
| { | |
| rdm->errored = 1; | |
| return 0; | |
| } | |
| } | |
| return x + 1; | |
| } | |
| static uint64_t | |
| parse_opt_integer_62 (struct rust_demangler *rdm, char tag) | |
| { | |
| if (!eat (rdm, tag)) | |
| return 0; | |
| return 1 + parse_integer_62 (rdm); | |
| } | |
| static uint64_t | |
| parse_disambiguator (struct rust_demangler *rdm) | |
| { | |
| return parse_opt_integer_62 (rdm, 's'); | |
| } | |
| static size_t | |
| parse_hex_nibbles (struct rust_demangler *rdm, uint64_t *value) | |
| { | |
| char c; | |
| size_t hex_len; | |
| hex_len = 0; | |
| *value = 0; | |
| while (!eat (rdm, '_')) | |
| { | |
| *value <<= 4; | |
| c = next (rdm); | |
| if (ISDIGIT (c)) | |
| *value |= c - '0'; | |
| else if (c >= 'a' && c <= 'f') | |
| *value |= 10 + (c - 'a'); | |
| else | |
| { | |
| rdm->errored = 1; | |
| return 0; | |
| } | |
| hex_len++; | |
| } | |
| return hex_len; | |
| } | |
| struct rust_mangled_ident | |
| { | |
| /* ASCII part of the identifier. */ | |
| const char *ascii; | |
| size_t ascii_len; | |
| /* Punycode insertion codes for Unicode codepoints, if any. */ | |
| const char *punycode; | |
| size_t punycode_len; | |
| }; | |
| static struct rust_mangled_ident | |
| parse_ident (struct rust_demangler *rdm) | |
| { | |
| char c; | |
| size_t start, len; | |
| int is_punycode = 0; | |
| struct rust_mangled_ident ident; | |
| ident.ascii = NULL; | |
| ident.ascii_len = 0; | |
| ident.punycode = NULL; | |
| ident.punycode_len = 0; | |
| if (rdm->version != -1) | |
| is_punycode = eat (rdm, 'u'); | |
| c = next (rdm); | |
| if (!ISDIGIT (c)) | |
| { | |
| rdm->errored = 1; | |
| return ident; | |
| } | |
| len = c - '0'; | |
| if (c != '0') | |
| while (ISDIGIT (peek (rdm))) | |
| len = len * 10 + (next (rdm) - '0'); | |
| /* Skip past the optional `_` separator (v0). */ | |
| if (rdm->version != -1) | |
| eat (rdm, '_'); | |
| start = rdm->next; | |
| rdm->next += len; | |
| /* Check for overflows. */ | |
| if ((start > rdm->next) || (rdm->next > rdm->sym_len)) | |
| { | |
| rdm->errored = 1; | |
| return ident; | |
| } | |
| ident.ascii = rdm->sym + start; | |
| ident.ascii_len = len; | |
| if (is_punycode) | |
| { | |
| ident.punycode_len = 0; | |
| while (ident.ascii_len > 0) | |
| { | |
| ident.ascii_len--; | |
| /* The last '_' is a separator between ascii & punycode. */ | |
| if (ident.ascii[ident.ascii_len] == '_') | |
| break; | |
| ident.punycode_len++; | |
| } | |
| if (!ident.punycode_len) | |
| { | |
| rdm->errored = 1; | |
| return ident; | |
| } | |
| ident.punycode = ident.ascii + (len - ident.punycode_len); | |
| } | |
| if (ident.ascii_len == 0) | |
| ident.ascii = NULL; | |
| return ident; | |
| } | |
| /* Printing functions. */ | |
| static void | |
| print_str (struct rust_demangler *rdm, const char *data, size_t len) | |
| { | |
| if (!rdm->errored && !rdm->skipping_printing) | |
| rdm->callback (data, len, rdm->callback_opaque); | |
| } | |
| #define PRINT(s) print_str (rdm, s, strlen (s)) | |
| static void | |
| print_uint64 (struct rust_demangler *rdm, uint64_t x) | |
| { | |
| char s[21]; | |
| snprintf (s, 21, "%" PRIu64, x); | |
| PRINT (s); | |
| } | |
| static void | |
| print_uint64_hex (struct rust_demangler *rdm, uint64_t x) | |
| { | |
| char s[17]; | |
| snprintf (s, 17, "%" PRIx64, x); | |
| PRINT (s); | |
| } | |
| /* Return a 0x0-0xf value if the char is 0-9a-f, and -1 otherwise. */ | |
| static int | |
| decode_lower_hex_nibble (char nibble) | |
| { | |
| if ('0' <= nibble && nibble <= '9') | |
| return nibble - '0'; | |
| if ('a' <= nibble && nibble <= 'f') | |
| return 0xa + (nibble - 'a'); | |
| return -1; | |
| } | |
| /* Return the unescaped character for a "$...$" escape, or 0 if invalid. */ | |
| static char | |
| decode_legacy_escape (const char *e, size_t len, size_t *out_len) | |
| { | |
| char c = 0; | |
| size_t escape_len = 0; | |
| int lo_nibble = -1, hi_nibble = -1; | |
| if (len < 3 || e[0] != '$') | |
| return 0; | |
| e++; | |
| len--; | |
| if (e[0] == 'C') | |
| { | |
| escape_len = 1; | |
| c = ','; | |
| } | |
| else if (len > 2) | |
| { | |
| escape_len = 2; | |
| if (e[0] == 'S' && e[1] == 'P') | |
| c = '@'; | |
| else if (e[0] == 'B' && e[1] == 'P') | |
| c = '*'; | |
| else if (e[0] == 'R' && e[1] == 'F') | |
| c = '&'; | |
| else if (e[0] == 'L' && e[1] == 'T') | |
| c = '<'; | |
| else if (e[0] == 'G' && e[1] == 'T') | |
| c = '>'; | |
| else if (e[0] == 'L' && e[1] == 'P') | |
| c = '('; | |
| else if (e[0] == 'R' && e[1] == 'P') | |
| c = ')'; | |
| else if (e[0] == 'u' && len > 3) | |
| { | |
| escape_len = 3; | |
| hi_nibble = decode_lower_hex_nibble (e[1]); | |
| if (hi_nibble < 0) | |
| return 0; | |
| lo_nibble = decode_lower_hex_nibble (e[2]); | |
| if (lo_nibble < 0) | |
| return 0; | |
| /* Only allow non-control ASCII characters. */ | |
| if (hi_nibble > 7) | |
| return 0; | |
| c = (hi_nibble << 4) | lo_nibble; | |
| if (c < 0x20) | |
| return 0; | |
| } | |
| } | |
| if (!c || len <= escape_len || e[escape_len] != '$') | |
| return 0; | |
| *out_len = 2 + escape_len; | |
| return c; | |
| } | |
| static void | |
| print_ident (struct rust_demangler *rdm, struct rust_mangled_ident ident) | |
| { | |
| char unescaped; | |
| uint8_t *out, *p, d; | |
| size_t len, cap, punycode_pos, j; | |
| /* Punycode parameters and state. */ | |
| uint32_t c; | |
| size_t base, t_min, t_max, skew, damp, bias, i; | |
| size_t delta, w, k, t; | |
| if (rdm->errored || rdm->skipping_printing) | |
| return; | |
| if (rdm->version == -1) | |
| { | |
| /* Ignore leading underscores preceding escape sequences. | |
| The mangler inserts an underscore to make sure the | |
| identifier begins with a XID_Start character. */ | |
| if (ident.ascii_len >= 2 && ident.ascii[0] == '_' | |
| && ident.ascii[1] == '$') | |
| { | |
| ident.ascii++; | |
| ident.ascii_len--; | |
| } | |
| while (ident.ascii_len > 0) | |
| { | |
| /* Handle legacy escape sequences ("$...$", ".." or "."). */ | |
| if (ident.ascii[0] == '$') | |
| { | |
| unescaped | |
| = decode_legacy_escape (ident.ascii, ident.ascii_len, &len); | |
| if (unescaped) | |
| print_str (rdm, &unescaped, 1); | |
| else | |
| { | |
| /* Unexpected escape sequence, print the rest verbatim. */ | |
| print_str (rdm, ident.ascii, ident.ascii_len); | |
| return; | |
| } | |
| } | |
| else if (ident.ascii[0] == '.') | |
| { | |
| if (ident.ascii_len >= 2 && ident.ascii[1] == '.') | |
| { | |
| /* ".." becomes "::" */ | |
| PRINT ("::"); | |
| len = 2; | |
| } | |
| else | |
| { | |
| PRINT ("."); | |
| len = 1; | |
| } | |
| } | |
| else | |
| { | |
| /* Print everything before the next escape sequence, at once. */ | |
| for (len = 0; len < ident.ascii_len; len++) | |
| if (ident.ascii[len] == '$' || ident.ascii[len] == '.') | |
| break; | |
| print_str (rdm, ident.ascii, len); | |
| } | |
| ident.ascii += len; | |
| ident.ascii_len -= len; | |
| } | |
| return; | |
| } | |
| if (!ident.punycode) | |
| { | |
| print_str (rdm, ident.ascii, ident.ascii_len); | |
| return; | |
| } | |
| len = 0; | |
| cap = 4; | |
| while (cap < ident.ascii_len) | |
| { | |
| cap *= 2; | |
| /* Check for overflows. */ | |
| if ((cap * 4) / 4 != cap) | |
| { | |
| rdm->errored = 1; | |
| return; | |
| } | |
| } | |
| /* Store the output codepoints as groups of 4 UTF-8 bytes. */ | |
| out = (uint8_t *)malloc (cap * 4); | |
| if (!out) | |
| { | |
| rdm->errored = 1; | |
| return; | |
| } | |
| /* Populate initial output from ASCII fragment. */ | |
| for (len = 0; len < ident.ascii_len; len++) | |
| { | |
| p = out + 4 * len; | |
| p[0] = 0; | |
| p[1] = 0; | |
| p[2] = 0; | |
| p[3] = ident.ascii[len]; | |
| } | |
| /* Punycode parameters and initial state. */ | |
| base = 36; | |
| t_min = 1; | |
| t_max = 26; | |
| skew = 38; | |
| damp = 700; | |
| bias = 72; | |
| i = 0; | |
| c = 0x80; | |
| punycode_pos = 0; | |
| while (punycode_pos < ident.punycode_len) | |
| { | |
| /* Read one delta value. */ | |
| delta = 0; | |
| w = 1; | |
| k = 0; | |
| do | |
| { | |
| k += base; | |
| t = k < bias ? 0 : (k - bias); | |
| if (t < t_min) | |
| t = t_min; | |
| if (t > t_max) | |
| t = t_max; | |
| if (punycode_pos >= ident.punycode_len) | |
| goto cleanup; | |
| d = ident.punycode[punycode_pos++]; | |
| if (ISLOWER (d)) | |
| d = d - 'a'; | |
| else if (ISDIGIT (d)) | |
| d = 26 + (d - '0'); | |
| else | |
| { | |
| rdm->errored = 1; | |
| goto cleanup; | |
| } | |
| delta += d * w; | |
| w *= base - t; | |
| } | |
| while (d >= t); | |
| /* Compute the new insert position and character. */ | |
| len++; | |
| i += delta; | |
| c += i / len; | |
| i %= len; | |
| /* Ensure enough space is available. */ | |
| if (cap < len) | |
| { | |
| cap *= 2; | |
| /* Check for overflows. */ | |
| if ((cap * 4) / 4 != cap || cap < len) | |
| { | |
| rdm->errored = 1; | |
| goto cleanup; | |
| } | |
| } | |
| p = (uint8_t *)realloc (out, cap * 4); | |
| if (!p) | |
| { | |
| rdm->errored = 1; | |
| goto cleanup; | |
| } | |
| out = p; | |
| /* Move the characters after the insert position. */ | |
| p = out + i * 4; | |
| memmove (p + 4, p, (len - i - 1) * 4); | |
| /* Insert the new character, as UTF-8 bytes. */ | |
| p[0] = c >= 0x10000 ? 0xf0 | (c >> 18) : 0; | |
| p[1] = c >= 0x800 ? (c < 0x10000 ? 0xe0 : 0x80) | ((c >> 12) & 0x3f) : 0; | |
| p[2] = (c < 0x800 ? 0xc0 : 0x80) | ((c >> 6) & 0x3f); | |
| p[3] = 0x80 | (c & 0x3f); | |
| /* If there are no more deltas, decoding is complete. */ | |
| if (punycode_pos == ident.punycode_len) | |
| break; | |
| i++; | |
| /* Perform bias adaptation. */ | |
| delta /= damp; | |
| damp = 2; | |
| delta += delta / len; | |
| k = 0; | |
| while (delta > ((base - t_min) * t_max) / 2) | |
| { | |
| delta /= base - t_min; | |
| k += base; | |
| } | |
| bias = k + ((base - t_min + 1) * delta) / (delta + skew); | |
| } | |
| /* Remove all the 0 bytes to leave behind an UTF-8 string. */ | |
| for (i = 0, j = 0; i < len * 4; i++) | |
| if (out[i] != 0) | |
| out[j++] = out[i]; | |
| print_str (rdm, (const char *)out, j); | |
| cleanup: | |
| free (out); | |
| } | |
| /* Print the lifetime according to the previously decoded index. | |
| An index of `0` always refers to `'_`, but starting with `1`, | |
| indices refer to late-bound lifetimes introduced by a binder. */ | |
| static void | |
| print_lifetime_from_index (struct rust_demangler *rdm, uint64_t lt) | |
| { | |
| char c; | |
| uint64_t depth; | |
| PRINT ("'"); | |
| if (lt == 0) | |
| { | |
| PRINT ("_"); | |
| return; | |
| } | |
| depth = rdm->bound_lifetime_depth - lt; | |
| /* Try to print lifetimes alphabetically first. */ | |
| if (depth < 26) | |
| { | |
| c = 'a' + depth; | |
| print_str (rdm, &c, 1); | |
| } | |
| else | |
| { | |
| /* Use `'_123` after running out of letters. */ | |
| PRINT ("_"); | |
| print_uint64 (rdm, depth); | |
| } | |
| } | |
| /* Demangling functions. */ | |
| static void demangle_binder (struct rust_demangler *rdm); | |
| static void demangle_path (struct rust_demangler *rdm, int in_value); | |
| static void demangle_generic_arg (struct rust_demangler *rdm); | |
| static void demangle_type (struct rust_demangler *rdm); | |
| static int demangle_path_maybe_open_generics (struct rust_demangler *rdm); | |
| static void demangle_dyn_trait (struct rust_demangler *rdm); | |
| static void demangle_const (struct rust_demangler *rdm); | |
| static void demangle_const_uint (struct rust_demangler *rdm); | |
| static void demangle_const_int (struct rust_demangler *rdm); | |
| static void demangle_const_bool (struct rust_demangler *rdm); | |
| static void demangle_const_char (struct rust_demangler *rdm); | |
| /* Optionally enter a binder ('G') for late-bound lifetimes, | |
| printing e.g. `for<'a, 'b> `, and make those lifetimes visible | |
| to the caller (via depth level, which the caller should reset). */ | |
| static void | |
| demangle_binder (struct rust_demangler *rdm) | |
| { | |
| uint64_t i, bound_lifetimes; | |
| if (rdm->errored) | |
| return; | |
| bound_lifetimes = parse_opt_integer_62 (rdm, 'G'); | |
| if (bound_lifetimes > 0) | |
| { | |
| PRINT ("for<"); | |
| for (i = 0; i < bound_lifetimes; i++) | |
| { | |
| if (i > 0) | |
| PRINT (", "); | |
| rdm->bound_lifetime_depth++; | |
| print_lifetime_from_index (rdm, 1); | |
| } | |
| PRINT ("> "); | |
| } | |
| } | |
| static void | |
| demangle_path (struct rust_demangler *rdm, int in_value) | |
| { | |
| char tag, ns; | |
| int was_skipping_printing; | |
| size_t i, backref, old_next; | |
| uint64_t dis; | |
| struct rust_mangled_ident name; | |
| if (rdm->errored) | |
| return; | |
| if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | |
| { | |
| ++ rdm->recursion; | |
| if (rdm->recursion > RUST_MAX_RECURSION_COUNT) | |
| /* FIXME: There ought to be a way to report | |
| that the recursion limit has been reached. */ | |
| goto fail_return; | |
| } | |
| switch (tag = next (rdm)) | |
| { | |
| case 'C': | |
| dis = parse_disambiguator (rdm); | |
| name = parse_ident (rdm); | |
| print_ident (rdm, name); | |
| if (rdm->verbose) | |
| { | |
| PRINT ("["); | |
| print_uint64_hex (rdm, dis); | |
| PRINT ("]"); | |
| } | |
| break; | |
| case 'N': | |
| ns = next (rdm); | |
| if (!ISLOWER (ns) && !ISUPPER (ns)) | |
| goto fail_return; | |
| demangle_path (rdm, in_value); | |
| dis = parse_disambiguator (rdm); | |
| name = parse_ident (rdm); | |
| if (ISUPPER (ns)) | |
| { | |
| /* Special namespaces, like closures and shims. */ | |
| PRINT ("::{"); | |
| switch (ns) | |
| { | |
| case 'C': | |
| PRINT ("closure"); | |
| break; | |
| case 'S': | |
| PRINT ("shim"); | |
| break; | |
| default: | |
| print_str (rdm, &ns, 1); | |
| } | |
| if (name.ascii || name.punycode) | |
| { | |
| PRINT (":"); | |
| print_ident (rdm, name); | |
| } | |
| PRINT ("#"); | |
| print_uint64 (rdm, dis); | |
| PRINT ("}"); | |
| } | |
| else | |
| { | |
| /* Implementation-specific/unspecified namespaces. */ | |
| if (name.ascii || name.punycode) | |
| { | |
| PRINT ("::"); | |
| print_ident (rdm, name); | |
| } | |
| } | |
| break; | |
| case 'M': | |
| case 'X': | |
| /* Ignore the `impl`'s own path.*/ | |
| parse_disambiguator (rdm); | |
| was_skipping_printing = rdm->skipping_printing; | |
| rdm->skipping_printing = 1; | |
| demangle_path (rdm, in_value); | |
| rdm->skipping_printing = was_skipping_printing; | |
| /* fallthrough */ | |
| case 'Y': | |
| PRINT ("<"); | |
| demangle_type (rdm); | |
| if (tag != 'M') | |
| { | |
| PRINT (" as "); | |
| demangle_path (rdm, 0); | |
| } | |
| PRINT (">"); | |
| break; | |
| case 'I': | |
| demangle_path (rdm, in_value); | |
| if (in_value) | |
| PRINT ("::"); | |
| PRINT ("<"); | |
| for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++) | |
| { | |
| if (i > 0) | |
| PRINT (", "); | |
| demangle_generic_arg (rdm); | |
| } | |
| PRINT (">"); | |
| break; | |
| case 'B': | |
| backref = parse_integer_62 (rdm); | |
| if (!rdm->skipping_printing) | |
| { | |
| old_next = rdm->next; | |
| rdm->next = backref; | |
| demangle_path (rdm, in_value); | |
| rdm->next = old_next; | |
| } | |
| break; | |
| default: | |
| goto fail_return; | |
| } | |
| goto pass_return; | |
| fail_return: | |
| rdm->errored = 1; | |
| pass_return: | |
| if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | |
| -- rdm->recursion; | |
| } | |
| static void | |
| demangle_generic_arg (struct rust_demangler *rdm) | |
| { | |
| uint64_t lt; | |
| if (eat (rdm, 'L')) | |
| { | |
| lt = parse_integer_62 (rdm); | |
| print_lifetime_from_index (rdm, lt); | |
| } | |
| else if (eat (rdm, 'K')) | |
| demangle_const (rdm); | |
| else | |
| demangle_type (rdm); | |
| } | |
| static const char * | |
| basic_type (char tag) | |
| { | |
| switch (tag) | |
| { | |
| case 'b': | |
| return "bool"; | |
| case 'c': | |
| return "char"; | |
| case 'e': | |
| return "str"; | |
| case 'u': | |
| return "()"; | |
| case 'a': | |
| return "i8"; | |
| case 's': | |
| return "i16"; | |
| case 'l': | |
| return "i32"; | |
| case 'x': | |
| return "i64"; | |
| case 'n': | |
| return "i128"; | |
| case 'i': | |
| return "isize"; | |
| case 'h': | |
| return "u8"; | |
| case 't': | |
| return "u16"; | |
| case 'm': | |
| return "u32"; | |
| case 'y': | |
| return "u64"; | |
| case 'o': | |
| return "u128"; | |
| case 'j': | |
| return "usize"; | |
| case 'f': | |
| return "f32"; | |
| case 'd': | |
| return "f64"; | |
| case 'z': | |
| return "!"; | |
| case 'p': | |
| return "_"; | |
| case 'v': | |
| return "..."; | |
| default: | |
| return NULL; | |
| } | |
| } | |
| static void | |
| demangle_type (struct rust_demangler *rdm) | |
| { | |
| char tag; | |
| size_t i, old_next, backref; | |
| uint64_t lt, old_bound_lifetime_depth; | |
| const char *basic; | |
| struct rust_mangled_ident abi; | |
| if (rdm->errored) | |
| return; | |
| tag = next (rdm); | |
| basic = basic_type (tag); | |
| if (basic) | |
| { | |
| PRINT (basic); | |
| return; | |
| } | |
| if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | |
| { | |
| ++ rdm->recursion; | |
| if (rdm->recursion > RUST_MAX_RECURSION_COUNT) | |
| /* FIXME: There ought to be a way to report | |
| that the recursion limit has been reached. */ | |
| { | |
| rdm->errored = 1; | |
| -- rdm->recursion; | |
| return; | |
| } | |
| } | |
| switch (tag) | |
| { | |
| case 'R': | |
| case 'Q': | |
| PRINT ("&"); | |
| if (eat (rdm, 'L')) | |
| { | |
| lt = parse_integer_62 (rdm); | |
| if (lt) | |
| { | |
| print_lifetime_from_index (rdm, lt); | |
| PRINT (" "); | |
| } | |
| } | |
| if (tag != 'R') | |
| PRINT ("mut "); | |
| demangle_type (rdm); | |
| break; | |
| case 'P': | |
| case 'O': | |
| PRINT ("*"); | |
| if (tag != 'P') | |
| PRINT ("mut "); | |
| else | |
| PRINT ("const "); | |
| demangle_type (rdm); | |
| break; | |
| case 'A': | |
| case 'S': | |
| PRINT ("["); | |
| demangle_type (rdm); | |
| if (tag == 'A') | |
| { | |
| PRINT ("; "); | |
| demangle_const (rdm); | |
| } | |
| PRINT ("]"); | |
| break; | |
| case 'T': | |
| PRINT ("("); | |
| for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++) | |
| { | |
| if (i > 0) | |
| PRINT (", "); | |
| demangle_type (rdm); | |
| } | |
| if (i == 1) | |
| PRINT (","); | |
| PRINT (")"); | |
| break; | |
| case 'F': | |
| old_bound_lifetime_depth = rdm->bound_lifetime_depth; | |
| demangle_binder (rdm); | |
| if (eat (rdm, 'U')) | |
| PRINT ("unsafe "); | |
| if (eat (rdm, 'K')) | |
| { | |
| if (eat (rdm, 'C')) | |
| { | |
| abi.ascii = "C"; | |
| abi.ascii_len = 1; | |
| } | |
| else | |
| { | |
| abi = parse_ident (rdm); | |
| if (!abi.ascii || abi.punycode) | |
| { | |
| rdm->errored = 1; | |
| goto restore; | |
| } | |
| } | |
| PRINT ("extern \""); | |
| /* If the ABI had any `-`, they were replaced with `_`, | |
| so the parts between `_` have to be re-joined with `-`. */ | |
| for (i = 0; i < abi.ascii_len; i++) | |
| { | |
| if (abi.ascii[i] == '_') | |
| { | |
| print_str (rdm, abi.ascii, i); | |
| PRINT ("-"); | |
| abi.ascii += i + 1; | |
| abi.ascii_len -= i + 1; | |
| i = 0; | |
| } | |
| } | |
| print_str (rdm, abi.ascii, abi.ascii_len); | |
| PRINT ("\" "); | |
| } | |
| PRINT ("fn("); | |
| for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++) | |
| { | |
| if (i > 0) | |
| PRINT (", "); | |
| demangle_type (rdm); | |
| } | |
| PRINT (")"); | |
| if (eat (rdm, 'u')) | |
| { | |
| /* Skip printing the return type if it's 'u', i.e. `()`. */ | |
| } | |
| else | |
| { | |
| PRINT (" -> "); | |
| demangle_type (rdm); | |
| } | |
| /* Restore `bound_lifetime_depth` to outside the binder. */ | |
| restore: | |
| rdm->bound_lifetime_depth = old_bound_lifetime_depth; | |
| break; | |
| case 'D': | |
| PRINT ("dyn "); | |
| old_bound_lifetime_depth = rdm->bound_lifetime_depth; | |
| demangle_binder (rdm); | |
| for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++) | |
| { | |
| if (i > 0) | |
| PRINT (" + "); | |
| demangle_dyn_trait (rdm); | |
| } | |
| /* Restore `bound_lifetime_depth` to outside the binder. */ | |
| rdm->bound_lifetime_depth = old_bound_lifetime_depth; | |
| if (!eat (rdm, 'L')) | |
| { | |
| rdm->errored = 1; | |
| return; | |
| } | |
| lt = parse_integer_62 (rdm); | |
| if (lt) | |
| { | |
| PRINT (" + "); | |
| print_lifetime_from_index (rdm, lt); | |
| } | |
| break; | |
| case 'B': | |
| backref = parse_integer_62 (rdm); | |
| if (!rdm->skipping_printing) | |
| { | |
| old_next = rdm->next; | |
| rdm->next = backref; | |
| demangle_type (rdm); | |
| rdm->next = old_next; | |
| } | |
| break; | |
| default: | |
| /* Go back to the tag, so `demangle_path` also sees it. */ | |
| rdm->next--; | |
| demangle_path (rdm, 0); | |
| } | |
| if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | |
| -- rdm->recursion; | |
| } | |
| /* A trait in a trait object may have some "existential projections" | |
| (i.e. associated type bindings) after it, which should be printed | |
| in the `<...>` of the trait, e.g. `dyn Trait<T, U, Assoc=X>`. | |
| To this end, this method will keep the `<...>` of an 'I' path | |
| open, by omitting the `>`, and return `Ok(true)` in that case. */ | |
| static int | |
| demangle_path_maybe_open_generics (struct rust_demangler *rdm) | |
| { | |
| int open; | |
| size_t i, old_next, backref; | |
| open = 0; | |
| if (rdm->errored) | |
| return open; | |
| if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | |
| { | |
| ++ rdm->recursion; | |
| if (rdm->recursion > RUST_MAX_RECURSION_COUNT) | |
| { | |
| /* FIXME: There ought to be a way to report | |
| that the recursion limit has been reached. */ | |
| rdm->errored = 1; | |
| goto end_of_func; | |
| } | |
| } | |
| if (eat (rdm, 'B')) | |
| { | |
| backref = parse_integer_62 (rdm); | |
| if (!rdm->skipping_printing) | |
| { | |
| old_next = rdm->next; | |
| rdm->next = backref; | |
| open = demangle_path_maybe_open_generics (rdm); | |
| rdm->next = old_next; | |
| } | |
| } | |
| else if (eat (rdm, 'I')) | |
| { | |
| demangle_path (rdm, 0); | |
| PRINT ("<"); | |
| open = 1; | |
| for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++) | |
| { | |
| if (i > 0) | |
| PRINT (", "); | |
| demangle_generic_arg (rdm); | |
| } | |
| } | |
| else | |
| demangle_path (rdm, 0); | |
| end_of_func: | |
| if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | |
| -- rdm->recursion; | |
| return open; | |
| } | |
| static void | |
| demangle_dyn_trait (struct rust_demangler *rdm) | |
| { | |
| int open; | |
| struct rust_mangled_ident name; | |
| if (rdm->errored) | |
| return; | |
| open = demangle_path_maybe_open_generics (rdm); | |
| while (eat (rdm, 'p')) | |
| { | |
| if (!open) | |
| PRINT ("<"); | |
| else | |
| PRINT (", "); | |
| open = 1; | |
| name = parse_ident (rdm); | |
| print_ident (rdm, name); | |
| PRINT (" = "); | |
| demangle_type (rdm); | |
| } | |
| if (open) | |
| PRINT (">"); | |
| } | |
| static void | |
| demangle_const (struct rust_demangler *rdm) | |
| { | |
| char ty_tag; | |
| size_t old_next, backref; | |
| if (rdm->errored) | |
| return; | |
| if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | |
| { | |
| ++ rdm->recursion; | |
| if (rdm->recursion > RUST_MAX_RECURSION_COUNT) | |
| /* FIXME: There ought to be a way to report | |
| that the recursion limit has been reached. */ | |
| goto fail_return; | |
| } | |
| if (eat (rdm, 'B')) | |
| { | |
| backref = parse_integer_62 (rdm); | |
| if (!rdm->skipping_printing) | |
| { | |
| old_next = rdm->next; | |
| rdm->next = backref; | |
| demangle_const (rdm); | |
| rdm->next = old_next; | |
| } | |
| goto pass_return; | |
| } | |
| ty_tag = next (rdm); | |
| switch (ty_tag) | |
| { | |
| /* Placeholder. */ | |
| case 'p': | |
| PRINT ("_"); | |
| goto pass_return; | |
| /* Unsigned integer types. */ | |
| case 'h': | |
| case 't': | |
| case 'm': | |
| case 'y': | |
| case 'o': | |
| case 'j': | |
| demangle_const_uint (rdm); | |
| break; | |
| /* Signed integer types. */ | |
| case 'a': | |
| case 's': | |
| case 'l': | |
| case 'x': | |
| case 'n': | |
| case 'i': | |
| demangle_const_int (rdm); | |
| break; | |
| /* Boolean. */ | |
| case 'b': | |
| demangle_const_bool (rdm); | |
| break; | |
| /* Character. */ | |
| case 'c': | |
| demangle_const_char (rdm); | |
| break; | |
| default: | |
| goto fail_return; | |
| } | |
| if (!rdm->errored && rdm->verbose) | |
| { | |
| PRINT (": "); | |
| PRINT (basic_type (ty_tag)); | |
| } | |
| goto pass_return; | |
| fail_return: | |
| rdm->errored = 1; | |
| pass_return: | |
| if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | |
| -- rdm->recursion; | |
| } | |
| static void | |
| demangle_const_uint (struct rust_demangler *rdm) | |
| { | |
| size_t hex_len; | |
| uint64_t value; | |
| if (rdm->errored) | |
| return; | |
| hex_len = parse_hex_nibbles (rdm, &value); | |
| if (hex_len > 16) | |
| { | |
| /* Print anything that doesn't fit in `uint64_t` verbatim. */ | |
| PRINT ("0x"); | |
| print_str (rdm, rdm->sym + (rdm->next - hex_len), hex_len); | |
| } | |
| else if (hex_len > 0) | |
| print_uint64 (rdm, value); | |
| else | |
| rdm->errored = 1; | |
| } | |
| static void | |
| demangle_const_int (struct rust_demangler *rdm) | |
| { | |
| if (eat (rdm, 'n')) | |
| PRINT ("-"); | |
| demangle_const_uint (rdm); | |
| } | |
| static void | |
| demangle_const_bool (struct rust_demangler *rdm) | |
| { | |
| uint64_t value; | |
| if (parse_hex_nibbles (rdm, &value) != 1) | |
| { | |
| rdm->errored = 1; | |
| return; | |
| } | |
| if (value == 0) | |
| PRINT ("false"); | |
| else if (value == 1) | |
| PRINT ("true"); | |
| else | |
| rdm->errored = 1; | |
| } | |
| static void | |
| demangle_const_char (struct rust_demangler *rdm) | |
| { | |
| size_t hex_len; | |
| uint64_t value; | |
| hex_len = parse_hex_nibbles (rdm, &value); | |
| if (hex_len == 0 || hex_len > 8) | |
| { | |
| rdm->errored = 1; | |
| return; | |
| } | |
| /* Match Rust's character "debug" output as best as we can. */ | |
| PRINT ("'"); | |
| if (value == '\t') | |
| PRINT ("\\t"); | |
| else if (value == '\r') | |
| PRINT ("\\r"); | |
| else if (value == '\n') | |
| PRINT ("\\n"); | |
| else if (value > ' ' && value < '~') | |
| { | |
| /* Rust also considers many non-ASCII codepoints to be printable, but | |
| that logic is not easily ported to C. */ | |
| char c = value; | |
| print_str (rdm, &c, 1); | |
| } | |
| else | |
| { | |
| PRINT ("\\u{"); | |
| print_uint64_hex (rdm, value); | |
| PRINT ("}"); | |
| } | |
| PRINT ("'"); | |
| } | |
| /* A legacy hash is the prefix "h" followed by 16 lowercase hex digits. | |
| The hex digits must contain at least 5 distinct digits. */ | |
| static int | |
| is_legacy_prefixed_hash (struct rust_mangled_ident ident) | |
| { | |
| uint16_t seen; | |
| int nibble; | |
| size_t i, count; | |
| if (ident.ascii_len != 17 || ident.ascii[0] != 'h') | |
| return 0; | |
| seen = 0; | |
| for (i = 0; i < 16; i++) | |
| { | |
| nibble = decode_lower_hex_nibble (ident.ascii[1 + i]); | |
| if (nibble < 0) | |
| return 0; | |
| seen |= (uint16_t)1 << nibble; | |
| } | |
| /* Count how many distinct digits were seen. */ | |
| count = 0; | |
| while (seen) | |
| { | |
| if (seen & 1) | |
| count++; | |
| seen >>= 1; | |
| } | |
| return count >= 5; | |
| } | |
| int | |
| rust_demangle_callback (const char *mangled, int options, | |
| demangle_callbackref callback, void *opaque) | |
| { | |
| const char *p; | |
| struct rust_demangler rdm; | |
| struct rust_mangled_ident ident; | |
| rdm.sym = mangled; | |
| rdm.sym_len = 0; | |
| rdm.callback_opaque = opaque; | |
| rdm.callback = callback; | |
| rdm.next = 0; | |
| rdm.errored = 0; | |
| rdm.skipping_printing = 0; | |
| rdm.verbose = (options & DMGL_VERBOSE) != 0; | |
| rdm.version = 0; | |
| rdm.recursion = (options & DMGL_NO_RECURSE_LIMIT) ? RUST_NO_RECURSION_LIMIT : 0; | |
| rdm.bound_lifetime_depth = 0; | |
| /* Rust symbols always start with _R (v0) or _ZN (legacy). */ | |
| if (rdm.sym[0] == '_' && rdm.sym[1] == 'R') | |
| rdm.sym += 2; | |
| else if (rdm.sym[0] == '_' && rdm.sym[1] == 'Z' && rdm.sym[2] == 'N') | |
| { | |
| rdm.sym += 3; | |
| rdm.version = -1; | |
| } | |
| else | |
| return 0; | |
| /* Paths (v0) always start with uppercase characters. */ | |
| if (rdm.version != -1 && !ISUPPER (rdm.sym[0])) | |
| return 0; | |
| /* Rust symbols (v0) use only [_0-9a-zA-Z] characters. */ | |
| for (p = rdm.sym; *p; p++) | |
| { | |
| /* Rust v0 symbols can have '.' suffixes, ignore those. */ | |
| if (rdm.version == 0 && *p == '.') | |
| break; | |
| rdm.sym_len++; | |
| if (*p == '_' || ISALNUM (*p)) | |
| continue; | |
| /* Legacy Rust symbols can also contain [.:$] characters. | |
| Or @ in the .suffix (which will be skipped, see below). */ | |
| if (rdm.version == -1 && (*p == '$' || *p == '.' || *p == ':' | |
| || *p == '@')) | |
| continue; | |
| return 0; | |
| } | |
| /* Legacy Rust symbols need to be handled separately. */ | |
| if (rdm.version == -1) | |
| { | |
| /* Legacy Rust symbols always end with E. But can be followed by a | |
| .suffix (which we want to ignore). */ | |
| int dot_suffix = 1; | |
| while (rdm.sym_len > 0 && | |
| !(dot_suffix && rdm.sym[rdm.sym_len - 1] == 'E')) | |
| { | |
| dot_suffix = rdm.sym[rdm.sym_len - 1] == '.'; | |
| rdm.sym_len--; | |
| } | |
| if (!(rdm.sym_len > 0 && rdm.sym[rdm.sym_len - 1] == 'E')) | |
| return 0; | |
| rdm.sym_len--; | |
| /* Legacy Rust symbols also always end with a path segment | |
| that encodes a 16 hex digit hash, i.e. '17h[a-f0-9]{16}'. | |
| This early check, before any parse_ident calls, should | |
| quickly filter out most C++ symbols unrelated to Rust. */ | |
| if (!(rdm.sym_len > 19 | |
| && !memcmp (&rdm.sym[rdm.sym_len - 19], "17h", 3))) | |
| return 0; | |
| do | |
| { | |
| ident = parse_ident (&rdm); | |
| if (rdm.errored || !ident.ascii) | |
| return 0; | |
| } | |
| while (rdm.next < rdm.sym_len); | |
| /* The last path segment should be the hash. */ | |
| if (!is_legacy_prefixed_hash (ident)) | |
| return 0; | |
| /* Reset the state for a second pass, to print the symbol. */ | |
| rdm.next = 0; | |
| if (!rdm.verbose && rdm.sym_len > 19) | |
| { | |
| /* Hide the last segment, containing the hash, if not verbose. */ | |
| rdm.sym_len -= 19; | |
| } | |
| do | |
| { | |
| if (rdm.next > 0) | |
| print_str (&rdm, "::", 2); | |
| ident = parse_ident (&rdm); | |
| print_ident (&rdm, ident); | |
| } | |
| while (rdm.next < rdm.sym_len); | |
| } | |
| else | |
| { | |
| demangle_path (&rdm, 1); | |
| /* Skip instantiating crate. */ | |
| if (!rdm.errored && rdm.next < rdm.sym_len) | |
| { | |
| rdm.skipping_printing = 1; | |
| demangle_path (&rdm, 0); | |
| } | |
| /* It's an error to not reach the end. */ | |
| rdm.errored |= rdm.next != rdm.sym_len; | |
| } | |
| return !rdm.errored; | |
| } | |
| /* Growable string buffers. */ | |
| struct str_buf | |
| { | |
| char *ptr; | |
| size_t len; | |
| size_t cap; | |
| int errored; | |
| }; | |
| static void | |
| str_buf_reserve (struct str_buf *buf, size_t extra) | |
| { | |
| size_t available, min_new_cap, new_cap; | |
| char *new_ptr; | |
| /* Allocation failed before. */ | |
| if (buf->errored) | |
| return; | |
| available = buf->cap - buf->len; | |
| if (extra <= available) | |
| return; | |
| min_new_cap = buf->cap + (extra - available); | |
| /* Check for overflows. */ | |
| if (min_new_cap < buf->cap) | |
| { | |
| buf->errored = 1; | |
| return; | |
| } | |
| new_cap = buf->cap; | |
| if (new_cap == 0) | |
| new_cap = 4; | |
| /* Double capacity until sufficiently large. */ | |
| while (new_cap < min_new_cap) | |
| { | |
| new_cap *= 2; | |
| /* Check for overflows. */ | |
| if (new_cap < buf->cap) | |
| { | |
| buf->errored = 1; | |
| return; | |
| } | |
| } | |
| new_ptr = (char *)realloc (buf->ptr, new_cap); | |
| if (new_ptr == NULL) | |
| { | |
| free (buf->ptr); | |
| buf->ptr = NULL; | |
| buf->len = 0; | |
| buf->cap = 0; | |
| buf->errored = 1; | |
| } | |
| else | |
| { | |
| buf->ptr = new_ptr; | |
| buf->cap = new_cap; | |
| } | |
| } | |
| static void | |
| str_buf_append (struct str_buf *buf, const char *data, size_t len) | |
| { | |
| str_buf_reserve (buf, len); | |
| if (buf->errored) | |
| return; | |
| memcpy (buf->ptr + buf->len, data, len); | |
| buf->len += len; | |
| } | |
| static void | |
| str_buf_demangle_callback (const char *data, size_t len, void *opaque) | |
| { | |
| str_buf_append ((struct str_buf *)opaque, data, len); | |
| } | |
| char * | |
| rust_demangle (const char *mangled, int options) | |
| { | |
| struct str_buf out; | |
| int success; | |
| out.ptr = NULL; | |
| out.len = 0; | |
| out.cap = 0; | |
| out.errored = 0; | |
| success = rust_demangle_callback (mangled, options, | |
| str_buf_demangle_callback, &out); | |
| if (!success) | |
| { | |
| free (out.ptr); | |
| return NULL; | |
| } | |
| str_buf_append (&out, "\0", 1); | |
| return out.ptr; | |
| } |