/rust/registry/src/index.crates.io-1949cf8c6b5b557f/cpp_demangle-0.4.4/src/lib.rs
Line | Count | Source |
1 | | //! This crate can parse a C++ “mangled” linker symbol name into a Rust value |
2 | | //! describing what the name refers to: a variable, a function, a virtual table, |
3 | | //! etc. The description type implements `Display`, producing human-readable |
4 | | //! text describing the mangled name. Debuggers and profilers can use this crate |
5 | | //! to provide more meaningful output. |
6 | | //! |
7 | | //! C++ requires the compiler to choose names for linker symbols consistently |
8 | | //! across compilation units, so that two compilation units that have seen the |
9 | | //! same declarations can pair up definitions in one unit with references in |
10 | | //! another. Almost all platforms other than Microsoft Windows follow the |
11 | | //! [Itanium C++ ABI][itanium]'s rules for this. |
12 | | //! |
13 | | //! [itanium]: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling |
14 | | //! |
15 | | //! For example, suppose a C++ compilation unit has the definition: |
16 | | //! |
17 | | //! ```c++ |
18 | | //! namespace space { |
19 | | //! int foo(int x, int y) { return x+y; } |
20 | | //! } |
21 | | //! ``` |
22 | | //! |
23 | | //! The Itanium C++ ABI specifies that the linker symbol for that function must |
24 | | //! be named `_ZN5space3fooEii`. This crate can parse that name into a Rust |
25 | | //! value representing its structure. Formatting the value with the `format!` |
26 | | //! macro or the `std::string::ToString::to_string` trait method yields the |
27 | | //! string `space::foo(int, int)`, which is more meaningful to the C++ |
28 | | //! developer. |
29 | | |
30 | | #![deny(missing_docs)] |
31 | | #![deny(missing_debug_implementations)] |
32 | | #![deny(unsafe_code)] |
33 | | // Clippy stuff. |
34 | | #![allow(unknown_lints)] |
35 | | #![allow(clippy::inline_always)] |
36 | | #![allow(clippy::redundant_field_names)] |
37 | | #![cfg_attr(not(feature = "std"), no_std)] |
38 | | |
39 | | #[cfg(feature = "alloc")] |
40 | | #[macro_use] |
41 | | extern crate alloc; |
42 | | |
43 | | #[cfg(not(feature = "alloc"))] |
44 | | compile_error!("`alloc` or `std` feature is required for this crate"); |
45 | | |
46 | | #[macro_use] |
47 | | mod logging; |
48 | | |
49 | | pub mod ast; |
50 | | pub mod error; |
51 | | mod index_str; |
52 | | mod subs; |
53 | | |
54 | | use alloc::string::String; |
55 | | use alloc::vec::Vec; |
56 | | use ast::{Demangle, Parse, ParseContext}; |
57 | | use core::fmt; |
58 | | use core::num::NonZeroU32; |
59 | | use error::{Error, Result}; |
60 | | use index_str::IndexStr; |
61 | | |
62 | | /// Options to control the parsing process. |
63 | | #[derive(Clone, Copy, Debug, Default)] |
64 | | #[repr(C)] |
65 | | pub struct ParseOptions { |
66 | | recursion_limit: Option<NonZeroU32>, |
67 | | } |
68 | | |
69 | | impl ParseOptions { |
70 | | /// Set the limit on recursion depth during the parsing phase. A low |
71 | | /// limit will cause valid symbols to be rejected, but a high limit may |
72 | | /// allow pathological symbols to overflow the stack during parsing. |
73 | | /// The default value is 96, which will not overflow the stack even in |
74 | | /// a debug build. |
75 | 0 | pub fn recursion_limit(mut self, limit: u32) -> Self { |
76 | 0 | self.recursion_limit = Some(NonZeroU32::new(limit).expect("Recursion limit must be > 0")); |
77 | 0 | self |
78 | 0 | } |
79 | | } |
80 | | |
81 | | /// Options to control the demangling process. |
82 | | #[derive(Clone, Copy, Debug, Default)] |
83 | | #[repr(C)] |
84 | | pub struct DemangleOptions { |
85 | | no_params: bool, |
86 | | no_return_type: bool, |
87 | | hide_expression_literal_types: bool, |
88 | | recursion_limit: Option<NonZeroU32>, |
89 | | } |
90 | | |
91 | | impl DemangleOptions { |
92 | | /// Construct a new `DemangleOptions` with the default values. |
93 | 0 | pub fn new() -> Self { |
94 | 0 | Default::default() |
95 | 0 | } |
96 | | |
97 | | /// Do not display function arguments. |
98 | 0 | pub fn no_params(mut self) -> Self { |
99 | 0 | self.no_params = true; |
100 | 0 | self |
101 | 0 | } |
102 | | |
103 | | /// Do not display the function return type. |
104 | 0 | pub fn no_return_type(mut self) -> Self { |
105 | 0 | self.no_return_type = true; |
106 | 0 | self |
107 | 0 | } |
108 | | |
109 | | /// Hide type annotations in template value parameters. |
110 | | /// These are not needed to distinguish template instances |
111 | | /// so this can make it easier to match user-provided |
112 | | /// template instance names. |
113 | 0 | pub fn hide_expression_literal_types(mut self) -> Self { |
114 | 0 | self.hide_expression_literal_types = true; |
115 | 0 | self |
116 | 0 | } |
117 | | |
118 | | /// Set the limit on recursion depth during the demangling phase. A low |
119 | | /// limit will cause valid symbols to be rejected, but a high limit may |
120 | | /// allow pathological symbols to overflow the stack during demangling. |
121 | | /// The default value is 128. |
122 | 0 | pub fn recursion_limit(mut self, limit: u32) -> Self { |
123 | 0 | self.recursion_limit = Some(NonZeroU32::new(limit).expect("Recursion limit must be > 0")); |
124 | 0 | self |
125 | 0 | } |
126 | | } |
127 | | |
128 | | /// A `Symbol` which owns the underlying storage for the mangled name. |
129 | | pub type OwnedSymbol = Symbol<Vec<u8>>; |
130 | | |
131 | | /// A `Symbol` which borrows the underlying storage for the mangled name. |
132 | | pub type BorrowedSymbol<'a> = Symbol<&'a [u8]>; |
133 | | |
134 | | /// A mangled symbol that has been parsed into an AST. |
135 | | /// |
136 | | /// This is generic over some storage type `T` which can be either owned or |
137 | | /// borrowed. See the `OwnedSymbol` and `BorrowedSymbol` type aliases. |
138 | | #[derive(Clone, Debug, PartialEq)] |
139 | | pub struct Symbol<T> { |
140 | | raw: T, |
141 | | substitutions: subs::SubstitutionTable, |
142 | | parsed: ast::MangledName, |
143 | | } |
144 | | |
145 | | impl<T> Symbol<T> |
146 | | where |
147 | | T: AsRef<[u8]>, |
148 | | { |
149 | | /// Given some raw storage, parse the mangled symbol from it with the default |
150 | | /// options. |
151 | | /// |
152 | | /// ``` |
153 | | /// use cpp_demangle::Symbol; |
154 | | /// use std::string::ToString; |
155 | | /// |
156 | | /// // First, something easy :) |
157 | | /// |
158 | | /// let mangled = b"_ZN5space3fooEibc"; |
159 | | /// |
160 | | /// let sym = Symbol::new(&mangled[..]) |
161 | | /// .expect("Could not parse mangled symbol!"); |
162 | | /// |
163 | | /// let demangled = sym.to_string(); |
164 | | /// assert_eq!(demangled, "space::foo(int, bool, char)"); |
165 | | /// |
166 | | /// // Now let's try something a little more complicated! |
167 | | /// |
168 | | /// let mangled = |
169 | | /// b"__Z28JS_GetPropertyDescriptorByIdP9JSContextN2JS6HandleIP8JSObjectEENS2_I4jsidEENS1_13MutableHandleINS1_18PropertyDescriptorEEE"; |
170 | | /// |
171 | | /// let sym = Symbol::new(&mangled[..]) |
172 | | /// .expect("Could not parse mangled symbol!"); |
173 | | /// |
174 | | /// let demangled = sym.to_string(); |
175 | | /// assert_eq!( |
176 | | /// demangled, |
177 | | /// "JS_GetPropertyDescriptorById(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::MutableHandle<JS::PropertyDescriptor>)" |
178 | | /// ); |
179 | | /// ``` |
180 | | #[inline] |
181 | 0 | pub fn new(raw: T) -> Result<Symbol<T>> { |
182 | 0 | Self::new_with_options(raw, &Default::default()) |
183 | 0 | } |
184 | | |
185 | | /// Given some raw storage, parse the mangled symbol from it. |
186 | | /// |
187 | | /// ``` |
188 | | /// use cpp_demangle::{ParseOptions, Symbol}; |
189 | | /// use std::string::ToString; |
190 | | /// |
191 | | /// // First, something easy :) |
192 | | /// |
193 | | /// let mangled = b"_ZN5space3fooEibc"; |
194 | | /// |
195 | | /// let parse_options = ParseOptions::default() |
196 | | /// .recursion_limit(1024); |
197 | | /// |
198 | | /// let sym = Symbol::new_with_options(&mangled[..], &parse_options) |
199 | | /// .expect("Could not parse mangled symbol!"); |
200 | | /// |
201 | | /// let demangled = sym.to_string(); |
202 | | /// assert_eq!(demangled, "space::foo(int, bool, char)"); |
203 | | /// |
204 | | /// // Now let's try something a little more complicated! |
205 | | /// |
206 | | /// let mangled = |
207 | | /// b"__Z28JS_GetPropertyDescriptorByIdP9JSContextN2JS6HandleIP8JSObjectEENS2_I4jsidEENS1_13MutableHandleINS1_18PropertyDescriptorEEE"; |
208 | | /// |
209 | | /// let sym = Symbol::new(&mangled[..]) |
210 | | /// .expect("Could not parse mangled symbol!"); |
211 | | /// |
212 | | /// let demangled = sym.to_string(); |
213 | | /// assert_eq!( |
214 | | /// demangled, |
215 | | /// "JS_GetPropertyDescriptorById(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::MutableHandle<JS::PropertyDescriptor>)" |
216 | | /// ); |
217 | | /// ``` |
218 | 0 | pub fn new_with_options(raw: T, options: &ParseOptions) -> Result<Symbol<T>> { |
219 | 0 | let mut substitutions = subs::SubstitutionTable::new(); |
220 | | |
221 | 0 | let parsed = { |
222 | 0 | let ctx = ParseContext::new(*options); |
223 | 0 | let input = IndexStr::new(raw.as_ref()); |
224 | | |
225 | 0 | let (parsed, tail) = ast::MangledName::parse(&ctx, &mut substitutions, input)?; |
226 | 0 | debug_assert!(ctx.recursion_level() == 0); |
227 | | |
228 | 0 | if tail.is_empty() { |
229 | 0 | parsed |
230 | | } else { |
231 | 0 | return Err(Error::UnexpectedText); |
232 | | } |
233 | | }; |
234 | | |
235 | 0 | let symbol = Symbol { |
236 | 0 | raw: raw, |
237 | 0 | substitutions: substitutions, |
238 | 0 | parsed: parsed, |
239 | 0 | }; |
240 | | |
241 | 0 | log!( |
242 | 0 | "Successfully parsed '{}' as |
243 | 0 |
|
244 | 0 | AST = {:#?} |
245 | 0 |
|
246 | 0 | substitutions = {:#?}", |
247 | 0 | String::from_utf8_lossy(symbol.raw.as_ref()), |
248 | | symbol.parsed, |
249 | | symbol.substitutions |
250 | | ); |
251 | | |
252 | 0 | Ok(symbol) |
253 | 0 | } Unexecuted instantiation: <cpp_demangle::Symbol<&str>>::new_with_options Unexecuted instantiation: <cpp_demangle::Symbol<_>>::new_with_options |
254 | | |
255 | | /// Demangle the symbol and return it as a String. |
256 | | /// |
257 | | /// Unlike the `ToString` implementation, this function allows options to |
258 | | /// be specified. |
259 | | /// |
260 | | /// ``` |
261 | | /// use cpp_demangle::{DemangleOptions, Symbol}; |
262 | | /// use std::string::ToString; |
263 | | /// |
264 | | /// let mangled = b"_ZN5space3fooEibc"; |
265 | | /// |
266 | | /// let sym = Symbol::new(&mangled[..]) |
267 | | /// .expect("Could not parse mangled symbol!"); |
268 | | /// |
269 | | /// let demangled = sym.to_string(); |
270 | | /// let options = DemangleOptions::default(); |
271 | | /// let demangled_again = sym.demangle(&options).unwrap(); |
272 | | /// assert_eq!(demangled_again, demangled); |
273 | | /// ``` |
274 | | #[allow(clippy::trivially_copy_pass_by_ref)] |
275 | 0 | pub fn demangle( |
276 | 0 | &self, |
277 | 0 | options: &DemangleOptions, |
278 | 0 | ) -> ::core::result::Result<String, fmt::Error> { |
279 | 0 | let mut out = String::new(); |
280 | | { |
281 | 0 | let mut ctx = ast::DemangleContext::new( |
282 | 0 | &self.substitutions, |
283 | 0 | self.raw.as_ref(), |
284 | 0 | *options, |
285 | 0 | &mut out, |
286 | | ); |
287 | 0 | self.parsed.demangle(&mut ctx, None)?; |
288 | | } |
289 | | |
290 | 0 | Ok(out) |
291 | 0 | } |
292 | | |
293 | | /// Demangle the symbol to a DemangleWrite, which lets the consumer be informed about |
294 | | /// syntactic structure. |
295 | | #[allow(clippy::trivially_copy_pass_by_ref)] |
296 | 0 | pub fn structured_demangle<W: DemangleWrite>( |
297 | 0 | &self, |
298 | 0 | out: &mut W, |
299 | 0 | options: &DemangleOptions, |
300 | 0 | ) -> fmt::Result { |
301 | 0 | let mut ctx = |
302 | 0 | ast::DemangleContext::new(&self.substitutions, self.raw.as_ref(), *options, out); |
303 | 0 | self.parsed.demangle(&mut ctx, None) |
304 | 0 | } Unexecuted instantiation: <cpp_demangle::Symbol<&str>>::structured_demangle::<symbolic_demangle::BoundedString> Unexecuted instantiation: <cpp_demangle::Symbol<_>>::structured_demangle::<_> |
305 | | } |
306 | | |
307 | | /// The type of a demangled AST node. |
308 | | /// This is only partial, not all nodes are represented. |
309 | | #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] |
310 | | pub enum DemangleNodeType { |
311 | | /// Entering a <prefix> production |
312 | | Prefix, |
313 | | /// Entering a <template-prefix> production |
314 | | TemplatePrefix, |
315 | | /// Entering a <template-args> production |
316 | | TemplateArgs, |
317 | | /// Entering a <unqualified-name> production |
318 | | UnqualifiedName, |
319 | | /// Entering a <template-param> production |
320 | | TemplateParam, |
321 | | /// Entering a <decltype> production |
322 | | Decltype, |
323 | | /// Entering a <data-member-prefix> production |
324 | | DataMemberPrefix, |
325 | | /// Entering a <nested-name> production |
326 | | NestedName, |
327 | | /// Entering a <special-name> production that is a vtable. |
328 | | VirtualTable, |
329 | | /// Additional values may be added in the future. Use a |
330 | | /// _ pattern for compatibility. |
331 | | __NonExhaustive, |
332 | | } |
333 | | |
334 | | /// Sink for demangled text that reports syntactic structure. |
335 | | pub trait DemangleWrite { |
336 | | /// Called when we are entering the scope of some AST node. |
337 | 0 | fn push_demangle_node(&mut self, _: DemangleNodeType) {}Unexecuted instantiation: <symbolic_demangle::BoundedString as cpp_demangle::DemangleWrite>::push_demangle_node Unexecuted instantiation: <_ as cpp_demangle::DemangleWrite>::push_demangle_node |
338 | | /// Same as `fmt::Write::write_str`. |
339 | | fn write_string(&mut self, s: &str) -> fmt::Result; |
340 | | /// Called when we are exiting the scope of some AST node for |
341 | | /// which `push_demangle_node` was called. |
342 | 0 | fn pop_demangle_node(&mut self) {}Unexecuted instantiation: <symbolic_demangle::BoundedString as cpp_demangle::DemangleWrite>::pop_demangle_node Unexecuted instantiation: <_ as cpp_demangle::DemangleWrite>::pop_demangle_node |
343 | | } |
344 | | |
345 | | impl<W: fmt::Write> DemangleWrite for W { |
346 | 0 | fn write_string(&mut self, s: &str) -> fmt::Result { |
347 | 0 | fmt::Write::write_str(self, s) |
348 | 0 | } Unexecuted instantiation: <symbolic_demangle::BoundedString as cpp_demangle::DemangleWrite>::write_string Unexecuted instantiation: <_ as cpp_demangle::DemangleWrite>::write_string |
349 | | } |
350 | | |
351 | | impl<'a, T> Symbol<&'a T> |
352 | | where |
353 | | T: AsRef<[u8]> + ?Sized, |
354 | | { |
355 | | /// Parse a mangled symbol from input and return it and the trailing tail of |
356 | | /// bytes that come after the symbol, with the default options. |
357 | | /// |
358 | | /// While `Symbol::new` will return an error if there is unexpected trailing |
359 | | /// bytes, `with_tail` simply returns the trailing bytes along with the |
360 | | /// parsed symbol. |
361 | | /// |
362 | | /// ``` |
363 | | /// use cpp_demangle::BorrowedSymbol; |
364 | | /// use std::string::ToString; |
365 | | /// |
366 | | /// let mangled = b"_ZN5space3fooEibc and some trailing junk"; |
367 | | /// |
368 | | /// let (sym, tail) = BorrowedSymbol::with_tail(&mangled[..]) |
369 | | /// .expect("Could not parse mangled symbol!"); |
370 | | /// |
371 | | /// assert_eq!(tail, b" and some trailing junk"); |
372 | | /// |
373 | | /// let demangled = sym.to_string(); |
374 | | /// assert_eq!(demangled, "space::foo(int, bool, char)"); |
375 | | /// ``` |
376 | | #[inline] |
377 | 0 | pub fn with_tail(input: &'a T) -> Result<(BorrowedSymbol<'a>, &'a [u8])> { |
378 | 0 | Self::with_tail_and_options(input, &Default::default()) |
379 | 0 | } |
380 | | |
381 | | /// Parse a mangled symbol from input and return it and the trailing tail of |
382 | | /// bytes that come after the symbol. |
383 | | /// |
384 | | /// While `Symbol::new_with_options` will return an error if there is |
385 | | /// unexpected trailing bytes, `with_tail_and_options` simply returns the |
386 | | /// trailing bytes along with the parsed symbol. |
387 | | /// |
388 | | /// ``` |
389 | | /// use cpp_demangle::{BorrowedSymbol, ParseOptions}; |
390 | | /// use std::string::ToString; |
391 | | /// |
392 | | /// let mangled = b"_ZN5space3fooEibc and some trailing junk"; |
393 | | /// |
394 | | /// let parse_options = ParseOptions::default() |
395 | | /// .recursion_limit(1024); |
396 | | /// |
397 | | /// let (sym, tail) = BorrowedSymbol::with_tail_and_options(&mangled[..], &parse_options) |
398 | | /// .expect("Could not parse mangled symbol!"); |
399 | | /// |
400 | | /// assert_eq!(tail, b" and some trailing junk"); |
401 | | /// |
402 | | /// let demangled = sym.to_string(); |
403 | | /// assert_eq!(demangled, "space::foo(int, bool, char)"); |
404 | | /// ``` |
405 | 0 | pub fn with_tail_and_options( |
406 | 0 | input: &'a T, |
407 | 0 | options: &ParseOptions, |
408 | 0 | ) -> Result<(BorrowedSymbol<'a>, &'a [u8])> { |
409 | 0 | let mut substitutions = subs::SubstitutionTable::new(); |
410 | | |
411 | 0 | let ctx = ParseContext::new(*options); |
412 | 0 | let idx_str = IndexStr::new(input.as_ref()); |
413 | 0 | let (parsed, tail) = ast::MangledName::parse(&ctx, &mut substitutions, idx_str)?; |
414 | 0 | debug_assert!(ctx.recursion_level() == 0); |
415 | | |
416 | 0 | let symbol = Symbol { |
417 | 0 | raw: input.as_ref(), |
418 | 0 | substitutions: substitutions, |
419 | 0 | parsed: parsed, |
420 | 0 | }; |
421 | | |
422 | 0 | log!( |
423 | 0 | "Successfully parsed '{}' as |
424 | 0 |
|
425 | 0 | AST = {:#?} |
426 | 0 |
|
427 | 0 | substitutions = {:#?}", |
428 | 0 | String::from_utf8_lossy(symbol.raw), |
429 | | symbol.parsed, |
430 | | symbol.substitutions |
431 | | ); |
432 | | |
433 | 0 | Ok((symbol, tail.into())) |
434 | 0 | } |
435 | | } |
436 | | |
437 | | impl<T> fmt::Display for Symbol<T> |
438 | | where |
439 | | T: AsRef<[u8]>, |
440 | | { |
441 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
442 | 0 | let mut out = String::new(); |
443 | | { |
444 | 0 | let options = DemangleOptions::default(); |
445 | 0 | let mut ctx = ast::DemangleContext::new( |
446 | 0 | &self.substitutions, |
447 | 0 | self.raw.as_ref(), |
448 | 0 | options, |
449 | 0 | &mut out, |
450 | | ); |
451 | 0 | self.parsed.demangle(&mut ctx, None).map_err(|err| { |
452 | 0 | log!("Demangling error: {:#?}", err); |
453 | 0 | fmt::Error |
454 | 0 | })?; |
455 | | } |
456 | 0 | write!(f, "{}", &out) |
457 | 0 | } |
458 | | } |