123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560 |
- /* 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, '_'))
- {
- 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 (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);
- 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 (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;
- }
- return;
- }
- ty_tag = next (rdm);
- switch (ty_tag)
- {
- /* Placeholder. */
- case 'p':
- PRINT ("_");
- 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:
- rdm->errored = 1;
- return;
- }
- if (rdm->errored)
- return;
- if (rdm->verbose)
- {
- PRINT (": ");
- PRINT (basic_type (ty_tag));
- }
- }
- 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++)
- {
- rdm.sym_len++;
- if (*p == '_' || ISALNUM (*p))
- continue;
- /* Legacy Rust symbols can also contain [.:$] characters. */
- if (rdm.version == -1 && (*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. */
- 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;
- }
|