Skip to content
Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
290 lines (256 sloc) 8.52 KB
//! This crate can parse a C++ “mangled” linker symbol name into a Rust value
//! describing what the name refers to: a variable, a function, a virtual table,
//! etc. The description type implements `Display`, producing human-readable
//! text describing the mangled name. Debuggers and profilers can use this crate
//! to provide more meaningful output.
//!
//! C++ requires the compiler to choose names for linker symbols consistently
//! across compilation units, so that two compilation units that have seen the
//! same declarations can pair up definitions in one unit with references in
//! another. Almost all platforms other than Microsoft Windows follow the
//! [Itanium C++ ABI][itanium]'s rules for this.
//!
//! [itanium]: http://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle
//!
//! For example, suppose a C++ compilation unit has the definition:
//!
//! ```c++
//! namespace space {
//! int foo(int x, int y) { return x+y; }
//! }
//! ```
//!
//! The Itanium C++ ABI specifies that the linker symbol for that function must
//! be named `_ZN5space3fooEii`. This crate can parse that name into a Rust
//! value representing its structure. Formatting the value with the `format!`
//! macro or the `std::string::ToString::to_string` trait method yields the
//! string `space::foo(int, int)`, which is more meaningful to the C++
//! developer.
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(unsafe_code)]
// Clippy stuff.
#![allow(unknown_lints)]
#![allow(inline_always)]
#![cfg_attr(all(not(feature = "std"), feature = "alloc"), no_std)]
#![cfg_attr(all(not(feature = "std"), feature = "alloc"), feature(alloc))]
#[macro_use]
extern crate cfg_if;
cfg_if! {
if #[cfg(all(not(feature = "std"), feature = "alloc"))] {
extern crate core as std;
#[macro_use]
extern crate alloc;
mod imports {
pub use alloc::boxed;
pub use alloc::vec;
pub use alloc::string;
pub use alloc::borrow;
pub use alloc::collections::btree_map;
}
} else {
mod imports {
pub use std::boxed;
pub use std::vec;
pub use std::string;
pub use std::borrow;
pub use std::collections::btree_map;
}
}
}
use imports::*;
use string::String;
use vec::Vec;
#[macro_use]
mod logging;
pub mod ast;
pub mod error;
mod index_str;
mod subs;
use ast::{Demangle, Parse, ParseContext};
use error::{Error, Result};
use index_str::IndexStr;
use std::fmt;
/// Options to control the demangling process.
#[derive(Clone, Copy, Debug, Default)]
pub struct DemangleOptions {
/// Do not display function arguments.
pub no_params: bool,
}
/// A `Symbol` which owns the underlying storage for the mangled name.
pub type OwnedSymbol = Symbol<Vec<u8>>;
/// A `Symbol` which borrows the underlying storage for the mangled name.
pub type BorrowedSymbol<'a> = Symbol<&'a [u8]>;
/// A mangled symbol that has been parsed into an AST.
///
/// This is generic over some storage type `T` which can be either owned or
/// borrowed. See the `OwnedSymbol` and `BorrowedSymbol` type aliases.
#[derive(Clone, Debug, PartialEq)]
pub struct Symbol<T> {
raw: T,
substitutions: subs::SubstitutionTable,
parsed: ast::MangledName,
}
impl<T> Symbol<T>
where
T: AsRef<[u8]>,
{
/// Given some raw storage, parse the mangled symbol from it.
///
/// ```
/// use cpp_demangle::Symbol;
/// use std::string::ToString;
///
/// // First, something easy :)
///
/// let mangled = b"_ZN5space3fooEibc";
///
/// let sym = Symbol::new(&mangled[..])
/// .expect("Could not parse mangled symbol!");
///
/// let demangled = sym.to_string();
/// assert_eq!(demangled, "space::foo(int, bool, char)");
///
/// // Now let's try something a little more complicated!
///
/// let mangled =
/// b"__Z28JS_GetPropertyDescriptorByIdP9JSContextN2JS6HandleIP8JSObjectEENS2_I4jsidEENS1_13MutableHandleINS1_18PropertyDescriptorEEE";
///
/// let sym = Symbol::new(&mangled[..])
/// .expect("Could not parse mangled symbol!");
///
/// let demangled = sym.to_string();
/// assert_eq!(
/// demangled,
/// "JS_GetPropertyDescriptorById(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::MutableHandle<JS::PropertyDescriptor>)"
/// );
/// ```
pub fn new(raw: T) -> Result<Symbol<T>> {
let mut substitutions = subs::SubstitutionTable::new();
let parsed = {
let ctx = ParseContext::default();
let input = IndexStr::new(raw.as_ref());
let (parsed, tail) = ast::MangledName::parse(&ctx, &mut substitutions, input)?;
debug_assert!(ctx.recursion_level() == 0);
if tail.is_empty() {
parsed
} else {
return Err(Error::UnexpectedText.into());
}
};
let symbol = Symbol {
raw: raw,
substitutions: substitutions,
parsed: parsed,
};
log!(
"Successfully parsed '{}' as
AST = {:#?}
substitutions = {:#?}",
String::from_utf8_lossy(symbol.raw.as_ref()),
symbol.parsed,
symbol.substitutions
);
Ok(symbol)
}
/// Demangle the symbol and return it as a String.
///
/// Unlike the `ToString` implementation, this function allows options to
/// be specified.
///
/// ```
/// use cpp_demangle::{DemangleOptions, Symbol};
/// use std::string::ToString;
///
/// let mangled = b"_ZN5space3fooEibc";
///
/// let sym = Symbol::new(&mangled[..])
/// .expect("Could not parse mangled symbol!");
///
/// let demangled = sym.to_string();
/// let options = DemangleOptions::default();
/// let demangled_again = sym.demangle(&options).unwrap();
/// assert_eq!(demangled_again, demangled);
/// ```
pub fn demangle(&self, options: &DemangleOptions) -> ::std::result::Result<String, fmt::Error> {
let mut out = String::new();
{
let mut ctx = ast::DemangleContext::new(
&self.substitutions,
self.raw.as_ref(),
options,
&mut out,
);
self.parsed.demangle(&mut ctx, None)?;
}
Ok(out)
}
}
impl<'a, T> Symbol<&'a T>
where T: AsRef<[u8]> + ?Sized,
{
/// Parse a mangled symbol from input and return it and the trailing tail of
/// bytes that come after the symbol.
///
/// While `Symbol::new` will return an error if there is unexpected trailing
/// bytes, `with_tail` simply returns the trailing bytes along with the
/// parsed symbol.
///
/// ```
/// use cpp_demangle::BorrowedSymbol;
/// use std::string::ToString;
///
/// let mangled = b"_ZN5space3fooEibc and some trailing junk";
///
/// let (sym, tail) = BorrowedSymbol::with_tail(&mangled[..])
/// .expect("Could not parse mangled symbol!");
///
/// assert_eq!(tail, b" and some trailing junk");
///
/// let demangled = sym.to_string();
/// assert_eq!(demangled, "space::foo(int, bool, char)");
/// ```
pub fn with_tail(input: &'a T) -> Result<(BorrowedSymbol<'a>, &'a [u8])> {
let mut substitutions = subs::SubstitutionTable::new();
let ctx = ParseContext::default();
let idx_str = IndexStr::new(input.as_ref());
let (parsed, tail) = ast::MangledName::parse(&ctx, &mut substitutions, idx_str)?;
debug_assert!(ctx.recursion_level() == 0);
let symbol = Symbol {
raw: input.as_ref(),
substitutions: substitutions,
parsed: parsed,
};
log!(
"Successfully parsed '{}' as
AST = {:#?}
substitutions = {:#?}",
String::from_utf8_lossy(symbol.raw.as_ref()),
symbol.parsed,
symbol.substitutions
);
Ok((symbol, tail.into()))
}
}
impl<T> fmt::Display for Symbol<T>
where
T: AsRef<[u8]>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut out = String::new();
{
let options = DemangleOptions::default();
let mut ctx = ast::DemangleContext::new(
&self.substitutions,
self.raw.as_ref(),
&options,
&mut out,
);
self.parsed.demangle(&mut ctx, None).map_err(|err| {
log!("Demangling error: {:#?}", err);
fmt::Error
})?;
}
write!(f, "{}", &out)
}
}
You can’t perform that action at this time.