diff --git a/src/legacy.rs b/src/legacy.rs index 2050b8e..5e30797 100644 --- a/src/legacy.rs +++ b/src/legacy.rs @@ -46,23 +46,22 @@ pub struct Demangle<'a> { // Note that this demangler isn't quite as fancy as it could be. We have lots // of other information in our symbols like hashes, version, type information, // etc. Additionally, this doesn't handle glue symbols at all. -pub fn demangle(s: &str) -> Result { +pub fn demangle(s: &str) -> Result<(Demangle, &str), ()> { // First validate the symbol. If it doesn't look like anything we're // expecting, we just print it literally. Note that we must handle non-Rust // symbols because we could have any function in the backtrace. - let inner; - if s.len() > 4 && s.starts_with("_ZN") && s.ends_with('E') { - inner = &s[3..s.len() - 1]; - } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with('E') { + let inner = if s.starts_with("_ZN") { + &s[3..] + } else if s.starts_with("ZN") { // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" // form too. - inner = &s[2..s.len() - 1]; - } else if s.len() > 5 && s.starts_with("__ZN") && s.ends_with('E') { + &s[2..] + } else if s.starts_with("__ZN") { // On OSX, symbols are prefixed with an extra _ - inner = &s[4..s.len() - 1]; + &s[4..] } else { return Err(()); - } + }; // only work with ascii text if inner.bytes().any(|c| c & 0x80 != 0) { @@ -70,40 +69,34 @@ pub fn demangle(s: &str) -> Result { } let mut elements = 0; - let mut chars = inner.chars().peekable(); - loop { - let mut i = 0usize; - while let Some(&c) = chars.peek() { - if !c.is_digit(10) { - break - } - chars.next(); - let next = i.checked_mul(10) - .and_then(|i| i.checked_add(c as usize - '0' as usize)); - i = match next { - Some(i) => i, - None => { - return Err(()); - } - }; + let mut chars = inner.chars(); + let mut c = try!(chars.next().ok_or(())); + while c != 'E' { + // Decode an identifier element's length. + if !c.is_digit(10) { + return Err(()); + } + let mut len = 0usize; + while let Some(d) = c.to_digit(10) { + len = try!(len.checked_mul(10) + .and_then(|len| len.checked_add(d as usize)) + .ok_or(())); + c = try!(chars.next().ok_or(())); } - if i == 0 { - if !chars.next().is_none() { - return Err(()); - } - break; - } else if chars.by_ref().take(i).count() != i { - return Err(()); - } else { - elements += 1; + // `c` already contains the first character of this identifier, skip it and + // all the other characters of this identifier, to reach the next element. + for _ in 0..len { + c = try!(chars.next().ok_or(())); } + + elements += 1; } - Ok(Demangle { + Ok((Demangle { inner: inner, elements: elements, - }) + }, chars.as_str())) } // Rust hashes are hex digits with an `h` prepended. diff --git a/src/lib.rs b/src/lib.rs index 41bc796..19778d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,25 +81,33 @@ pub fn demangle(mut s: &str) -> Demangle { } } - // Output like LLVM IR adds extra period-delimited words. See if - // we are in that case and save the trailing words if so. let mut suffix = ""; - if let Some(i) = s.rfind("E.") { - let (head, tail) = s.split_at(i + 1); // After the E, before the period - - if is_symbol_like(tail) { - s = head; - suffix = tail; + let mut style = match legacy::demangle(s) { + Ok((d, s)) => { + suffix = s; + Some(DemangleStyle::Legacy(d)) } - } - - let style = match legacy::demangle(s) { - Ok(d) => Some(DemangleStyle::Legacy(d)), Err(()) => match v0::demangle(s) { - Ok(d) => Some(DemangleStyle::V0(d)), + Ok((d, s)) => { + suffix = s; + Some(DemangleStyle::V0(d)) + } Err(v0::Invalid) => None, }, }; + + // Output like LLVM IR adds extra period-delimited words. See if + // we are in that case and save the trailing words if so. + if !suffix.is_empty() { + if suffix.starts_with(".") && is_symbol_like(suffix) { + // Keep the suffix. + } else { + // Reset the suffix and invalidate the demangling. + suffix = ""; + style = None; + } + } + Demangle { style: style, original: s, diff --git a/src/v0.rs b/src/v0.rs index 8fdbe83..7a6c6a2 100644 --- a/src/v0.rs +++ b/src/v0.rs @@ -12,7 +12,7 @@ pub struct Demangle<'a> { /// This function will take a **mangled** symbol and return a value. When printed, /// the de-mangled version will be written. If the symbol does not look like /// a mangled symbol, the original value will be written instead. -pub fn demangle(s: &str) -> Result { +pub fn demangle(s: &str) -> Result<(Demangle, &str), Invalid> { // First validate the symbol. If it doesn't look like anything we're // expecting, we just print it literally. Note that we must handle non-Rust // symbols because we could have any function in the backtrace. @@ -47,17 +47,18 @@ pub fn demangle(s: &str) -> Result { next: 0, }; try!(parser.skip_path()); - if parser.next < parser.sym.len() { - // Instantiating crate. - try!(parser.skip_path()); - } - if parser.next != parser.sym.len() { - return Err(Invalid); + + // Instantiating crate (paths always start with uppercase characters). + match parser.sym.as_bytes().get(parser.next) { + Some(&b'A'...b'Z') => { + try!(parser.skip_path()); + } + _ => {} } - Ok(Demangle { + Ok((Demangle { inner: inner, - }) + }, &parser.sym[parser.next..])) } impl<'s> Display for Demangle<'s> { @@ -1064,4 +1065,20 @@ mod tests { ((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _))))))" ); } + + #[test] + fn demangle_thinlto() { + t_nohash!("_RC3foo.llvm.9D1C9369", "foo"); + t_nohash!("_RC3foo.llvm.9D1C9369@@16", "foo"); + t_nohash!("_RNvC9backtrace3foo.llvm.A5310EB9", "backtrace::foo"); + } + + #[test] + fn demangle_extra_suffix() { + // From alexcrichton/rustc-demangle#27: + t_nohash!( + "_RNvNtNtNtNtCs92dm3009vxr_4rand4rngs7adapter9reseeding4fork23FORK_HANDLER_REGISTERED.0.0", + "rand::rngs::adapter::reseeding::fork::FORK_HANDLER_REGISTERED.0.0" + ); + } }