/rust/registry/src/index.crates.io-6f17d22bba15001f/rustc-demangle-0.1.24/src/legacy.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use core::char; |
2 | | use core::fmt; |
3 | | |
4 | | /// Representation of a demangled symbol name. |
5 | | pub struct Demangle<'a> { |
6 | | inner: &'a str, |
7 | | /// The number of ::-separated elements in the original name. |
8 | | elements: usize, |
9 | | } |
10 | | |
11 | | /// De-mangles a Rust symbol into a more readable version |
12 | | /// |
13 | | /// All Rust symbols by default are mangled as they contain characters that |
14 | | /// cannot be represented in all object files. The mangling mechanism is similar |
15 | | /// to C++'s, but Rust has a few specifics to handle items like lifetimes in |
16 | | /// symbols. |
17 | | /// |
18 | | /// This function will take a **mangled** symbol and return a value. When printed, |
19 | | /// the de-mangled version will be written. If the symbol does not look like |
20 | | /// a mangled symbol, the original value will be written instead. |
21 | | /// |
22 | | /// # Examples |
23 | | /// |
24 | | /// ``` |
25 | | /// use rustc_demangle::demangle; |
26 | | /// |
27 | | /// assert_eq!(demangle("_ZN4testE").to_string(), "test"); |
28 | | /// assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar"); |
29 | | /// assert_eq!(demangle("foo").to_string(), "foo"); |
30 | | /// ``` |
31 | | |
32 | | // All Rust symbols are in theory lists of "::"-separated identifiers. Some |
33 | | // assemblers, however, can't handle these characters in symbol names. To get |
34 | | // around this, we use C++-style mangling. The mangling method is: |
35 | | // |
36 | | // 1. Prefix the symbol with "_ZN" |
37 | | // 2. For each element of the path, emit the length plus the element |
38 | | // 3. End the path with "E" |
39 | | // |
40 | | // For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar". |
41 | | // |
42 | | // We're the ones printing our backtraces, so we can't rely on anything else to |
43 | | // demangle our symbols. It's *much* nicer to look at demangled symbols, so |
44 | | // this function is implemented to give us nice pretty output. |
45 | | // |
46 | | // Note that this demangler isn't quite as fancy as it could be. We have lots |
47 | | // of other information in our symbols like hashes, version, type information, |
48 | | // etc. Additionally, this doesn't handle glue symbols at all. |
49 | 0 | pub fn demangle(s: &str) -> Result<(Demangle, &str), ()> { |
50 | | // First validate the symbol. If it doesn't look like anything we're |
51 | | // expecting, we just print it literally. Note that we must handle non-Rust |
52 | | // symbols because we could have any function in the backtrace. |
53 | 0 | let inner = if s.starts_with("_ZN") { |
54 | 0 | &s[3..] |
55 | 0 | } else if s.starts_with("ZN") { |
56 | | // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" |
57 | | // form too. |
58 | 0 | &s[2..] |
59 | 0 | } else if s.starts_with("__ZN") { |
60 | | // On OSX, symbols are prefixed with an extra _ |
61 | 0 | &s[4..] |
62 | | } else { |
63 | 0 | return Err(()); |
64 | | }; |
65 | | |
66 | | // only work with ascii text |
67 | 0 | if inner.bytes().any(|c| c & 0x80 != 0) { |
68 | 0 | return Err(()); |
69 | 0 | } |
70 | 0 |
|
71 | 0 | let mut elements = 0; |
72 | 0 | let mut chars = inner.chars(); |
73 | 0 | let mut c = chars.next().ok_or(())?; |
74 | 0 | while c != 'E' { |
75 | | // Decode an identifier element's length. |
76 | 0 | if !c.is_digit(10) { |
77 | 0 | return Err(()); |
78 | 0 | } |
79 | 0 | let mut len = 0usize; |
80 | 0 | while let Some(d) = c.to_digit(10) { |
81 | 0 | len = len |
82 | 0 | .checked_mul(10) |
83 | 0 | .and_then(|len| len.checked_add(d as usize)) |
84 | 0 | .ok_or(())?; |
85 | 0 | c = chars.next().ok_or(())?; |
86 | | } |
87 | | |
88 | | // `c` already contains the first character of this identifier, skip it and |
89 | | // all the other characters of this identifier, to reach the next element. |
90 | 0 | for _ in 0..len { |
91 | 0 | c = chars.next().ok_or(())?; |
92 | | } |
93 | | |
94 | 0 | elements += 1; |
95 | | } |
96 | | |
97 | 0 | Ok((Demangle { inner, elements }, chars.as_str())) |
98 | 0 | } |
99 | | |
100 | | // Rust hashes are hex digits with an `h` prepended. |
101 | 0 | fn is_rust_hash(s: &str) -> bool { |
102 | 0 | s.starts_with('h') && s[1..].chars().all(|c| c.is_digit(16)) |
103 | 0 | } |
104 | | |
105 | | impl<'a> fmt::Display for Demangle<'a> { |
106 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
107 | 0 | // Alright, let's do this. |
108 | 0 | let mut inner = self.inner; |
109 | 0 | for element in 0..self.elements { |
110 | 0 | let mut rest = inner; |
111 | 0 | while rest.chars().next().unwrap().is_digit(10) { |
112 | 0 | rest = &rest[1..]; |
113 | 0 | } |
114 | 0 | let i: usize = inner[..(inner.len() - rest.len())].parse().unwrap(); |
115 | 0 | inner = &rest[i..]; |
116 | 0 | rest = &rest[..i]; |
117 | 0 | // Skip printing the hash if alternate formatting |
118 | 0 | // was requested. |
119 | 0 | if f.alternate() && element + 1 == self.elements && is_rust_hash(&rest) { |
120 | 0 | break; |
121 | 0 | } |
122 | 0 | if element != 0 { |
123 | 0 | f.write_str("::")?; |
124 | 0 | } |
125 | 0 | if rest.starts_with("_$") { |
126 | 0 | rest = &rest[1..]; |
127 | 0 | } |
128 | 0 | loop { |
129 | 0 | if rest.starts_with('.') { |
130 | 0 | if let Some('.') = rest[1..].chars().next() { |
131 | 0 | f.write_str("::")?; |
132 | 0 | rest = &rest[2..]; |
133 | | } else { |
134 | 0 | f.write_str(".")?; |
135 | 0 | rest = &rest[1..]; |
136 | | } |
137 | 0 | } else if rest.starts_with('$') { |
138 | 0 | let (escape, after_escape) = if let Some(end) = rest[1..].find('$') { |
139 | 0 | (&rest[1..=end], &rest[end + 2..]) |
140 | | } else { |
141 | 0 | break; |
142 | | }; |
143 | | |
144 | | // see src/librustc_codegen_utils/symbol_names/legacy.rs for these mappings |
145 | 0 | let unescaped = match escape { |
146 | 0 | "SP" => "@", |
147 | 0 | "BP" => "*", |
148 | 0 | "RF" => "&", |
149 | 0 | "LT" => "<", |
150 | 0 | "GT" => ">", |
151 | 0 | "LP" => "(", |
152 | 0 | "RP" => ")", |
153 | 0 | "C" => ",", |
154 | | |
155 | | _ => { |
156 | 0 | if escape.starts_with('u') { |
157 | 0 | let digits = &escape[1..]; |
158 | 0 | let all_lower_hex = digits.chars().all(|c| match c { |
159 | 0 | '0'..='9' | 'a'..='f' => true, |
160 | 0 | _ => false, |
161 | 0 | }); |
162 | 0 | let c = u32::from_str_radix(digits, 16) |
163 | 0 | .ok() |
164 | 0 | .and_then(char::from_u32); |
165 | 0 | if let (true, Some(c)) = (all_lower_hex, c) { |
166 | | // FIXME(eddyb) do we need to filter out control codepoints? |
167 | 0 | if !c.is_control() { |
168 | 0 | c.fmt(f)?; |
169 | 0 | rest = after_escape; |
170 | 0 | continue; |
171 | 0 | } |
172 | 0 | } |
173 | 0 | } |
174 | 0 | break; |
175 | | } |
176 | | }; |
177 | 0 | f.write_str(unescaped)?; |
178 | 0 | rest = after_escape; |
179 | 0 | } else if let Some(i) = rest.find(|c| c == '$' || c == '.') { |
180 | 0 | f.write_str(&rest[..i])?; |
181 | 0 | rest = &rest[i..]; |
182 | | } else { |
183 | 0 | break; |
184 | | } |
185 | | } |
186 | 0 | f.write_str(rest)?; |
187 | | } |
188 | | |
189 | 0 | Ok(()) |
190 | 0 | } |
191 | | } |
192 | | |
193 | | #[cfg(test)] |
194 | | mod tests { |
195 | | use std::prelude::v1::*; |
196 | | |
197 | | macro_rules! t { |
198 | | ($a:expr, $b:expr) => { |
199 | | assert!(ok($a, $b)) |
200 | | }; |
201 | | } |
202 | | |
203 | | macro_rules! t_err { |
204 | | ($a:expr) => { |
205 | | assert!(ok_err($a)) |
206 | | }; |
207 | | } |
208 | | |
209 | | macro_rules! t_nohash { |
210 | | ($a:expr, $b:expr) => {{ |
211 | | assert_eq!(format!("{:#}", ::demangle($a)), $b); |
212 | | }}; |
213 | | } |
214 | | |
215 | | fn ok(sym: &str, expected: &str) -> bool { |
216 | | match ::try_demangle(sym) { |
217 | | Ok(s) => { |
218 | | if s.to_string() == expected { |
219 | | true |
220 | | } else { |
221 | | println!("\n{}\n!=\n{}\n", s, expected); |
222 | | false |
223 | | } |
224 | | } |
225 | | Err(_) => { |
226 | | println!("error demangling"); |
227 | | false |
228 | | } |
229 | | } |
230 | | } |
231 | | |
232 | | fn ok_err(sym: &str) -> bool { |
233 | | match ::try_demangle(sym) { |
234 | | Ok(_) => { |
235 | | println!("succeeded in demangling"); |
236 | | false |
237 | | } |
238 | | Err(_) => ::demangle(sym).to_string() == sym, |
239 | | } |
240 | | } |
241 | | |
242 | | #[test] |
243 | | fn demangle() { |
244 | | t_err!("test"); |
245 | | t!("_ZN4testE", "test"); |
246 | | t_err!("_ZN4test"); |
247 | | t!("_ZN4test1a2bcE", "test::a::bc"); |
248 | | } |
249 | | |
250 | | #[test] |
251 | | fn demangle_dollars() { |
252 | | t!("_ZN4$RP$E", ")"); |
253 | | t!("_ZN8$RF$testE", "&test"); |
254 | | t!("_ZN8$BP$test4foobE", "*test::foob"); |
255 | | t!("_ZN9$u20$test4foobE", " test::foob"); |
256 | | t!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>"); |
257 | | } |
258 | | |
259 | | #[test] |
260 | | fn demangle_many_dollars() { |
261 | | t!("_ZN13test$u20$test4foobE", "test test::foob"); |
262 | | t!("_ZN12test$BP$test4foobE", "test*test::foob"); |
263 | | } |
264 | | |
265 | | #[test] |
266 | | fn demangle_osx() { |
267 | | t!( |
268 | | "__ZN5alloc9allocator6Layout9for_value17h02a996811f781011E", |
269 | | "alloc::allocator::Layout::for_value::h02a996811f781011" |
270 | | ); |
271 | | t!("__ZN38_$LT$core..option..Option$LT$T$GT$$GT$6unwrap18_MSG_FILE_LINE_COL17haf7cb8d5824ee659E", "<core::option::Option<T>>::unwrap::_MSG_FILE_LINE_COL::haf7cb8d5824ee659"); |
272 | | t!("__ZN4core5slice89_$LT$impl$u20$core..iter..traits..IntoIterator$u20$for$u20$$RF$$u27$a$u20$$u5b$T$u5d$$GT$9into_iter17h450e234d27262170E", "core::slice::<impl core::iter::traits::IntoIterator for &'a [T]>::into_iter::h450e234d27262170"); |
273 | | } |
274 | | |
275 | | #[test] |
276 | | fn demangle_windows() { |
277 | | t!("ZN4testE", "test"); |
278 | | t!("ZN13test$u20$test4foobE", "test test::foob"); |
279 | | t!("ZN12test$RF$test4foobE", "test&test::foob"); |
280 | | } |
281 | | |
282 | | #[test] |
283 | | fn demangle_elements_beginning_with_underscore() { |
284 | | t!("_ZN13_$LT$test$GT$E", "<test>"); |
285 | | t!("_ZN28_$u7b$$u7b$closure$u7d$$u7d$E", "{{closure}}"); |
286 | | t!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR"); |
287 | | } |
288 | | |
289 | | #[test] |
290 | | fn demangle_trait_impls() { |
291 | | t!( |
292 | | "_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE", |
293 | | "<Test + 'static as foo::Bar<Test>>::bar" |
294 | | ); |
295 | | } |
296 | | |
297 | | #[test] |
298 | | fn demangle_without_hash() { |
299 | | let s = "_ZN3foo17h05af221e174051e9E"; |
300 | | t!(s, "foo::h05af221e174051e9"); |
301 | | t_nohash!(s, "foo"); |
302 | | } |
303 | | |
304 | | #[test] |
305 | | fn demangle_without_hash_edgecases() { |
306 | | // One element, no hash. |
307 | | t_nohash!("_ZN3fooE", "foo"); |
308 | | // Two elements, no hash. |
309 | | t_nohash!("_ZN3foo3barE", "foo::bar"); |
310 | | // Longer-than-normal hash. |
311 | | t_nohash!("_ZN3foo20h05af221e174051e9abcE", "foo"); |
312 | | // Shorter-than-normal hash. |
313 | | t_nohash!("_ZN3foo5h05afE", "foo"); |
314 | | // Valid hash, but not at the end. |
315 | | t_nohash!("_ZN17h05af221e174051e93fooE", "h05af221e174051e9::foo"); |
316 | | // Not a valid hash, missing the 'h'. |
317 | | t_nohash!("_ZN3foo16ffaf221e174051e9E", "foo::ffaf221e174051e9"); |
318 | | // Not a valid hash, has a non-hex-digit. |
319 | | t_nohash!("_ZN3foo17hg5af221e174051e9E", "foo::hg5af221e174051e9"); |
320 | | } |
321 | | |
322 | | #[test] |
323 | | fn demangle_thinlto() { |
324 | | // One element, no hash. |
325 | | t!("_ZN3fooE.llvm.9D1C9369", "foo"); |
326 | | t!("_ZN3fooE.llvm.9D1C9369@@16", "foo"); |
327 | | t_nohash!( |
328 | | "_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", |
329 | | "backtrace::foo" |
330 | | ); |
331 | | } |
332 | | |
333 | | #[test] |
334 | | fn demangle_llvm_ir_branch_labels() { |
335 | | t!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut::haf9727c2edfbc47b.exit.i.i"); |
336 | | t_nohash!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut.exit.i.i"); |
337 | | } |
338 | | |
339 | | #[test] |
340 | | fn demangle_ignores_suffix_that_doesnt_look_like_a_symbol() { |
341 | | t_err!("_ZN3fooE.llvm moocow"); |
342 | | } |
343 | | |
344 | | #[test] |
345 | | fn dont_panic() { |
346 | | ::demangle("_ZN2222222222222222222222EE").to_string(); |
347 | | ::demangle("_ZN5*70527e27.ll34csaғE").to_string(); |
348 | | ::demangle("_ZN5*70527a54.ll34_$b.1E").to_string(); |
349 | | ::demangle( |
350 | | "\ |
351 | | _ZN5~saäb4e\n\ |
352 | | 2734cOsbE\n\ |
353 | | 5usage20h)3\0\0\0\0\0\0\07e2734cOsbE\ |
354 | | ", |
355 | | ) |
356 | | .to_string(); |
357 | | } |
358 | | |
359 | | #[test] |
360 | | fn invalid_no_chop() { |
361 | | t_err!("_ZNfooE"); |
362 | | } |
363 | | |
364 | | #[test] |
365 | | fn handle_assoc_types() { |
366 | | t!("_ZN151_$LT$alloc..boxed..Box$LT$alloc..boxed..FnBox$LT$A$C$$u20$Output$u3d$R$GT$$u20$$u2b$$u20$$u27$a$GT$$u20$as$u20$core..ops..function..FnOnce$LT$A$GT$$GT$9call_once17h69e8f44b3723e1caE", "<alloc::boxed::Box<alloc::boxed::FnBox<A, Output=R> + 'a> as core::ops::function::FnOnce<A>>::call_once::h69e8f44b3723e1ca"); |
367 | | } |
368 | | |
369 | | #[test] |
370 | | fn handle_bang() { |
371 | | t!( |
372 | | "_ZN88_$LT$core..result..Result$LT$$u21$$C$$u20$E$GT$$u20$as$u20$std..process..Termination$GT$6report17hfc41d0da4a40b3e8E", |
373 | | "<core::result::Result<!, E> as std::process::Termination>::report::hfc41d0da4a40b3e8" |
374 | | ); |
375 | | } |
376 | | |
377 | | #[test] |
378 | | fn demangle_utf8_idents() { |
379 | | t_nohash!( |
380 | | "_ZN11utf8_idents157_$u10e1$$u10d0$$u10ed$$u10db$$u10d4$$u10da$$u10d0$$u10d3$_$u10d2$$u10d4$$u10db$$u10e0$$u10d8$$u10d4$$u10da$$u10d8$_$u10e1$$u10d0$$u10d3$$u10d8$$u10da$$u10d8$17h21634fd5714000aaE", |
381 | | "utf8_idents::საჭმელად_გემრიელი_სადილი" |
382 | | ); |
383 | | } |
384 | | |
385 | | #[test] |
386 | | fn demangle_issue_60925() { |
387 | | t_nohash!( |
388 | | "_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h059a991a004536adE", |
389 | | "issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo" |
390 | | ); |
391 | | } |
392 | | } |