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