rust-demangle.c revision 1.1.1.5 1 1.1 christos /* Demangler for the Rust programming language
2 1.1.1.5 christos Copyright (C) 2016-2024 Free Software Foundation, Inc.
3 1.1 christos Written by David Tolnay (dtolnay (at) gmail.com).
4 1.1.1.4 christos Rewritten by Eduard-Mihai Burtescu (eddyb (at) lyken.rs) for v0 support.
5 1.1 christos
6 1.1 christos This file is part of the libiberty library.
7 1.1 christos Libiberty is free software; you can redistribute it and/or
8 1.1 christos modify it under the terms of the GNU Library General Public
9 1.1 christos License as published by the Free Software Foundation; either
10 1.1 christos version 2 of the License, or (at your option) any later version.
11 1.1 christos
12 1.1 christos In addition to the permissions in the GNU Library General Public
13 1.1 christos License, the Free Software Foundation gives you unlimited permission
14 1.1 christos to link the compiled version of this file into combinations with other
15 1.1 christos programs, and to distribute those combinations without any restriction
16 1.1 christos coming from the use of this file. (The Library Public License
17 1.1 christos restrictions do apply in other respects; for example, they cover
18 1.1 christos modification of the file, and distribution when not linked into a
19 1.1 christos combined executable.)
20 1.1 christos
21 1.1 christos Libiberty is distributed in the hope that it will be useful,
22 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of
23 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 1.1 christos Library General Public License for more details.
25 1.1 christos
26 1.1 christos You should have received a copy of the GNU Library General Public
27 1.1 christos License along with libiberty; see the file COPYING.LIB.
28 1.1 christos If not, see <http://www.gnu.org/licenses/>. */
29 1.1 christos
30 1.1 christos
31 1.1 christos #ifdef HAVE_CONFIG_H
32 1.1 christos #include "config.h"
33 1.1 christos #endif
34 1.1 christos
35 1.1 christos #include "safe-ctype.h"
36 1.1 christos
37 1.1.1.3 christos #include <inttypes.h>
38 1.1 christos #include <sys/types.h>
39 1.1 christos #include <string.h>
40 1.1 christos #include <stdio.h>
41 1.1.1.3 christos #include <stdlib.h>
42 1.1 christos
43 1.1 christos #ifdef HAVE_STRING_H
44 1.1 christos #include <string.h>
45 1.1 christos #else
46 1.1 christos extern size_t strlen(const char *s);
47 1.1 christos extern int strncmp(const char *s1, const char *s2, size_t n);
48 1.1 christos extern void *memset(void *s, int c, size_t n);
49 1.1 christos #endif
50 1.1 christos
51 1.1 christos #include <demangle.h>
52 1.1 christos #include "libiberty.h"
53 1.1 christos
54 1.1.1.3 christos struct rust_demangler
55 1.1.1.3 christos {
56 1.1.1.3 christos const char *sym;
57 1.1.1.3 christos size_t sym_len;
58 1.1 christos
59 1.1.1.3 christos void *callback_opaque;
60 1.1.1.3 christos demangle_callbackref callback;
61 1.1 christos
62 1.1.1.3 christos /* Position of the next character to read from the symbol. */
63 1.1.1.3 christos size_t next;
64 1.1 christos
65 1.1.1.3 christos /* Non-zero if any error occurred. */
66 1.1.1.3 christos int errored;
67 1.1 christos
68 1.1.1.4 christos /* Non-zero if nothing should be printed. */
69 1.1.1.4 christos int skipping_printing;
70 1.1.1.4 christos
71 1.1.1.3 christos /* Non-zero if printing should be verbose (e.g. include hashes). */
72 1.1.1.3 christos int verbose;
73 1.1 christos
74 1.1.1.3 christos /* Rust mangling version, with legacy mangling being -1. */
75 1.1.1.3 christos int version;
76 1.1.1.4 christos
77 1.1.1.4 christos /* Recursion depth. */
78 1.1.1.4 christos unsigned int recursion;
79 1.1.1.4 christos /* Maximum number of times demangle_path may be called recursively. */
80 1.1.1.4 christos #define RUST_MAX_RECURSION_COUNT 1024
81 1.1.1.4 christos #define RUST_NO_RECURSION_LIMIT ((unsigned int) -1)
82 1.1.1.4 christos
83 1.1.1.4 christos uint64_t bound_lifetime_depth;
84 1.1.1.3 christos };
85 1.1 christos
86 1.1.1.3 christos /* Parsing functions. */
87 1.1 christos
88 1.1.1.3 christos static char
89 1.1.1.3 christos peek (const struct rust_demangler *rdm)
90 1.1 christos {
91 1.1.1.3 christos if (rdm->next < rdm->sym_len)
92 1.1.1.3 christos return rdm->sym[rdm->next];
93 1.1.1.3 christos return 0;
94 1.1.1.3 christos }
95 1.1 christos
96 1.1.1.4 christos static int
97 1.1.1.4 christos eat (struct rust_demangler *rdm, char c)
98 1.1.1.4 christos {
99 1.1.1.4 christos if (peek (rdm) == c)
100 1.1.1.4 christos {
101 1.1.1.4 christos rdm->next++;
102 1.1.1.4 christos return 1;
103 1.1.1.4 christos }
104 1.1.1.4 christos else
105 1.1.1.4 christos return 0;
106 1.1.1.4 christos }
107 1.1.1.4 christos
108 1.1.1.3 christos static char
109 1.1.1.3 christos next (struct rust_demangler *rdm)
110 1.1.1.3 christos {
111 1.1.1.3 christos char c = peek (rdm);
112 1.1.1.3 christos if (!c)
113 1.1.1.3 christos rdm->errored = 1;
114 1.1.1.3 christos else
115 1.1.1.3 christos rdm->next++;
116 1.1.1.3 christos return c;
117 1.1.1.3 christos }
118 1.1.1.3 christos
119 1.1.1.4 christos static uint64_t
120 1.1.1.4 christos parse_integer_62 (struct rust_demangler *rdm)
121 1.1.1.4 christos {
122 1.1.1.4 christos char c;
123 1.1.1.4 christos uint64_t x;
124 1.1.1.4 christos
125 1.1.1.4 christos if (eat (rdm, '_'))
126 1.1.1.4 christos return 0;
127 1.1.1.4 christos
128 1.1.1.4 christos x = 0;
129 1.1.1.4 christos while (!eat (rdm, '_') && !rdm->errored)
130 1.1.1.4 christos {
131 1.1.1.4 christos c = next (rdm);
132 1.1.1.4 christos x *= 62;
133 1.1.1.4 christos if (ISDIGIT (c))
134 1.1.1.4 christos x += c - '0';
135 1.1.1.4 christos else if (ISLOWER (c))
136 1.1.1.4 christos x += 10 + (c - 'a');
137 1.1.1.4 christos else if (ISUPPER (c))
138 1.1.1.4 christos x += 10 + 26 + (c - 'A');
139 1.1.1.4 christos else
140 1.1.1.4 christos {
141 1.1.1.4 christos rdm->errored = 1;
142 1.1.1.4 christos return 0;
143 1.1.1.4 christos }
144 1.1.1.4 christos }
145 1.1.1.4 christos return x + 1;
146 1.1.1.4 christos }
147 1.1.1.4 christos
148 1.1.1.4 christos static uint64_t
149 1.1.1.4 christos parse_opt_integer_62 (struct rust_demangler *rdm, char tag)
150 1.1.1.4 christos {
151 1.1.1.4 christos if (!eat (rdm, tag))
152 1.1.1.4 christos return 0;
153 1.1.1.4 christos return 1 + parse_integer_62 (rdm);
154 1.1.1.4 christos }
155 1.1.1.4 christos
156 1.1.1.4 christos static uint64_t
157 1.1.1.4 christos parse_disambiguator (struct rust_demangler *rdm)
158 1.1.1.4 christos {
159 1.1.1.4 christos return parse_opt_integer_62 (rdm, 's');
160 1.1.1.4 christos }
161 1.1.1.4 christos
162 1.1.1.4 christos static size_t
163 1.1.1.4 christos parse_hex_nibbles (struct rust_demangler *rdm, uint64_t *value)
164 1.1.1.4 christos {
165 1.1.1.4 christos char c;
166 1.1.1.4 christos size_t hex_len;
167 1.1.1.4 christos
168 1.1.1.4 christos hex_len = 0;
169 1.1.1.4 christos *value = 0;
170 1.1.1.4 christos
171 1.1.1.4 christos while (!eat (rdm, '_'))
172 1.1.1.4 christos {
173 1.1.1.4 christos *value <<= 4;
174 1.1.1.4 christos
175 1.1.1.4 christos c = next (rdm);
176 1.1.1.4 christos if (ISDIGIT (c))
177 1.1.1.4 christos *value |= c - '0';
178 1.1.1.4 christos else if (c >= 'a' && c <= 'f')
179 1.1.1.4 christos *value |= 10 + (c - 'a');
180 1.1.1.4 christos else
181 1.1.1.4 christos {
182 1.1.1.4 christos rdm->errored = 1;
183 1.1.1.4 christos return 0;
184 1.1.1.4 christos }
185 1.1.1.4 christos hex_len++;
186 1.1.1.4 christos }
187 1.1.1.4 christos
188 1.1.1.4 christos return hex_len;
189 1.1.1.4 christos }
190 1.1.1.4 christos
191 1.1.1.3 christos struct rust_mangled_ident
192 1.1.1.3 christos {
193 1.1.1.3 christos /* ASCII part of the identifier. */
194 1.1.1.3 christos const char *ascii;
195 1.1.1.3 christos size_t ascii_len;
196 1.1.1.4 christos
197 1.1.1.4 christos /* Punycode insertion codes for Unicode codepoints, if any. */
198 1.1.1.4 christos const char *punycode;
199 1.1.1.4 christos size_t punycode_len;
200 1.1.1.3 christos };
201 1.1.1.3 christos
202 1.1.1.3 christos static struct rust_mangled_ident
203 1.1.1.3 christos parse_ident (struct rust_demangler *rdm)
204 1.1.1.3 christos {
205 1.1.1.3 christos char c;
206 1.1.1.3 christos size_t start, len;
207 1.1.1.4 christos int is_punycode = 0;
208 1.1.1.3 christos struct rust_mangled_ident ident;
209 1.1.1.3 christos
210 1.1.1.3 christos ident.ascii = NULL;
211 1.1.1.3 christos ident.ascii_len = 0;
212 1.1.1.4 christos ident.punycode = NULL;
213 1.1.1.4 christos ident.punycode_len = 0;
214 1.1.1.4 christos
215 1.1.1.4 christos if (rdm->version != -1)
216 1.1.1.4 christos is_punycode = eat (rdm, 'u');
217 1.1.1.3 christos
218 1.1.1.3 christos c = next (rdm);
219 1.1.1.3 christos if (!ISDIGIT (c))
220 1.1.1.3 christos {
221 1.1.1.3 christos rdm->errored = 1;
222 1.1.1.3 christos return ident;
223 1.1.1.3 christos }
224 1.1.1.3 christos len = c - '0';
225 1.1.1.3 christos
226 1.1.1.3 christos if (c != '0')
227 1.1.1.3 christos while (ISDIGIT (peek (rdm)))
228 1.1.1.3 christos len = len * 10 + (next (rdm) - '0');
229 1.1.1.3 christos
230 1.1.1.4 christos /* Skip past the optional `_` separator (v0). */
231 1.1.1.4 christos if (rdm->version != -1)
232 1.1.1.4 christos eat (rdm, '_');
233 1.1.1.4 christos
234 1.1.1.3 christos start = rdm->next;
235 1.1.1.3 christos rdm->next += len;
236 1.1.1.3 christos /* Check for overflows. */
237 1.1.1.3 christos if ((start > rdm->next) || (rdm->next > rdm->sym_len))
238 1.1.1.3 christos {
239 1.1.1.3 christos rdm->errored = 1;
240 1.1.1.3 christos return ident;
241 1.1.1.3 christos }
242 1.1.1.3 christos
243 1.1.1.3 christos ident.ascii = rdm->sym + start;
244 1.1.1.3 christos ident.ascii_len = len;
245 1.1.1.3 christos
246 1.1.1.4 christos if (is_punycode)
247 1.1.1.4 christos {
248 1.1.1.4 christos ident.punycode_len = 0;
249 1.1.1.4 christos while (ident.ascii_len > 0)
250 1.1.1.4 christos {
251 1.1.1.4 christos ident.ascii_len--;
252 1.1.1.4 christos
253 1.1.1.4 christos /* The last '_' is a separator between ascii & punycode. */
254 1.1.1.4 christos if (ident.ascii[ident.ascii_len] == '_')
255 1.1.1.4 christos break;
256 1.1.1.4 christos
257 1.1.1.4 christos ident.punycode_len++;
258 1.1.1.4 christos }
259 1.1.1.4 christos if (!ident.punycode_len)
260 1.1.1.4 christos {
261 1.1.1.4 christos rdm->errored = 1;
262 1.1.1.4 christos return ident;
263 1.1.1.4 christos }
264 1.1.1.4 christos ident.punycode = ident.ascii + (len - ident.punycode_len);
265 1.1.1.4 christos }
266 1.1.1.4 christos
267 1.1.1.3 christos if (ident.ascii_len == 0)
268 1.1.1.3 christos ident.ascii = NULL;
269 1.1.1.3 christos
270 1.1.1.3 christos return ident;
271 1.1.1.3 christos }
272 1.1.1.3 christos
273 1.1.1.3 christos /* Printing functions. */
274 1.1.1.3 christos
275 1.1.1.3 christos static void
276 1.1.1.3 christos print_str (struct rust_demangler *rdm, const char *data, size_t len)
277 1.1.1.3 christos {
278 1.1.1.4 christos if (!rdm->errored && !rdm->skipping_printing)
279 1.1.1.3 christos rdm->callback (data, len, rdm->callback_opaque);
280 1.1.1.3 christos }
281 1.1.1.3 christos
282 1.1.1.3 christos #define PRINT(s) print_str (rdm, s, strlen (s))
283 1.1.1.3 christos
284 1.1.1.4 christos static void
285 1.1.1.4 christos print_uint64 (struct rust_demangler *rdm, uint64_t x)
286 1.1.1.4 christos {
287 1.1.1.4 christos char s[21];
288 1.1.1.4 christos snprintf (s, 21, "%" PRIu64, x);
289 1.1.1.4 christos PRINT (s);
290 1.1.1.4 christos }
291 1.1.1.4 christos
292 1.1.1.4 christos static void
293 1.1.1.4 christos print_uint64_hex (struct rust_demangler *rdm, uint64_t x)
294 1.1.1.4 christos {
295 1.1.1.4 christos char s[17];
296 1.1.1.4 christos snprintf (s, 17, "%" PRIx64, x);
297 1.1.1.4 christos PRINT (s);
298 1.1.1.4 christos }
299 1.1.1.4 christos
300 1.1.1.3 christos /* Return a 0x0-0xf value if the char is 0-9a-f, and -1 otherwise. */
301 1.1.1.3 christos static int
302 1.1.1.3 christos decode_lower_hex_nibble (char nibble)
303 1.1.1.3 christos {
304 1.1.1.3 christos if ('0' <= nibble && nibble <= '9')
305 1.1.1.3 christos return nibble - '0';
306 1.1.1.3 christos if ('a' <= nibble && nibble <= 'f')
307 1.1.1.3 christos return 0xa + (nibble - 'a');
308 1.1.1.3 christos return -1;
309 1.1.1.3 christos }
310 1.1.1.3 christos
311 1.1.1.3 christos /* Return the unescaped character for a "$...$" escape, or 0 if invalid. */
312 1.1.1.3 christos static char
313 1.1.1.3 christos decode_legacy_escape (const char *e, size_t len, size_t *out_len)
314 1.1.1.3 christos {
315 1.1.1.3 christos char c = 0;
316 1.1.1.3 christos size_t escape_len = 0;
317 1.1.1.3 christos int lo_nibble = -1, hi_nibble = -1;
318 1.1 christos
319 1.1.1.3 christos if (len < 3 || e[0] != '$')
320 1.1 christos return 0;
321 1.1 christos
322 1.1.1.3 christos e++;
323 1.1.1.3 christos len--;
324 1.1.1.3 christos
325 1.1.1.3 christos if (e[0] == 'C')
326 1.1.1.3 christos {
327 1.1.1.3 christos escape_len = 1;
328 1.1.1.3 christos
329 1.1.1.3 christos c = ',';
330 1.1.1.3 christos }
331 1.1.1.3 christos else if (len > 2)
332 1.1.1.3 christos {
333 1.1.1.3 christos escape_len = 2;
334 1.1.1.3 christos
335 1.1.1.3 christos if (e[0] == 'S' && e[1] == 'P')
336 1.1.1.3 christos c = '@';
337 1.1.1.3 christos else if (e[0] == 'B' && e[1] == 'P')
338 1.1.1.3 christos c = '*';
339 1.1.1.3 christos else if (e[0] == 'R' && e[1] == 'F')
340 1.1.1.3 christos c = '&';
341 1.1.1.3 christos else if (e[0] == 'L' && e[1] == 'T')
342 1.1.1.3 christos c = '<';
343 1.1.1.3 christos else if (e[0] == 'G' && e[1] == 'T')
344 1.1.1.3 christos c = '>';
345 1.1.1.3 christos else if (e[0] == 'L' && e[1] == 'P')
346 1.1.1.3 christos c = '(';
347 1.1.1.3 christos else if (e[0] == 'R' && e[1] == 'P')
348 1.1.1.3 christos c = ')';
349 1.1.1.3 christos else if (e[0] == 'u' && len > 3)
350 1.1.1.3 christos {
351 1.1.1.3 christos escape_len = 3;
352 1.1.1.3 christos
353 1.1.1.3 christos hi_nibble = decode_lower_hex_nibble (e[1]);
354 1.1.1.3 christos if (hi_nibble < 0)
355 1.1.1.3 christos return 0;
356 1.1.1.3 christos lo_nibble = decode_lower_hex_nibble (e[2]);
357 1.1.1.3 christos if (lo_nibble < 0)
358 1.1.1.3 christos return 0;
359 1.1.1.3 christos
360 1.1.1.3 christos /* Only allow non-control ASCII characters. */
361 1.1.1.3 christos if (hi_nibble > 7)
362 1.1.1.3 christos return 0;
363 1.1.1.3 christos c = (hi_nibble << 4) | lo_nibble;
364 1.1.1.3 christos if (c < 0x20)
365 1.1.1.3 christos return 0;
366 1.1.1.3 christos }
367 1.1.1.3 christos }
368 1.1.1.3 christos
369 1.1.1.3 christos if (!c || len <= escape_len || e[escape_len] != '$')
370 1.1 christos return 0;
371 1.1 christos
372 1.1.1.3 christos *out_len = 2 + escape_len;
373 1.1.1.3 christos return c;
374 1.1 christos }
375 1.1 christos
376 1.1.1.3 christos static void
377 1.1.1.3 christos print_ident (struct rust_demangler *rdm, struct rust_mangled_ident ident)
378 1.1.1.3 christos {
379 1.1.1.3 christos char unescaped;
380 1.1.1.4 christos uint8_t *out, *p, d;
381 1.1.1.4 christos size_t len, cap, punycode_pos, j;
382 1.1.1.4 christos /* Punycode parameters and state. */
383 1.1.1.4 christos uint32_t c;
384 1.1.1.4 christos size_t base, t_min, t_max, skew, damp, bias, i;
385 1.1.1.4 christos size_t delta, w, k, t;
386 1.1.1.3 christos
387 1.1.1.4 christos if (rdm->errored || rdm->skipping_printing)
388 1.1.1.3 christos return;
389 1.1.1.3 christos
390 1.1.1.3 christos if (rdm->version == -1)
391 1.1.1.3 christos {
392 1.1.1.3 christos /* Ignore leading underscores preceding escape sequences.
393 1.1.1.3 christos The mangler inserts an underscore to make sure the
394 1.1.1.3 christos identifier begins with a XID_Start character. */
395 1.1.1.3 christos if (ident.ascii_len >= 2 && ident.ascii[0] == '_'
396 1.1.1.3 christos && ident.ascii[1] == '$')
397 1.1.1.3 christos {
398 1.1.1.3 christos ident.ascii++;
399 1.1.1.3 christos ident.ascii_len--;
400 1.1.1.3 christos }
401 1.1.1.3 christos
402 1.1.1.3 christos while (ident.ascii_len > 0)
403 1.1.1.3 christos {
404 1.1.1.3 christos /* Handle legacy escape sequences ("$...$", ".." or "."). */
405 1.1.1.3 christos if (ident.ascii[0] == '$')
406 1.1.1.3 christos {
407 1.1.1.3 christos unescaped
408 1.1.1.3 christos = decode_legacy_escape (ident.ascii, ident.ascii_len, &len);
409 1.1.1.3 christos if (unescaped)
410 1.1.1.3 christos print_str (rdm, &unescaped, 1);
411 1.1.1.3 christos else
412 1.1.1.3 christos {
413 1.1.1.3 christos /* Unexpected escape sequence, print the rest verbatim. */
414 1.1.1.3 christos print_str (rdm, ident.ascii, ident.ascii_len);
415 1.1.1.3 christos return;
416 1.1.1.3 christos }
417 1.1.1.3 christos }
418 1.1.1.3 christos else if (ident.ascii[0] == '.')
419 1.1.1.3 christos {
420 1.1.1.3 christos if (ident.ascii_len >= 2 && ident.ascii[1] == '.')
421 1.1.1.3 christos {
422 1.1.1.3 christos /* ".." becomes "::" */
423 1.1.1.3 christos PRINT ("::");
424 1.1.1.3 christos len = 2;
425 1.1.1.3 christos }
426 1.1.1.3 christos else
427 1.1.1.3 christos {
428 1.1.1.4 christos PRINT (".");
429 1.1.1.3 christos len = 1;
430 1.1.1.3 christos }
431 1.1.1.3 christos }
432 1.1.1.3 christos else
433 1.1.1.3 christos {
434 1.1.1.3 christos /* Print everything before the next escape sequence, at once. */
435 1.1.1.3 christos for (len = 0; len < ident.ascii_len; len++)
436 1.1.1.3 christos if (ident.ascii[len] == '$' || ident.ascii[len] == '.')
437 1.1.1.3 christos break;
438 1.1.1.3 christos
439 1.1.1.3 christos print_str (rdm, ident.ascii, len);
440 1.1.1.3 christos }
441 1.1.1.3 christos
442 1.1.1.3 christos ident.ascii += len;
443 1.1.1.3 christos ident.ascii_len -= len;
444 1.1.1.3 christos }
445 1.1 christos
446 1.1.1.3 christos return;
447 1.1.1.3 christos }
448 1.1.1.4 christos
449 1.1.1.4 christos if (!ident.punycode)
450 1.1.1.4 christos {
451 1.1.1.4 christos print_str (rdm, ident.ascii, ident.ascii_len);
452 1.1.1.4 christos return;
453 1.1.1.4 christos }
454 1.1.1.4 christos
455 1.1.1.4 christos len = 0;
456 1.1.1.4 christos cap = 4;
457 1.1.1.4 christos while (cap < ident.ascii_len)
458 1.1.1.4 christos {
459 1.1.1.4 christos cap *= 2;
460 1.1.1.4 christos /* Check for overflows. */
461 1.1.1.4 christos if ((cap * 4) / 4 != cap)
462 1.1.1.4 christos {
463 1.1.1.4 christos rdm->errored = 1;
464 1.1.1.4 christos return;
465 1.1.1.4 christos }
466 1.1.1.4 christos }
467 1.1.1.4 christos
468 1.1.1.4 christos /* Store the output codepoints as groups of 4 UTF-8 bytes. */
469 1.1.1.4 christos out = (uint8_t *)malloc (cap * 4);
470 1.1.1.4 christos if (!out)
471 1.1.1.4 christos {
472 1.1.1.4 christos rdm->errored = 1;
473 1.1.1.4 christos return;
474 1.1.1.4 christos }
475 1.1.1.4 christos
476 1.1.1.4 christos /* Populate initial output from ASCII fragment. */
477 1.1.1.4 christos for (len = 0; len < ident.ascii_len; len++)
478 1.1.1.4 christos {
479 1.1.1.4 christos p = out + 4 * len;
480 1.1.1.4 christos p[0] = 0;
481 1.1.1.4 christos p[1] = 0;
482 1.1.1.4 christos p[2] = 0;
483 1.1.1.4 christos p[3] = ident.ascii[len];
484 1.1.1.4 christos }
485 1.1.1.4 christos
486 1.1.1.4 christos /* Punycode parameters and initial state. */
487 1.1.1.4 christos base = 36;
488 1.1.1.4 christos t_min = 1;
489 1.1.1.4 christos t_max = 26;
490 1.1.1.4 christos skew = 38;
491 1.1.1.4 christos damp = 700;
492 1.1.1.4 christos bias = 72;
493 1.1.1.4 christos i = 0;
494 1.1.1.4 christos c = 0x80;
495 1.1.1.4 christos
496 1.1.1.4 christos punycode_pos = 0;
497 1.1.1.4 christos while (punycode_pos < ident.punycode_len)
498 1.1.1.4 christos {
499 1.1.1.4 christos /* Read one delta value. */
500 1.1.1.4 christos delta = 0;
501 1.1.1.4 christos w = 1;
502 1.1.1.4 christos k = 0;
503 1.1.1.4 christos do
504 1.1.1.4 christos {
505 1.1.1.4 christos k += base;
506 1.1.1.4 christos t = k < bias ? 0 : (k - bias);
507 1.1.1.4 christos if (t < t_min)
508 1.1.1.4 christos t = t_min;
509 1.1.1.4 christos if (t > t_max)
510 1.1.1.4 christos t = t_max;
511 1.1.1.4 christos
512 1.1.1.4 christos if (punycode_pos >= ident.punycode_len)
513 1.1.1.4 christos goto cleanup;
514 1.1.1.4 christos d = ident.punycode[punycode_pos++];
515 1.1.1.4 christos
516 1.1.1.4 christos if (ISLOWER (d))
517 1.1.1.4 christos d = d - 'a';
518 1.1.1.4 christos else if (ISDIGIT (d))
519 1.1.1.4 christos d = 26 + (d - '0');
520 1.1.1.4 christos else
521 1.1.1.4 christos {
522 1.1.1.4 christos rdm->errored = 1;
523 1.1.1.4 christos goto cleanup;
524 1.1.1.4 christos }
525 1.1.1.4 christos
526 1.1.1.4 christos delta += d * w;
527 1.1.1.4 christos w *= base - t;
528 1.1.1.4 christos }
529 1.1.1.4 christos while (d >= t);
530 1.1.1.4 christos
531 1.1.1.4 christos /* Compute the new insert position and character. */
532 1.1.1.4 christos len++;
533 1.1.1.4 christos i += delta;
534 1.1.1.4 christos c += i / len;
535 1.1.1.4 christos i %= len;
536 1.1.1.4 christos
537 1.1.1.4 christos /* Ensure enough space is available. */
538 1.1.1.4 christos if (cap < len)
539 1.1.1.4 christos {
540 1.1.1.4 christos cap *= 2;
541 1.1.1.4 christos /* Check for overflows. */
542 1.1.1.4 christos if ((cap * 4) / 4 != cap || cap < len)
543 1.1.1.4 christos {
544 1.1.1.4 christos rdm->errored = 1;
545 1.1.1.4 christos goto cleanup;
546 1.1.1.4 christos }
547 1.1.1.4 christos }
548 1.1.1.4 christos p = (uint8_t *)realloc (out, cap * 4);
549 1.1.1.4 christos if (!p)
550 1.1.1.4 christos {
551 1.1.1.4 christos rdm->errored = 1;
552 1.1.1.4 christos goto cleanup;
553 1.1.1.4 christos }
554 1.1.1.4 christos out = p;
555 1.1.1.4 christos
556 1.1.1.4 christos /* Move the characters after the insert position. */
557 1.1.1.4 christos p = out + i * 4;
558 1.1.1.4 christos memmove (p + 4, p, (len - i - 1) * 4);
559 1.1.1.4 christos
560 1.1.1.4 christos /* Insert the new character, as UTF-8 bytes. */
561 1.1.1.4 christos p[0] = c >= 0x10000 ? 0xf0 | (c >> 18) : 0;
562 1.1.1.4 christos p[1] = c >= 0x800 ? (c < 0x10000 ? 0xe0 : 0x80) | ((c >> 12) & 0x3f) : 0;
563 1.1.1.4 christos p[2] = (c < 0x800 ? 0xc0 : 0x80) | ((c >> 6) & 0x3f);
564 1.1.1.4 christos p[3] = 0x80 | (c & 0x3f);
565 1.1.1.4 christos
566 1.1.1.4 christos /* If there are no more deltas, decoding is complete. */
567 1.1.1.4 christos if (punycode_pos == ident.punycode_len)
568 1.1.1.4 christos break;
569 1.1.1.4 christos
570 1.1.1.4 christos i++;
571 1.1.1.4 christos
572 1.1.1.4 christos /* Perform bias adaptation. */
573 1.1.1.4 christos delta /= damp;
574 1.1.1.4 christos damp = 2;
575 1.1.1.4 christos
576 1.1.1.4 christos delta += delta / len;
577 1.1.1.4 christos k = 0;
578 1.1.1.4 christos while (delta > ((base - t_min) * t_max) / 2)
579 1.1.1.4 christos {
580 1.1.1.4 christos delta /= base - t_min;
581 1.1.1.4 christos k += base;
582 1.1.1.4 christos }
583 1.1.1.4 christos bias = k + ((base - t_min + 1) * delta) / (delta + skew);
584 1.1.1.4 christos }
585 1.1.1.4 christos
586 1.1.1.4 christos /* Remove all the 0 bytes to leave behind an UTF-8 string. */
587 1.1.1.4 christos for (i = 0, j = 0; i < len * 4; i++)
588 1.1.1.4 christos if (out[i] != 0)
589 1.1.1.4 christos out[j++] = out[i];
590 1.1.1.4 christos
591 1.1.1.4 christos print_str (rdm, (const char *)out, j);
592 1.1.1.4 christos
593 1.1.1.4 christos cleanup:
594 1.1.1.4 christos free (out);
595 1.1.1.4 christos }
596 1.1.1.4 christos
597 1.1.1.4 christos /* Print the lifetime according to the previously decoded index.
598 1.1.1.4 christos An index of `0` always refers to `'_`, but starting with `1`,
599 1.1.1.4 christos indices refer to late-bound lifetimes introduced by a binder. */
600 1.1.1.4 christos static void
601 1.1.1.4 christos print_lifetime_from_index (struct rust_demangler *rdm, uint64_t lt)
602 1.1.1.4 christos {
603 1.1.1.4 christos char c;
604 1.1.1.4 christos uint64_t depth;
605 1.1.1.4 christos
606 1.1.1.4 christos PRINT ("'");
607 1.1.1.4 christos if (lt == 0)
608 1.1.1.4 christos {
609 1.1.1.4 christos PRINT ("_");
610 1.1.1.4 christos return;
611 1.1.1.4 christos }
612 1.1.1.4 christos
613 1.1.1.4 christos depth = rdm->bound_lifetime_depth - lt;
614 1.1.1.4 christos /* Try to print lifetimes alphabetically first. */
615 1.1.1.4 christos if (depth < 26)
616 1.1.1.4 christos {
617 1.1.1.4 christos c = 'a' + depth;
618 1.1.1.4 christos print_str (rdm, &c, 1);
619 1.1.1.4 christos }
620 1.1.1.4 christos else
621 1.1.1.4 christos {
622 1.1.1.4 christos /* Use `'_123` after running out of letters. */
623 1.1.1.4 christos PRINT ("_");
624 1.1.1.4 christos print_uint64 (rdm, depth);
625 1.1.1.4 christos }
626 1.1.1.4 christos }
627 1.1.1.4 christos
628 1.1.1.4 christos /* Demangling functions. */
629 1.1.1.4 christos
630 1.1.1.4 christos static void demangle_binder (struct rust_demangler *rdm);
631 1.1.1.4 christos static void demangle_path (struct rust_demangler *rdm, int in_value);
632 1.1.1.4 christos static void demangle_generic_arg (struct rust_demangler *rdm);
633 1.1.1.4 christos static void demangle_type (struct rust_demangler *rdm);
634 1.1.1.4 christos static int demangle_path_maybe_open_generics (struct rust_demangler *rdm);
635 1.1.1.4 christos static void demangle_dyn_trait (struct rust_demangler *rdm);
636 1.1.1.4 christos static void demangle_const (struct rust_demangler *rdm);
637 1.1.1.4 christos static void demangle_const_uint (struct rust_demangler *rdm);
638 1.1.1.4 christos static void demangle_const_int (struct rust_demangler *rdm);
639 1.1.1.4 christos static void demangle_const_bool (struct rust_demangler *rdm);
640 1.1.1.4 christos static void demangle_const_char (struct rust_demangler *rdm);
641 1.1.1.4 christos
642 1.1.1.4 christos /* Optionally enter a binder ('G') for late-bound lifetimes,
643 1.1.1.4 christos printing e.g. `for<'a, 'b> `, and make those lifetimes visible
644 1.1.1.4 christos to the caller (via depth level, which the caller should reset). */
645 1.1.1.4 christos static void
646 1.1.1.4 christos demangle_binder (struct rust_demangler *rdm)
647 1.1.1.4 christos {
648 1.1.1.4 christos uint64_t i, bound_lifetimes;
649 1.1.1.4 christos
650 1.1.1.4 christos if (rdm->errored)
651 1.1.1.4 christos return;
652 1.1.1.4 christos
653 1.1.1.4 christos bound_lifetimes = parse_opt_integer_62 (rdm, 'G');
654 1.1.1.4 christos if (bound_lifetimes > 0)
655 1.1.1.4 christos {
656 1.1.1.4 christos PRINT ("for<");
657 1.1.1.4 christos for (i = 0; i < bound_lifetimes; i++)
658 1.1.1.4 christos {
659 1.1.1.4 christos if (i > 0)
660 1.1.1.4 christos PRINT (", ");
661 1.1.1.4 christos rdm->bound_lifetime_depth++;
662 1.1.1.4 christos print_lifetime_from_index (rdm, 1);
663 1.1.1.4 christos }
664 1.1.1.4 christos PRINT ("> ");
665 1.1.1.4 christos }
666 1.1.1.4 christos }
667 1.1.1.4 christos
668 1.1.1.4 christos static void
669 1.1.1.4 christos demangle_path (struct rust_demangler *rdm, int in_value)
670 1.1.1.4 christos {
671 1.1.1.4 christos char tag, ns;
672 1.1.1.4 christos int was_skipping_printing;
673 1.1.1.4 christos size_t i, backref, old_next;
674 1.1.1.4 christos uint64_t dis;
675 1.1.1.4 christos struct rust_mangled_ident name;
676 1.1.1.4 christos
677 1.1.1.4 christos if (rdm->errored)
678 1.1.1.4 christos return;
679 1.1.1.4 christos
680 1.1.1.4 christos if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
681 1.1.1.4 christos {
682 1.1.1.4 christos ++ rdm->recursion;
683 1.1.1.4 christos if (rdm->recursion > RUST_MAX_RECURSION_COUNT)
684 1.1.1.4 christos /* FIXME: There ought to be a way to report
685 1.1.1.4 christos that the recursion limit has been reached. */
686 1.1.1.4 christos goto fail_return;
687 1.1.1.4 christos }
688 1.1.1.4 christos
689 1.1.1.4 christos switch (tag = next (rdm))
690 1.1.1.4 christos {
691 1.1.1.4 christos case 'C':
692 1.1.1.4 christos dis = parse_disambiguator (rdm);
693 1.1.1.4 christos name = parse_ident (rdm);
694 1.1.1.4 christos
695 1.1.1.4 christos print_ident (rdm, name);
696 1.1.1.4 christos if (rdm->verbose)
697 1.1.1.4 christos {
698 1.1.1.4 christos PRINT ("[");
699 1.1.1.4 christos print_uint64_hex (rdm, dis);
700 1.1.1.4 christos PRINT ("]");
701 1.1.1.4 christos }
702 1.1.1.4 christos break;
703 1.1.1.4 christos case 'N':
704 1.1.1.4 christos ns = next (rdm);
705 1.1.1.4 christos if (!ISLOWER (ns) && !ISUPPER (ns))
706 1.1.1.4 christos goto fail_return;
707 1.1.1.4 christos
708 1.1.1.4 christos demangle_path (rdm, in_value);
709 1.1.1.4 christos
710 1.1.1.4 christos dis = parse_disambiguator (rdm);
711 1.1.1.4 christos name = parse_ident (rdm);
712 1.1.1.4 christos
713 1.1.1.4 christos if (ISUPPER (ns))
714 1.1.1.4 christos {
715 1.1.1.4 christos /* Special namespaces, like closures and shims. */
716 1.1.1.4 christos PRINT ("::{");
717 1.1.1.4 christos switch (ns)
718 1.1.1.4 christos {
719 1.1.1.4 christos case 'C':
720 1.1.1.4 christos PRINT ("closure");
721 1.1.1.4 christos break;
722 1.1.1.4 christos case 'S':
723 1.1.1.4 christos PRINT ("shim");
724 1.1.1.4 christos break;
725 1.1.1.4 christos default:
726 1.1.1.4 christos print_str (rdm, &ns, 1);
727 1.1.1.4 christos }
728 1.1.1.4 christos if (name.ascii || name.punycode)
729 1.1.1.4 christos {
730 1.1.1.4 christos PRINT (":");
731 1.1.1.4 christos print_ident (rdm, name);
732 1.1.1.4 christos }
733 1.1.1.4 christos PRINT ("#");
734 1.1.1.4 christos print_uint64 (rdm, dis);
735 1.1.1.4 christos PRINT ("}");
736 1.1.1.4 christos }
737 1.1.1.4 christos else
738 1.1.1.4 christos {
739 1.1.1.4 christos /* Implementation-specific/unspecified namespaces. */
740 1.1.1.4 christos
741 1.1.1.4 christos if (name.ascii || name.punycode)
742 1.1.1.4 christos {
743 1.1.1.4 christos PRINT ("::");
744 1.1.1.4 christos print_ident (rdm, name);
745 1.1.1.4 christos }
746 1.1.1.4 christos }
747 1.1.1.4 christos break;
748 1.1.1.4 christos case 'M':
749 1.1.1.4 christos case 'X':
750 1.1.1.4 christos /* Ignore the `impl`'s own path.*/
751 1.1.1.4 christos parse_disambiguator (rdm);
752 1.1.1.4 christos was_skipping_printing = rdm->skipping_printing;
753 1.1.1.4 christos rdm->skipping_printing = 1;
754 1.1.1.4 christos demangle_path (rdm, in_value);
755 1.1.1.4 christos rdm->skipping_printing = was_skipping_printing;
756 1.1.1.4 christos /* fallthrough */
757 1.1.1.4 christos case 'Y':
758 1.1.1.4 christos PRINT ("<");
759 1.1.1.4 christos demangle_type (rdm);
760 1.1.1.4 christos if (tag != 'M')
761 1.1.1.4 christos {
762 1.1.1.4 christos PRINT (" as ");
763 1.1.1.4 christos demangle_path (rdm, 0);
764 1.1.1.4 christos }
765 1.1.1.4 christos PRINT (">");
766 1.1.1.4 christos break;
767 1.1.1.4 christos case 'I':
768 1.1.1.4 christos demangle_path (rdm, in_value);
769 1.1.1.4 christos if (in_value)
770 1.1.1.4 christos PRINT ("::");
771 1.1.1.4 christos PRINT ("<");
772 1.1.1.4 christos for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
773 1.1.1.4 christos {
774 1.1.1.4 christos if (i > 0)
775 1.1.1.4 christos PRINT (", ");
776 1.1.1.4 christos demangle_generic_arg (rdm);
777 1.1.1.4 christos }
778 1.1.1.4 christos PRINT (">");
779 1.1.1.4 christos break;
780 1.1.1.4 christos case 'B':
781 1.1.1.4 christos backref = parse_integer_62 (rdm);
782 1.1.1.4 christos if (!rdm->skipping_printing)
783 1.1.1.4 christos {
784 1.1.1.4 christos old_next = rdm->next;
785 1.1.1.4 christos rdm->next = backref;
786 1.1.1.4 christos demangle_path (rdm, in_value);
787 1.1.1.4 christos rdm->next = old_next;
788 1.1.1.4 christos }
789 1.1.1.4 christos break;
790 1.1.1.4 christos default:
791 1.1.1.4 christos goto fail_return;
792 1.1.1.4 christos }
793 1.1.1.4 christos goto pass_return;
794 1.1.1.4 christos
795 1.1.1.4 christos fail_return:
796 1.1.1.4 christos rdm->errored = 1;
797 1.1.1.4 christos pass_return:
798 1.1.1.4 christos if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
799 1.1.1.4 christos -- rdm->recursion;
800 1.1.1.4 christos }
801 1.1.1.4 christos
802 1.1.1.4 christos static void
803 1.1.1.4 christos demangle_generic_arg (struct rust_demangler *rdm)
804 1.1.1.4 christos {
805 1.1.1.4 christos uint64_t lt;
806 1.1.1.4 christos if (eat (rdm, 'L'))
807 1.1.1.4 christos {
808 1.1.1.4 christos lt = parse_integer_62 (rdm);
809 1.1.1.4 christos print_lifetime_from_index (rdm, lt);
810 1.1.1.4 christos }
811 1.1.1.4 christos else if (eat (rdm, 'K'))
812 1.1.1.4 christos demangle_const (rdm);
813 1.1.1.4 christos else
814 1.1.1.4 christos demangle_type (rdm);
815 1.1.1.4 christos }
816 1.1.1.4 christos
817 1.1.1.4 christos static const char *
818 1.1.1.4 christos basic_type (char tag)
819 1.1.1.4 christos {
820 1.1.1.4 christos switch (tag)
821 1.1.1.4 christos {
822 1.1.1.4 christos case 'b':
823 1.1.1.4 christos return "bool";
824 1.1.1.4 christos case 'c':
825 1.1.1.4 christos return "char";
826 1.1.1.4 christos case 'e':
827 1.1.1.4 christos return "str";
828 1.1.1.4 christos case 'u':
829 1.1.1.4 christos return "()";
830 1.1.1.4 christos case 'a':
831 1.1.1.4 christos return "i8";
832 1.1.1.4 christos case 's':
833 1.1.1.4 christos return "i16";
834 1.1.1.4 christos case 'l':
835 1.1.1.4 christos return "i32";
836 1.1.1.4 christos case 'x':
837 1.1.1.4 christos return "i64";
838 1.1.1.4 christos case 'n':
839 1.1.1.4 christos return "i128";
840 1.1.1.4 christos case 'i':
841 1.1.1.4 christos return "isize";
842 1.1.1.4 christos case 'h':
843 1.1.1.4 christos return "u8";
844 1.1.1.4 christos case 't':
845 1.1.1.4 christos return "u16";
846 1.1.1.4 christos case 'm':
847 1.1.1.4 christos return "u32";
848 1.1.1.4 christos case 'y':
849 1.1.1.4 christos return "u64";
850 1.1.1.4 christos case 'o':
851 1.1.1.4 christos return "u128";
852 1.1.1.4 christos case 'j':
853 1.1.1.4 christos return "usize";
854 1.1.1.4 christos case 'f':
855 1.1.1.4 christos return "f32";
856 1.1.1.4 christos case 'd':
857 1.1.1.4 christos return "f64";
858 1.1.1.4 christos case 'z':
859 1.1.1.4 christos return "!";
860 1.1.1.4 christos case 'p':
861 1.1.1.4 christos return "_";
862 1.1.1.4 christos case 'v':
863 1.1.1.4 christos return "...";
864 1.1.1.4 christos
865 1.1.1.4 christos default:
866 1.1.1.4 christos return NULL;
867 1.1.1.4 christos }
868 1.1.1.4 christos }
869 1.1.1.4 christos
870 1.1.1.4 christos static void
871 1.1.1.4 christos demangle_type (struct rust_demangler *rdm)
872 1.1.1.4 christos {
873 1.1.1.4 christos char tag;
874 1.1.1.4 christos size_t i, old_next, backref;
875 1.1.1.4 christos uint64_t lt, old_bound_lifetime_depth;
876 1.1.1.4 christos const char *basic;
877 1.1.1.4 christos struct rust_mangled_ident abi;
878 1.1.1.4 christos
879 1.1.1.4 christos if (rdm->errored)
880 1.1.1.4 christos return;
881 1.1.1.4 christos
882 1.1.1.4 christos tag = next (rdm);
883 1.1.1.4 christos
884 1.1.1.4 christos basic = basic_type (tag);
885 1.1.1.4 christos if (basic)
886 1.1.1.4 christos {
887 1.1.1.4 christos PRINT (basic);
888 1.1.1.4 christos return;
889 1.1.1.4 christos }
890 1.1.1.4 christos
891 1.1.1.4 christos if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
892 1.1.1.4 christos {
893 1.1.1.4 christos ++ rdm->recursion;
894 1.1.1.4 christos if (rdm->recursion > RUST_MAX_RECURSION_COUNT)
895 1.1.1.4 christos /* FIXME: There ought to be a way to report
896 1.1.1.4 christos that the recursion limit has been reached. */
897 1.1.1.4 christos {
898 1.1.1.4 christos rdm->errored = 1;
899 1.1.1.4 christos -- rdm->recursion;
900 1.1.1.4 christos return;
901 1.1.1.4 christos }
902 1.1.1.4 christos }
903 1.1.1.4 christos
904 1.1.1.4 christos switch (tag)
905 1.1.1.4 christos {
906 1.1.1.4 christos case 'R':
907 1.1.1.4 christos case 'Q':
908 1.1.1.4 christos PRINT ("&");
909 1.1.1.4 christos if (eat (rdm, 'L'))
910 1.1.1.4 christos {
911 1.1.1.4 christos lt = parse_integer_62 (rdm);
912 1.1.1.4 christos if (lt)
913 1.1.1.4 christos {
914 1.1.1.4 christos print_lifetime_from_index (rdm, lt);
915 1.1.1.4 christos PRINT (" ");
916 1.1.1.4 christos }
917 1.1.1.4 christos }
918 1.1.1.4 christos if (tag != 'R')
919 1.1.1.4 christos PRINT ("mut ");
920 1.1.1.4 christos demangle_type (rdm);
921 1.1.1.4 christos break;
922 1.1.1.4 christos case 'P':
923 1.1.1.4 christos case 'O':
924 1.1.1.4 christos PRINT ("*");
925 1.1.1.4 christos if (tag != 'P')
926 1.1.1.4 christos PRINT ("mut ");
927 1.1.1.4 christos else
928 1.1.1.4 christos PRINT ("const ");
929 1.1.1.4 christos demangle_type (rdm);
930 1.1.1.4 christos break;
931 1.1.1.4 christos case 'A':
932 1.1.1.4 christos case 'S':
933 1.1.1.4 christos PRINT ("[");
934 1.1.1.4 christos demangle_type (rdm);
935 1.1.1.4 christos if (tag == 'A')
936 1.1.1.4 christos {
937 1.1.1.4 christos PRINT ("; ");
938 1.1.1.4 christos demangle_const (rdm);
939 1.1.1.4 christos }
940 1.1.1.4 christos PRINT ("]");
941 1.1.1.4 christos break;
942 1.1.1.4 christos case 'T':
943 1.1.1.4 christos PRINT ("(");
944 1.1.1.4 christos for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
945 1.1.1.4 christos {
946 1.1.1.4 christos if (i > 0)
947 1.1.1.4 christos PRINT (", ");
948 1.1.1.4 christos demangle_type (rdm);
949 1.1.1.4 christos }
950 1.1.1.4 christos if (i == 1)
951 1.1.1.4 christos PRINT (",");
952 1.1.1.4 christos PRINT (")");
953 1.1.1.4 christos break;
954 1.1.1.4 christos case 'F':
955 1.1.1.4 christos old_bound_lifetime_depth = rdm->bound_lifetime_depth;
956 1.1.1.4 christos demangle_binder (rdm);
957 1.1.1.4 christos
958 1.1.1.4 christos if (eat (rdm, 'U'))
959 1.1.1.4 christos PRINT ("unsafe ");
960 1.1.1.4 christos
961 1.1.1.4 christos if (eat (rdm, 'K'))
962 1.1.1.4 christos {
963 1.1.1.4 christos if (eat (rdm, 'C'))
964 1.1.1.4 christos {
965 1.1.1.4 christos abi.ascii = "C";
966 1.1.1.4 christos abi.ascii_len = 1;
967 1.1.1.4 christos }
968 1.1.1.4 christos else
969 1.1.1.4 christos {
970 1.1.1.4 christos abi = parse_ident (rdm);
971 1.1.1.4 christos if (!abi.ascii || abi.punycode)
972 1.1.1.4 christos {
973 1.1.1.4 christos rdm->errored = 1;
974 1.1.1.4 christos goto restore;
975 1.1.1.4 christos }
976 1.1.1.4 christos }
977 1.1.1.4 christos
978 1.1.1.4 christos PRINT ("extern \"");
979 1.1.1.4 christos
980 1.1.1.4 christos /* If the ABI had any `-`, they were replaced with `_`,
981 1.1.1.4 christos so the parts between `_` have to be re-joined with `-`. */
982 1.1.1.4 christos for (i = 0; i < abi.ascii_len; i++)
983 1.1.1.4 christos {
984 1.1.1.4 christos if (abi.ascii[i] == '_')
985 1.1.1.4 christos {
986 1.1.1.4 christos print_str (rdm, abi.ascii, i);
987 1.1.1.4 christos PRINT ("-");
988 1.1.1.4 christos abi.ascii += i + 1;
989 1.1.1.4 christos abi.ascii_len -= i + 1;
990 1.1.1.4 christos i = 0;
991 1.1.1.4 christos }
992 1.1.1.4 christos }
993 1.1.1.4 christos print_str (rdm, abi.ascii, abi.ascii_len);
994 1.1.1.4 christos
995 1.1.1.4 christos PRINT ("\" ");
996 1.1.1.4 christos }
997 1.1.1.4 christos
998 1.1.1.4 christos PRINT ("fn(");
999 1.1.1.4 christos for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
1000 1.1.1.4 christos {
1001 1.1.1.4 christos if (i > 0)
1002 1.1.1.4 christos PRINT (", ");
1003 1.1.1.4 christos demangle_type (rdm);
1004 1.1.1.4 christos }
1005 1.1.1.4 christos PRINT (")");
1006 1.1.1.4 christos
1007 1.1.1.4 christos if (eat (rdm, 'u'))
1008 1.1.1.4 christos {
1009 1.1.1.4 christos /* Skip printing the return type if it's 'u', i.e. `()`. */
1010 1.1.1.4 christos }
1011 1.1.1.4 christos else
1012 1.1.1.4 christos {
1013 1.1.1.4 christos PRINT (" -> ");
1014 1.1.1.4 christos demangle_type (rdm);
1015 1.1.1.4 christos }
1016 1.1.1.4 christos
1017 1.1.1.4 christos /* Restore `bound_lifetime_depth` to outside the binder. */
1018 1.1.1.4 christos restore:
1019 1.1.1.4 christos rdm->bound_lifetime_depth = old_bound_lifetime_depth;
1020 1.1.1.4 christos break;
1021 1.1.1.4 christos case 'D':
1022 1.1.1.4 christos PRINT ("dyn ");
1023 1.1.1.4 christos
1024 1.1.1.4 christos old_bound_lifetime_depth = rdm->bound_lifetime_depth;
1025 1.1.1.4 christos demangle_binder (rdm);
1026 1.1.1.4 christos
1027 1.1.1.4 christos for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
1028 1.1.1.4 christos {
1029 1.1.1.4 christos if (i > 0)
1030 1.1.1.4 christos PRINT (" + ");
1031 1.1.1.4 christos demangle_dyn_trait (rdm);
1032 1.1.1.4 christos }
1033 1.1.1.4 christos
1034 1.1.1.4 christos /* Restore `bound_lifetime_depth` to outside the binder. */
1035 1.1.1.4 christos rdm->bound_lifetime_depth = old_bound_lifetime_depth;
1036 1.1.1.4 christos
1037 1.1.1.4 christos if (!eat (rdm, 'L'))
1038 1.1.1.4 christos {
1039 1.1.1.4 christos rdm->errored = 1;
1040 1.1.1.4 christos return;
1041 1.1.1.4 christos }
1042 1.1.1.4 christos lt = parse_integer_62 (rdm);
1043 1.1.1.4 christos if (lt)
1044 1.1.1.4 christos {
1045 1.1.1.4 christos PRINT (" + ");
1046 1.1.1.4 christos print_lifetime_from_index (rdm, lt);
1047 1.1.1.4 christos }
1048 1.1.1.4 christos break;
1049 1.1.1.4 christos case 'B':
1050 1.1.1.4 christos backref = parse_integer_62 (rdm);
1051 1.1.1.4 christos if (!rdm->skipping_printing)
1052 1.1.1.4 christos {
1053 1.1.1.4 christos old_next = rdm->next;
1054 1.1.1.4 christos rdm->next = backref;
1055 1.1.1.4 christos demangle_type (rdm);
1056 1.1.1.4 christos rdm->next = old_next;
1057 1.1.1.4 christos }
1058 1.1.1.4 christos break;
1059 1.1.1.4 christos default:
1060 1.1.1.4 christos /* Go back to the tag, so `demangle_path` also sees it. */
1061 1.1.1.4 christos rdm->next--;
1062 1.1.1.4 christos demangle_path (rdm, 0);
1063 1.1.1.4 christos }
1064 1.1.1.4 christos
1065 1.1.1.4 christos if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
1066 1.1.1.4 christos -- rdm->recursion;
1067 1.1.1.4 christos }
1068 1.1.1.4 christos
1069 1.1.1.4 christos /* A trait in a trait object may have some "existential projections"
1070 1.1.1.4 christos (i.e. associated type bindings) after it, which should be printed
1071 1.1.1.4 christos in the `<...>` of the trait, e.g. `dyn Trait<T, U, Assoc=X>`.
1072 1.1.1.4 christos To this end, this method will keep the `<...>` of an 'I' path
1073 1.1.1.4 christos open, by omitting the `>`, and return `Ok(true)` in that case. */
1074 1.1.1.4 christos static int
1075 1.1.1.4 christos demangle_path_maybe_open_generics (struct rust_demangler *rdm)
1076 1.1.1.4 christos {
1077 1.1.1.4 christos int open;
1078 1.1.1.4 christos size_t i, old_next, backref;
1079 1.1.1.4 christos
1080 1.1.1.4 christos open = 0;
1081 1.1.1.4 christos
1082 1.1.1.4 christos if (rdm->errored)
1083 1.1.1.4 christos return open;
1084 1.1.1.4 christos
1085 1.1.1.4 christos if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
1086 1.1.1.4 christos {
1087 1.1.1.4 christos ++ rdm->recursion;
1088 1.1.1.4 christos if (rdm->recursion > RUST_MAX_RECURSION_COUNT)
1089 1.1.1.4 christos {
1090 1.1.1.4 christos /* FIXME: There ought to be a way to report
1091 1.1.1.4 christos that the recursion limit has been reached. */
1092 1.1.1.4 christos rdm->errored = 1;
1093 1.1.1.4 christos goto end_of_func;
1094 1.1.1.4 christos }
1095 1.1.1.4 christos }
1096 1.1.1.4 christos
1097 1.1.1.4 christos if (eat (rdm, 'B'))
1098 1.1.1.4 christos {
1099 1.1.1.4 christos backref = parse_integer_62 (rdm);
1100 1.1.1.4 christos if (!rdm->skipping_printing)
1101 1.1.1.4 christos {
1102 1.1.1.4 christos old_next = rdm->next;
1103 1.1.1.4 christos rdm->next = backref;
1104 1.1.1.4 christos open = demangle_path_maybe_open_generics (rdm);
1105 1.1.1.4 christos rdm->next = old_next;
1106 1.1.1.4 christos }
1107 1.1.1.4 christos }
1108 1.1.1.4 christos else if (eat (rdm, 'I'))
1109 1.1.1.4 christos {
1110 1.1.1.4 christos demangle_path (rdm, 0);
1111 1.1.1.4 christos PRINT ("<");
1112 1.1.1.4 christos open = 1;
1113 1.1.1.4 christos for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
1114 1.1.1.4 christos {
1115 1.1.1.4 christos if (i > 0)
1116 1.1.1.4 christos PRINT (", ");
1117 1.1.1.4 christos demangle_generic_arg (rdm);
1118 1.1.1.4 christos }
1119 1.1.1.4 christos }
1120 1.1.1.4 christos else
1121 1.1.1.4 christos demangle_path (rdm, 0);
1122 1.1.1.4 christos
1123 1.1.1.4 christos end_of_func:
1124 1.1.1.4 christos if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
1125 1.1.1.4 christos -- rdm->recursion;
1126 1.1.1.4 christos
1127 1.1.1.4 christos return open;
1128 1.1.1.4 christos }
1129 1.1.1.4 christos
1130 1.1.1.4 christos static void
1131 1.1.1.4 christos demangle_dyn_trait (struct rust_demangler *rdm)
1132 1.1.1.4 christos {
1133 1.1.1.4 christos int open;
1134 1.1.1.4 christos struct rust_mangled_ident name;
1135 1.1.1.4 christos
1136 1.1.1.4 christos if (rdm->errored)
1137 1.1.1.4 christos return;
1138 1.1.1.4 christos
1139 1.1.1.4 christos open = demangle_path_maybe_open_generics (rdm);
1140 1.1.1.4 christos
1141 1.1.1.4 christos while (eat (rdm, 'p'))
1142 1.1.1.4 christos {
1143 1.1.1.4 christos if (!open)
1144 1.1.1.4 christos PRINT ("<");
1145 1.1.1.4 christos else
1146 1.1.1.4 christos PRINT (", ");
1147 1.1.1.4 christos open = 1;
1148 1.1.1.4 christos
1149 1.1.1.4 christos name = parse_ident (rdm);
1150 1.1.1.4 christos print_ident (rdm, name);
1151 1.1.1.4 christos PRINT (" = ");
1152 1.1.1.4 christos demangle_type (rdm);
1153 1.1.1.4 christos }
1154 1.1.1.4 christos
1155 1.1.1.4 christos if (open)
1156 1.1.1.4 christos PRINT (">");
1157 1.1.1.4 christos }
1158 1.1.1.4 christos
1159 1.1.1.4 christos static void
1160 1.1.1.4 christos demangle_const (struct rust_demangler *rdm)
1161 1.1.1.4 christos {
1162 1.1.1.4 christos char ty_tag;
1163 1.1.1.4 christos size_t old_next, backref;
1164 1.1.1.4 christos
1165 1.1.1.4 christos if (rdm->errored)
1166 1.1.1.4 christos return;
1167 1.1.1.4 christos
1168 1.1.1.4 christos if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
1169 1.1.1.4 christos {
1170 1.1.1.4 christos ++ rdm->recursion;
1171 1.1.1.4 christos if (rdm->recursion > RUST_MAX_RECURSION_COUNT)
1172 1.1.1.4 christos /* FIXME: There ought to be a way to report
1173 1.1.1.4 christos that the recursion limit has been reached. */
1174 1.1.1.4 christos goto fail_return;
1175 1.1.1.4 christos }
1176 1.1.1.4 christos
1177 1.1.1.4 christos if (eat (rdm, 'B'))
1178 1.1.1.4 christos {
1179 1.1.1.4 christos backref = parse_integer_62 (rdm);
1180 1.1.1.4 christos if (!rdm->skipping_printing)
1181 1.1.1.4 christos {
1182 1.1.1.4 christos old_next = rdm->next;
1183 1.1.1.4 christos rdm->next = backref;
1184 1.1.1.4 christos demangle_const (rdm);
1185 1.1.1.4 christos rdm->next = old_next;
1186 1.1.1.4 christos }
1187 1.1.1.4 christos goto pass_return;
1188 1.1.1.4 christos }
1189 1.1.1.4 christos
1190 1.1.1.4 christos ty_tag = next (rdm);
1191 1.1.1.4 christos switch (ty_tag)
1192 1.1.1.4 christos {
1193 1.1.1.4 christos /* Placeholder. */
1194 1.1.1.4 christos case 'p':
1195 1.1.1.4 christos PRINT ("_");
1196 1.1.1.4 christos goto pass_return;
1197 1.1.1.4 christos
1198 1.1.1.4 christos /* Unsigned integer types. */
1199 1.1.1.4 christos case 'h':
1200 1.1.1.4 christos case 't':
1201 1.1.1.4 christos case 'm':
1202 1.1.1.4 christos case 'y':
1203 1.1.1.4 christos case 'o':
1204 1.1.1.4 christos case 'j':
1205 1.1.1.4 christos demangle_const_uint (rdm);
1206 1.1.1.4 christos break;
1207 1.1.1.4 christos
1208 1.1.1.4 christos /* Signed integer types. */
1209 1.1.1.4 christos case 'a':
1210 1.1.1.4 christos case 's':
1211 1.1.1.4 christos case 'l':
1212 1.1.1.4 christos case 'x':
1213 1.1.1.4 christos case 'n':
1214 1.1.1.4 christos case 'i':
1215 1.1.1.4 christos demangle_const_int (rdm);
1216 1.1.1.4 christos break;
1217 1.1.1.4 christos
1218 1.1.1.4 christos /* Boolean. */
1219 1.1.1.4 christos case 'b':
1220 1.1.1.4 christos demangle_const_bool (rdm);
1221 1.1.1.4 christos break;
1222 1.1.1.4 christos
1223 1.1.1.4 christos /* Character. */
1224 1.1.1.4 christos case 'c':
1225 1.1.1.4 christos demangle_const_char (rdm);
1226 1.1.1.4 christos break;
1227 1.1.1.4 christos
1228 1.1.1.4 christos default:
1229 1.1.1.4 christos goto fail_return;
1230 1.1.1.4 christos }
1231 1.1.1.4 christos
1232 1.1.1.4 christos if (!rdm->errored && rdm->verbose)
1233 1.1.1.4 christos {
1234 1.1.1.4 christos PRINT (": ");
1235 1.1.1.4 christos PRINT (basic_type (ty_tag));
1236 1.1.1.4 christos }
1237 1.1.1.4 christos goto pass_return;
1238 1.1.1.4 christos
1239 1.1.1.4 christos fail_return:
1240 1.1.1.4 christos rdm->errored = 1;
1241 1.1.1.4 christos pass_return:
1242 1.1.1.4 christos if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
1243 1.1.1.4 christos -- rdm->recursion;
1244 1.1.1.4 christos }
1245 1.1.1.4 christos
1246 1.1.1.4 christos static void
1247 1.1.1.4 christos demangle_const_uint (struct rust_demangler *rdm)
1248 1.1.1.4 christos {
1249 1.1.1.4 christos size_t hex_len;
1250 1.1.1.4 christos uint64_t value;
1251 1.1.1.4 christos
1252 1.1.1.4 christos if (rdm->errored)
1253 1.1.1.4 christos return;
1254 1.1.1.4 christos
1255 1.1.1.4 christos hex_len = parse_hex_nibbles (rdm, &value);
1256 1.1.1.4 christos
1257 1.1.1.4 christos if (hex_len > 16)
1258 1.1.1.4 christos {
1259 1.1.1.4 christos /* Print anything that doesn't fit in `uint64_t` verbatim. */
1260 1.1.1.4 christos PRINT ("0x");
1261 1.1.1.4 christos print_str (rdm, rdm->sym + (rdm->next - hex_len), hex_len);
1262 1.1.1.4 christos }
1263 1.1.1.4 christos else if (hex_len > 0)
1264 1.1.1.4 christos print_uint64 (rdm, value);
1265 1.1.1.4 christos else
1266 1.1.1.4 christos rdm->errored = 1;
1267 1.1.1.4 christos }
1268 1.1.1.4 christos
1269 1.1.1.4 christos static void
1270 1.1.1.4 christos demangle_const_int (struct rust_demangler *rdm)
1271 1.1.1.4 christos {
1272 1.1.1.4 christos if (eat (rdm, 'n'))
1273 1.1.1.4 christos PRINT ("-");
1274 1.1.1.4 christos demangle_const_uint (rdm);
1275 1.1.1.4 christos }
1276 1.1.1.4 christos
1277 1.1.1.4 christos static void
1278 1.1.1.4 christos demangle_const_bool (struct rust_demangler *rdm)
1279 1.1.1.4 christos {
1280 1.1.1.4 christos uint64_t value;
1281 1.1.1.4 christos
1282 1.1.1.4 christos if (parse_hex_nibbles (rdm, &value) != 1)
1283 1.1.1.4 christos {
1284 1.1.1.4 christos rdm->errored = 1;
1285 1.1.1.4 christos return;
1286 1.1.1.4 christos }
1287 1.1.1.4 christos
1288 1.1.1.4 christos if (value == 0)
1289 1.1.1.4 christos PRINT ("false");
1290 1.1.1.4 christos else if (value == 1)
1291 1.1.1.4 christos PRINT ("true");
1292 1.1.1.4 christos else
1293 1.1.1.4 christos rdm->errored = 1;
1294 1.1.1.4 christos }
1295 1.1.1.4 christos
1296 1.1.1.4 christos static void
1297 1.1.1.4 christos demangle_const_char (struct rust_demangler *rdm)
1298 1.1.1.4 christos {
1299 1.1.1.4 christos size_t hex_len;
1300 1.1.1.4 christos uint64_t value;
1301 1.1.1.4 christos
1302 1.1.1.4 christos hex_len = parse_hex_nibbles (rdm, &value);
1303 1.1.1.4 christos
1304 1.1.1.4 christos if (hex_len == 0 || hex_len > 8)
1305 1.1.1.4 christos {
1306 1.1.1.4 christos rdm->errored = 1;
1307 1.1.1.4 christos return;
1308 1.1.1.4 christos }
1309 1.1.1.4 christos
1310 1.1.1.4 christos /* Match Rust's character "debug" output as best as we can. */
1311 1.1.1.4 christos PRINT ("'");
1312 1.1.1.4 christos if (value == '\t')
1313 1.1.1.4 christos PRINT ("\\t");
1314 1.1.1.4 christos else if (value == '\r')
1315 1.1.1.4 christos PRINT ("\\r");
1316 1.1.1.4 christos else if (value == '\n')
1317 1.1.1.4 christos PRINT ("\\n");
1318 1.1.1.4 christos else if (value > ' ' && value < '~')
1319 1.1.1.4 christos {
1320 1.1.1.4 christos /* Rust also considers many non-ASCII codepoints to be printable, but
1321 1.1.1.4 christos that logic is not easily ported to C. */
1322 1.1.1.4 christos char c = value;
1323 1.1.1.4 christos print_str (rdm, &c, 1);
1324 1.1.1.4 christos }
1325 1.1.1.4 christos else
1326 1.1.1.4 christos {
1327 1.1.1.4 christos PRINT ("\\u{");
1328 1.1.1.4 christos print_uint64_hex (rdm, value);
1329 1.1.1.4 christos PRINT ("}");
1330 1.1.1.4 christos }
1331 1.1.1.4 christos PRINT ("'");
1332 1.1.1.3 christos }
1333 1.1.1.3 christos
1334 1.1.1.3 christos /* A legacy hash is the prefix "h" followed by 16 lowercase hex digits.
1335 1.1.1.3 christos The hex digits must contain at least 5 distinct digits. */
1336 1.1 christos static int
1337 1.1.1.3 christos is_legacy_prefixed_hash (struct rust_mangled_ident ident)
1338 1.1 christos {
1339 1.1.1.3 christos uint16_t seen;
1340 1.1.1.3 christos int nibble;
1341 1.1.1.3 christos size_t i, count;
1342 1.1 christos
1343 1.1.1.3 christos if (ident.ascii_len != 17 || ident.ascii[0] != 'h')
1344 1.1 christos return 0;
1345 1.1 christos
1346 1.1.1.3 christos seen = 0;
1347 1.1.1.3 christos for (i = 0; i < 16; i++)
1348 1.1.1.3 christos {
1349 1.1.1.3 christos nibble = decode_lower_hex_nibble (ident.ascii[1 + i]);
1350 1.1.1.3 christos if (nibble < 0)
1351 1.1.1.3 christos return 0;
1352 1.1.1.3 christos seen |= (uint16_t)1 << nibble;
1353 1.1.1.3 christos }
1354 1.1 christos
1355 1.1.1.3 christos /* Count how many distinct digits were seen. */
1356 1.1 christos count = 0;
1357 1.1.1.3 christos while (seen)
1358 1.1.1.3 christos {
1359 1.1.1.3 christos if (seen & 1)
1360 1.1.1.3 christos count++;
1361 1.1.1.3 christos seen >>= 1;
1362 1.1.1.3 christos }
1363 1.1 christos
1364 1.1.1.3 christos return count >= 5;
1365 1.1 christos }
1366 1.1 christos
1367 1.1.1.3 christos int
1368 1.1.1.3 christos rust_demangle_callback (const char *mangled, int options,
1369 1.1.1.3 christos demangle_callbackref callback, void *opaque)
1370 1.1 christos {
1371 1.1.1.3 christos const char *p;
1372 1.1.1.3 christos struct rust_demangler rdm;
1373 1.1.1.3 christos struct rust_mangled_ident ident;
1374 1.1.1.3 christos
1375 1.1.1.3 christos rdm.sym = mangled;
1376 1.1.1.3 christos rdm.sym_len = 0;
1377 1.1.1.3 christos
1378 1.1.1.3 christos rdm.callback_opaque = opaque;
1379 1.1.1.3 christos rdm.callback = callback;
1380 1.1.1.3 christos
1381 1.1.1.3 christos rdm.next = 0;
1382 1.1.1.3 christos rdm.errored = 0;
1383 1.1.1.4 christos rdm.skipping_printing = 0;
1384 1.1.1.3 christos rdm.verbose = (options & DMGL_VERBOSE) != 0;
1385 1.1.1.3 christos rdm.version = 0;
1386 1.1.1.4 christos rdm.recursion = (options & DMGL_NO_RECURSE_LIMIT) ? RUST_NO_RECURSION_LIMIT : 0;
1387 1.1.1.4 christos rdm.bound_lifetime_depth = 0;
1388 1.1.1.3 christos
1389 1.1.1.4 christos /* Rust symbols always start with _R (v0) or _ZN (legacy). */
1390 1.1.1.4 christos if (rdm.sym[0] == '_' && rdm.sym[1] == 'R')
1391 1.1.1.4 christos rdm.sym += 2;
1392 1.1.1.4 christos else if (rdm.sym[0] == '_' && rdm.sym[1] == 'Z' && rdm.sym[2] == 'N')
1393 1.1.1.3 christos {
1394 1.1.1.3 christos rdm.sym += 3;
1395 1.1.1.3 christos rdm.version = -1;
1396 1.1.1.3 christos }
1397 1.1.1.3 christos else
1398 1.1.1.3 christos return 0;
1399 1.1.1.3 christos
1400 1.1.1.4 christos /* Paths (v0) always start with uppercase characters. */
1401 1.1.1.4 christos if (rdm.version != -1 && !ISUPPER (rdm.sym[0]))
1402 1.1.1.4 christos return 0;
1403 1.1.1.4 christos
1404 1.1.1.4 christos /* Rust symbols (v0) use only [_0-9a-zA-Z] characters. */
1405 1.1.1.3 christos for (p = rdm.sym; *p; p++)
1406 1.1.1.3 christos {
1407 1.1.1.4 christos /* Rust v0 symbols can have '.' suffixes, ignore those. */
1408 1.1.1.4 christos if (rdm.version == 0 && *p == '.')
1409 1.1.1.4 christos break;
1410 1.1.1.4 christos
1411 1.1.1.3 christos rdm.sym_len++;
1412 1.1 christos
1413 1.1.1.3 christos if (*p == '_' || ISALNUM (*p))
1414 1.1.1.3 christos continue;
1415 1.1 christos
1416 1.1.1.4 christos /* Legacy Rust symbols can also contain [.:$] characters.
1417 1.1.1.4 christos Or @ in the .suffix (which will be skipped, see below). */
1418 1.1.1.4 christos if (rdm.version == -1 && (*p == '$' || *p == '.' || *p == ':'
1419 1.1.1.4 christos || *p == '@'))
1420 1.1.1.3 christos continue;
1421 1.1.1.3 christos
1422 1.1.1.3 christos return 0;
1423 1.1.1.3 christos }
1424 1.1.1.3 christos
1425 1.1.1.3 christos /* Legacy Rust symbols need to be handled separately. */
1426 1.1.1.3 christos if (rdm.version == -1)
1427 1.1.1.3 christos {
1428 1.1.1.4 christos /* Legacy Rust symbols always end with E. But can be followed by a
1429 1.1.1.4 christos .suffix (which we want to ignore). */
1430 1.1.1.4 christos int dot_suffix = 1;
1431 1.1.1.4 christos while (rdm.sym_len > 0 &&
1432 1.1.1.4 christos !(dot_suffix && rdm.sym[rdm.sym_len - 1] == 'E'))
1433 1.1.1.4 christos {
1434 1.1.1.4 christos dot_suffix = rdm.sym[rdm.sym_len - 1] == '.';
1435 1.1.1.4 christos rdm.sym_len--;
1436 1.1.1.4 christos }
1437 1.1.1.4 christos
1438 1.1.1.3 christos if (!(rdm.sym_len > 0 && rdm.sym[rdm.sym_len - 1] == 'E'))
1439 1.1.1.3 christos return 0;
1440 1.1.1.3 christos rdm.sym_len--;
1441 1.1.1.3 christos
1442 1.1.1.3 christos /* Legacy Rust symbols also always end with a path segment
1443 1.1.1.3 christos that encodes a 16 hex digit hash, i.e. '17h[a-f0-9]{16}'.
1444 1.1.1.3 christos This early check, before any parse_ident calls, should
1445 1.1.1.3 christos quickly filter out most C++ symbols unrelated to Rust. */
1446 1.1.1.3 christos if (!(rdm.sym_len > 19
1447 1.1.1.3 christos && !memcmp (&rdm.sym[rdm.sym_len - 19], "17h", 3)))
1448 1.1.1.3 christos return 0;
1449 1.1.1.3 christos
1450 1.1.1.3 christos do
1451 1.1.1.3 christos {
1452 1.1.1.3 christos ident = parse_ident (&rdm);
1453 1.1.1.3 christos if (rdm.errored || !ident.ascii)
1454 1.1.1.3 christos return 0;
1455 1.1.1.3 christos }
1456 1.1.1.3 christos while (rdm.next < rdm.sym_len);
1457 1.1.1.3 christos
1458 1.1.1.3 christos /* The last path segment should be the hash. */
1459 1.1.1.3 christos if (!is_legacy_prefixed_hash (ident))
1460 1.1.1.3 christos return 0;
1461 1.1.1.3 christos
1462 1.1.1.3 christos /* Reset the state for a second pass, to print the symbol. */
1463 1.1.1.3 christos rdm.next = 0;
1464 1.1.1.3 christos if (!rdm.verbose && rdm.sym_len > 19)
1465 1.1.1.3 christos {
1466 1.1.1.3 christos /* Hide the last segment, containing the hash, if not verbose. */
1467 1.1.1.3 christos rdm.sym_len -= 19;
1468 1.1.1.3 christos }
1469 1.1.1.3 christos
1470 1.1.1.3 christos do
1471 1.1.1.3 christos {
1472 1.1.1.3 christos if (rdm.next > 0)
1473 1.1.1.3 christos print_str (&rdm, "::", 2);
1474 1.1.1.3 christos
1475 1.1.1.3 christos ident = parse_ident (&rdm);
1476 1.1.1.3 christos print_ident (&rdm, ident);
1477 1.1.1.3 christos }
1478 1.1.1.3 christos while (rdm.next < rdm.sym_len);
1479 1.1.1.3 christos }
1480 1.1.1.3 christos else
1481 1.1.1.4 christos {
1482 1.1.1.4 christos demangle_path (&rdm, 1);
1483 1.1.1.4 christos
1484 1.1.1.4 christos /* Skip instantiating crate. */
1485 1.1.1.4 christos if (!rdm.errored && rdm.next < rdm.sym_len)
1486 1.1.1.4 christos {
1487 1.1.1.4 christos rdm.skipping_printing = 1;
1488 1.1.1.4 christos demangle_path (&rdm, 0);
1489 1.1.1.4 christos }
1490 1.1.1.4 christos
1491 1.1.1.4 christos /* It's an error to not reach the end. */
1492 1.1.1.4 christos rdm.errored |= rdm.next != rdm.sym_len;
1493 1.1.1.4 christos }
1494 1.1.1.3 christos
1495 1.1.1.3 christos return !rdm.errored;
1496 1.1.1.3 christos }
1497 1.1.1.3 christos
1498 1.1.1.3 christos /* Growable string buffers. */
1499 1.1.1.3 christos struct str_buf
1500 1.1.1.3 christos {
1501 1.1.1.3 christos char *ptr;
1502 1.1.1.3 christos size_t len;
1503 1.1.1.3 christos size_t cap;
1504 1.1.1.3 christos int errored;
1505 1.1.1.3 christos };
1506 1.1.1.3 christos
1507 1.1.1.3 christos static void
1508 1.1.1.3 christos str_buf_reserve (struct str_buf *buf, size_t extra)
1509 1.1.1.3 christos {
1510 1.1.1.3 christos size_t available, min_new_cap, new_cap;
1511 1.1.1.3 christos char *new_ptr;
1512 1.1.1.3 christos
1513 1.1.1.3 christos /* Allocation failed before. */
1514 1.1.1.3 christos if (buf->errored)
1515 1.1.1.3 christos return;
1516 1.1.1.3 christos
1517 1.1.1.3 christos available = buf->cap - buf->len;
1518 1.1.1.3 christos
1519 1.1.1.3 christos if (extra <= available)
1520 1.1 christos return;
1521 1.1 christos
1522 1.1.1.3 christos min_new_cap = buf->cap + (extra - available);
1523 1.1.1.3 christos
1524 1.1.1.3 christos /* Check for overflows. */
1525 1.1.1.3 christos if (min_new_cap < buf->cap)
1526 1.1.1.3 christos {
1527 1.1.1.3 christos buf->errored = 1;
1528 1.1.1.3 christos return;
1529 1.1.1.3 christos }
1530 1.1.1.3 christos
1531 1.1.1.3 christos new_cap = buf->cap;
1532 1.1.1.3 christos
1533 1.1.1.3 christos if (new_cap == 0)
1534 1.1.1.3 christos new_cap = 4;
1535 1.1.1.3 christos
1536 1.1.1.3 christos /* Double capacity until sufficiently large. */
1537 1.1.1.3 christos while (new_cap < min_new_cap)
1538 1.1.1.3 christos {
1539 1.1.1.3 christos new_cap *= 2;
1540 1.1.1.3 christos
1541 1.1.1.3 christos /* Check for overflows. */
1542 1.1.1.3 christos if (new_cap < buf->cap)
1543 1.1.1.3 christos {
1544 1.1.1.3 christos buf->errored = 1;
1545 1.1.1.3 christos return;
1546 1.1.1.3 christos }
1547 1.1.1.3 christos }
1548 1.1.1.3 christos
1549 1.1.1.3 christos new_ptr = (char *)realloc (buf->ptr, new_cap);
1550 1.1.1.3 christos if (new_ptr == NULL)
1551 1.1.1.3 christos {
1552 1.1.1.3 christos free (buf->ptr);
1553 1.1.1.3 christos buf->ptr = NULL;
1554 1.1.1.3 christos buf->len = 0;
1555 1.1.1.3 christos buf->cap = 0;
1556 1.1.1.3 christos buf->errored = 1;
1557 1.1.1.3 christos }
1558 1.1.1.3 christos else
1559 1.1.1.3 christos {
1560 1.1.1.3 christos buf->ptr = new_ptr;
1561 1.1.1.3 christos buf->cap = new_cap;
1562 1.1.1.3 christos }
1563 1.1 christos }
1564 1.1 christos
1565 1.1.1.3 christos static void
1566 1.1.1.3 christos str_buf_append (struct str_buf *buf, const char *data, size_t len)
1567 1.1 christos {
1568 1.1.1.3 christos str_buf_reserve (buf, len);
1569 1.1.1.3 christos if (buf->errored)
1570 1.1.1.3 christos return;
1571 1.1 christos
1572 1.1.1.3 christos memcpy (buf->ptr + buf->len, data, len);
1573 1.1.1.3 christos buf->len += len;
1574 1.1.1.3 christos }
1575 1.1 christos
1576 1.1.1.3 christos static void
1577 1.1.1.3 christos str_buf_demangle_callback (const char *data, size_t len, void *opaque)
1578 1.1.1.3 christos {
1579 1.1.1.3 christos str_buf_append ((struct str_buf *)opaque, data, len);
1580 1.1.1.3 christos }
1581 1.1.1.3 christos
1582 1.1.1.3 christos char *
1583 1.1.1.3 christos rust_demangle (const char *mangled, int options)
1584 1.1.1.3 christos {
1585 1.1.1.3 christos struct str_buf out;
1586 1.1.1.3 christos int success;
1587 1.1 christos
1588 1.1.1.3 christos out.ptr = NULL;
1589 1.1.1.3 christos out.len = 0;
1590 1.1.1.3 christos out.cap = 0;
1591 1.1.1.3 christos out.errored = 0;
1592 1.1.1.3 christos
1593 1.1.1.3 christos success = rust_demangle_callback (mangled, options,
1594 1.1.1.3 christos str_buf_demangle_callback, &out);
1595 1.1.1.3 christos
1596 1.1.1.3 christos if (!success)
1597 1.1.1.3 christos {
1598 1.1.1.3 christos free (out.ptr);
1599 1.1.1.3 christos return NULL;
1600 1.1.1.3 christos }
1601 1.1 christos
1602 1.1.1.3 christos str_buf_append (&out, "\0", 1);
1603 1.1.1.3 christos return out.ptr;
1604 1.1 christos }
1605