diff --git a/src/lib.rs b/src/lib.rs index 64c30ad..046c6f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -441,5 +441,8 @@ mod tests { super::demangle("RYFG_FGyyEvRYFF_EvRYFFEvERLB_B_B_ERLRjB_B_B_") ) .is_err()); + // NOTE(eddyb) somewhat reduced version of the above, effectively + // ` fn()>` with a larger number of lifetimes in `...`. + assert!(write!(s, "{}", super::demangle("_RMC0FGZZZ_Eu")).is_err()); } } diff --git a/src/v0.rs b/src/v0.rs index b07f182..226900d 100644 --- a/src/v0.rs +++ b/src/v0.rs @@ -1,10 +1,12 @@ -use core::char; -use core::fmt; +use core::{char, fmt, mem}; #[allow(unused_macros)] macro_rules! write { ($($ignored:tt)*) => { - compile_error!("use `fmt::Trait::fmt(&value, self.out)` instead of write!") + compile_error!( + "use `self.print(value)` or `fmt::Trait::fmt(&value, self.out)`, \ + instead of `write!(self.out, \"{...}\", value)`" + ) }; } @@ -52,16 +54,27 @@ pub fn demangle(s: &str) -> Result<(Demangle, &str), Invalid> { } // Verify that the symbol is indeed a valid path. + let try_parse_path = |parser| { + let mut dummy_printer = Printer { + parser: Ok(parser), + out: None, + bound_lifetime_depth: 0, + }; + dummy_printer + .print_path(false) + .expect("`fmt::Error`s should be impossible without a `fmt::Formatter`"); + dummy_printer.parser + }; let mut parser = Parser { sym: inner, next: 0, depth: 0, }; - parser.skip_path()?; + parser = try_parse_path(parser)?; // Instantiating crate (paths always start with uppercase characters). if let Some(&(b'A'..=b'Z')) = parser.sym.as_bytes().get(parser.next) { - parser.skip_path()?; + parser = try_parse_path(parser)?; } Ok((Demangle { inner }, &parser.sym[parser.next..])) @@ -75,7 +88,7 @@ impl<'s> fmt::Display for Demangle<'s> { next: 0, depth: 0, }), - out: f, + out: Some(f), bound_lifetime_depth: 0, }; printer.print_path(true) @@ -439,175 +452,23 @@ impl<'s> Parser<'s> { }) } } - - fn skip_path(&mut self) -> Result<(), Invalid> { - self.push_depth()?; - - match self.next()? { - b'C' => { - self.disambiguator()?; - self.ident()?; - } - b'N' => { - self.namespace()?; - self.skip_path()?; - self.disambiguator()?; - self.ident()?; - } - b'M' => { - self.disambiguator()?; - self.skip_path()?; - self.skip_type()?; - } - b'X' => { - self.disambiguator()?; - self.skip_path()?; - self.skip_type()?; - self.skip_path()?; - } - b'Y' => { - self.skip_type()?; - self.skip_path()?; - } - b'I' => { - self.skip_path()?; - while !self.eat(b'E') { - self.skip_generic_arg()?; - } - } - b'B' => { - self.backref()?; - } - _ => return Err(Invalid), - } - - self.pop_depth(); - Ok(()) - } - - fn skip_generic_arg(&mut self) -> Result<(), Invalid> { - if self.eat(b'L') { - self.integer_62()?; - Ok(()) - } else if self.eat(b'K') { - self.skip_const() - } else { - self.skip_type() - } - } - - fn skip_type(&mut self) -> Result<(), Invalid> { - self.push_depth()?; - - match self.next()? { - tag if basic_type(tag).is_some() => {} - - b'R' | b'Q' => { - if self.eat(b'L') { - self.integer_62()?; - } - self.skip_type()?; - } - b'P' | b'O' | b'S' => self.skip_type()?, - b'A' => { - self.skip_type()?; - self.skip_const()?; - } - b'T' => { - while !self.eat(b'E') { - self.skip_type()?; - } - } - b'F' => { - let _binder = self.opt_integer_62(b'G')?; - let _is_unsafe = self.eat(b'U'); - if self.eat(b'K') { - let c_abi = self.eat(b'C'); - if !c_abi { - let abi = self.ident()?; - if abi.ascii.is_empty() || !abi.punycode.is_empty() { - return Err(Invalid); - } - } - } - while !self.eat(b'E') { - self.skip_type()?; - } - self.skip_type()?; - } - b'D' => { - let _binder = self.opt_integer_62(b'G')?; - while !self.eat(b'E') { - self.skip_path()?; - while self.eat(b'p') { - self.ident()?; - self.skip_type()?; - } - } - if !self.eat(b'L') { - return Err(Invalid); - } - self.integer_62()?; - } - b'B' => { - self.backref()?; - } - _ => { - // Go back to the tag, so `skip_path` also sees it. - self.next -= 1; - self.skip_path()?; - } - } - - self.pop_depth(); - Ok(()) - } - - fn skip_const(&mut self) -> Result<(), Invalid> { - self.push_depth()?; - - if self.eat(b'B') { - self.backref()?; - - self.pop_depth(); - return Ok(()); - } - - let ty_tag = self.next()?; - - if ty_tag == b'p' { - // We don't encode the type if the value is a placeholder. - self.pop_depth(); - return Ok(()); - } - - match ty_tag { - // Unsigned integer types. - b'h' | b't' | b'm' | b'y' | b'o' | b'j' | - // Bool. - b'b' | - // Char. - b'c' => {} - - // Signed integer types. - b'a' | b's' | b'l' | b'x' | b'n' | b'i' => { - // Negation on signed integers. - let _ = self.eat(b'n'); - } - - _ => return Err(Invalid), - } - - self.hex_nibbles()?; - - self.pop_depth(); - Ok(()) - } } struct Printer<'a, 'b: 'a, 's> { + /// The input parser to demangle from, or `Err(Invalid)` if any error was + /// encountered (in order to disallow further likely-incorrect demangling). + /// + /// See also the documentation on the `invalid!` and `parse!` macros below. parser: Result, Invalid>, - out: &'a mut fmt::Formatter<'b>, + + /// The output formatter to demangle to, or `None` while skipping printing. + out: Option<&'a mut fmt::Formatter<'b>>, + + /// Cumulative number of lifetimes bound by `for<...>` binders ('G'), + /// anywhere "around" the current entity (e.g. type) being demangled. + /// This value is not tracked while skipping printing, as it'd be unused. + /// + /// See also the documentation on the `Printer::in_binder` method. bound_lifetime_depth: u32, } @@ -618,7 +479,7 @@ struct Printer<'a, 'b: 'a, 's> { macro_rules! invalid { ($printer:ident) => {{ $printer.parser = Err(Invalid); - return $printer.out.write_str("?"); + return $printer.print("?"); }}; } @@ -647,13 +508,35 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { self.parser_mut().map(|p| p.eat(b)) == Ok(true) } - /// Return a nested parser for a backref. - fn backref_printer<'c>(&'c mut self) -> Printer<'c, 'b, 's> { - Printer { - parser: self.parser_mut().and_then(|p| p.backref()), - out: self.out, - bound_lifetime_depth: self.bound_lifetime_depth, + /// Skip printing (i.e. `self.out` will be `None`) for the duration of the + /// given closure. This should not change parsing behavior, only disable the + /// output, but there may be optimizations (such as not traversing backrefs). + fn skipping_printing(&mut self, f: F) + where + F: FnOnce(&mut Self) -> fmt::Result, + { + let orig_out = self.out.take(); + f(self).expect("`fmt::Error`s should be impossible without a `fmt::Formatter`"); + self.out = orig_out; + } + + /// Print the target of a backref, using the given closure. + /// When printing is being skipped, the backref will only be parsed, + /// ignoring the backref's target completely. + fn print_backref(&mut self, f: F) -> fmt::Result + where + F: FnOnce(&mut Self) -> fmt::Result, + { + let backref_parser = self.parser_mut().and_then(|p| p.backref()); + + if self.out.is_none() { + return Ok(()); } + + let orig_parser = mem::replace(&mut self.parser, backref_parser); + let r = f(self); + self.parser = orig_parser; + r } fn pop_depth(&mut self) { @@ -662,24 +545,38 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { } } + /// Output the given value to `self.out` (using `fmt::Display` formatting), + /// if printing isn't being skipped. + fn print(&mut self, x: impl fmt::Display) -> fmt::Result { + if let Some(out) = &mut self.out { + fmt::Display::fmt(&x, out)?; + } + Ok(()) + } + /// Print the lifetime according to the previously decoded index. /// An index of `0` always refers to `'_`, but starting with `1`, /// indices refer to late-bound lifetimes introduced by a binder. fn print_lifetime_from_index(&mut self, lt: u64) -> fmt::Result { - self.out.write_str("'")?; + // Bound lifetimes aren't tracked when skipping printing. + if self.out.is_none() { + return Ok(()); + } + + self.print("'")?; if lt == 0 { - return self.out.write_str("_"); + return self.print("_"); } match (self.bound_lifetime_depth as u64).checked_sub(lt) { Some(depth) => { // Try to print lifetimes alphabetically first. if depth < 26 { let c = (b'a' + depth as u8) as char; - fmt::Display::fmt(&c, self.out) + self.print(c) } else { // Use `'_123` after running out of letters. - self.out.write_str("_")?; - fmt::Display::fmt(&depth, self.out) + self.print("_")?; + self.print(depth) } } None => invalid!(self), @@ -694,16 +591,22 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { F: FnOnce(&mut Self) -> fmt::Result, { let bound_lifetimes = parse!(self, opt_integer_62(b'G')); + + // Don't track bound lifetimes when skipping printing. + if self.out.is_none() { + return f(self); + } + if bound_lifetimes > 0 { - self.out.write_str("for<")?; + self.print("for<")?; for i in 0..bound_lifetimes { if i > 0 { - self.out.write_str(", ")?; + self.print(", ")?; } self.bound_lifetime_depth += 1; self.print_lifetime_from_index(1)?; } - self.out.write_str("> ")?; + self.print("> ")?; } let r = f(self); @@ -724,7 +627,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { let mut i = 0; while self.parser.is_ok() && !self.eat(b'E') { if i > 0 { - self.out.write_str(sep)?; + self.print(sep)?; } f(self)?; i += 1; @@ -741,11 +644,13 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { let dis = parse!(self, disambiguator); let name = parse!(self, ident); - fmt::Display::fmt(&name, self.out)?; - if !self.out.alternate() { - self.out.write_str("[")?; - fmt::LowerHex::fmt(&dis, self.out)?; - self.out.write_str("]")?; + self.print(name)?; + if let Some(out) = &mut self.out { + if !out.alternate() { + out.write_str("[")?; + fmt::LowerHex::fmt(&dis, out)?; + out.write_str("]")?; + } } } b'N' => { @@ -759,26 +664,26 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { match ns { // Special namespaces, like closures and shims. Some(ns) => { - self.out.write_str("::{")?; + self.print("::{")?; match ns { - 'C' => self.out.write_str("closure")?, - 'S' => self.out.write_str("shim")?, - _ => fmt::Display::fmt(&ns, self.out)?, + 'C' => self.print("closure")?, + 'S' => self.print("shim")?, + _ => self.print(ns)?, } if !name.ascii.is_empty() || !name.punycode.is_empty() { - self.out.write_str(":")?; - fmt::Display::fmt(&name, self.out)?; + self.print(":")?; + self.print(name)?; } - self.out.write_str("#")?; - fmt::Display::fmt(&dis, self.out)?; - self.out.write_str("}")?; + self.print("#")?; + self.print(dis)?; + self.print("}")?; } // Implementation-specific/unspecified namespaces. None => { if !name.ascii.is_empty() || !name.punycode.is_empty() { - self.out.write_str("::")?; - fmt::Display::fmt(&name, self.out)?; + self.print("::")?; + self.print(name)?; } } } @@ -787,28 +692,28 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { if tag != b'Y' { // Ignore the `impl`'s own path. parse!(self, disambiguator); - parse!(self, skip_path); + self.skipping_printing(|this| this.print_path(false)); } - self.out.write_str("<")?; + self.print("<")?; self.print_type()?; if tag != b'M' { - self.out.write_str(" as ")?; + self.print(" as ")?; self.print_path(false)?; } - self.out.write_str(">")?; + self.print(">")?; } b'I' => { self.print_path(in_value)?; if in_value { - self.out.write_str("::")?; + self.print("::")?; } - self.out.write_str("<")?; + self.print("<")?; self.print_sep_list(Self::print_generic_arg, ", ")?; - self.out.write_str(">")?; + self.print(">")?; } b'B' => { - self.backref_printer().print_path(in_value)?; + self.print_backref(|this| this.print_path(in_value))?; } _ => invalid!(self), } @@ -832,53 +737,53 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { let tag = parse!(self, next); if let Some(ty) = basic_type(tag) { - return self.out.write_str(ty); + return self.print(ty); } parse!(self, push_depth); match tag { b'R' | b'Q' => { - self.out.write_str("&")?; + self.print("&")?; if self.eat(b'L') { let lt = parse!(self, integer_62); if lt != 0 { self.print_lifetime_from_index(lt)?; - self.out.write_str(" ")?; + self.print(" ")?; } } if tag != b'R' { - self.out.write_str("mut ")?; + self.print("mut ")?; } self.print_type()?; } b'P' | b'O' => { - self.out.write_str("*")?; + self.print("*")?; if tag != b'P' { - self.out.write_str("mut ")?; + self.print("mut ")?; } else { - self.out.write_str("const ")?; + self.print("const ")?; } self.print_type()?; } b'A' | b'S' => { - self.out.write_str("[")?; + self.print("[")?; self.print_type()?; if tag == b'A' { - self.out.write_str("; ")?; + self.print("; ")?; self.print_const()?; } - self.out.write_str("]")?; + self.print("]")?; } b'T' => { - self.out.write_str("(")?; + self.print("(")?; let count = self.print_sep_list(Self::print_type, ", ")?; if count == 1 { - self.out.write_str(",")?; + self.print(",")?; } - self.out.write_str(")")?; + self.print(")")?; } b'F' => self.in_binder(|this| { let is_unsafe = this.eat(b'U'); @@ -897,39 +802,39 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { }; if is_unsafe { - this.out.write_str("unsafe ")?; + this.print("unsafe ")?; } if let Some(abi) = abi { - this.out.write_str("extern \"")?; + this.print("extern \"")?; // If the ABI had any `-`, they were replaced with `_`, // so the parts between `_` have to be re-joined with `-`. let mut parts = abi.split('_'); - this.out.write_str(parts.next().unwrap())?; + this.print(parts.next().unwrap())?; for part in parts { - this.out.write_str("-")?; - this.out.write_str(part)?; + this.print("-")?; + this.print(part)?; } - this.out.write_str("\" ")?; + this.print("\" ")?; } - this.out.write_str("fn(")?; + this.print("fn(")?; this.print_sep_list(Self::print_type, ", ")?; - this.out.write_str(")")?; + this.print(")")?; if this.eat(b'u') { // Skip printing the return type if it's 'u', i.e. `()`. } else { - this.out.write_str(" -> ")?; + this.print(" -> ")?; this.print_type()?; } Ok(()) })?, b'D' => { - self.out.write_str("dyn ")?; + self.print("dyn ")?; self.in_binder(|this| { this.print_sep_list(Self::print_dyn_trait, " + ")?; Ok(()) @@ -940,12 +845,12 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { } let lt = parse!(self, integer_62); if lt != 0 { - self.out.write_str(" + ")?; + self.print(" + ")?; self.print_lifetime_from_index(lt)?; } } b'B' => { - self.backref_printer().print_type()?; + self.print_backref(Self::print_type)?; } _ => { // Go back to the tag, so `print_path` also sees it. @@ -965,10 +870,17 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { /// open, by omitting the `>`, and return `Ok(true)` in that case. fn print_path_maybe_open_generics(&mut self) -> Result { if self.eat(b'B') { - self.backref_printer().print_path_maybe_open_generics() + // NOTE(eddyb) the closure may not run if printing is being skipped, + // but in that case the returned boolean doesn't matter. + let mut open = false; + self.print_backref(|this| { + open = this.print_path_maybe_open_generics()?; + Ok(()) + })?; + Ok(open) } else if self.eat(b'I') { self.print_path(false)?; - self.out.write_str("<")?; + self.print("<")?; self.print_sep_list(Self::print_generic_arg, ", ")?; Ok(true) } else { @@ -982,20 +894,20 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { while self.eat(b'p') { if !open { - self.out.write_str("<")?; + self.print("<")?; open = true; } else { - self.out.write_str(", ")?; + self.print(", ")?; } let name = parse!(self, ident); - fmt::Display::fmt(&name, self.out)?; - self.out.write_str(" = ")?; + self.print(name)?; + self.print(" = ")?; self.print_type()?; } if open { - self.out.write_str(">")?; + self.print(">")?; } Ok(()) @@ -1005,7 +917,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { parse!(self, push_depth); if self.eat(b'B') { - self.backref_printer().print_const()?; + self.print_backref(Self::print_const)?; self.pop_depth(); return Ok(()); @@ -1015,7 +927,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { if ty_tag == b'p' { // We don't encode the type if the value is a placeholder. - self.out.write_str("_")?; + self.print("_")?; self.pop_depth(); return Ok(()); @@ -1035,10 +947,12 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { _ => invalid!(self), }; - if !self.out.alternate() { - self.out.write_str(": ")?; - let ty = basic_type(ty_tag).unwrap(); - self.out.write_str(ty)?; + if let Some(out) = &mut self.out { + if !out.alternate() { + self.print(": ")?; + let ty = basic_type(ty_tag).unwrap(); + self.print(ty)?; + } } self.pop_depth(); @@ -1050,20 +964,20 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { // Print anything that doesn't fit in `u64` verbatim. if hex.len() > 16 { - self.out.write_str("0x")?; - return self.out.write_str(hex); + self.print("0x")?; + return self.print(hex); } let mut v = 0; for c in hex.chars() { v = (v << 4) | (c.to_digit(16).unwrap() as u64); } - fmt::Display::fmt(&v, self.out) + self.print(v) } fn print_const_int(&mut self) -> fmt::Result { if self.eat(b'n') { - self.out.write_str("-")?; + self.print("-")?; } self.print_const_uint() @@ -1071,8 +985,8 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { fn print_const_bool(&mut self) -> fmt::Result { match parse!(self, hex_nibbles).as_bytes() { - b"0" => self.out.write_str("false"), - b"1" => self.out.write_str("true"), + b"0" => self.print("false"), + b"1" => self.print("true"), _ => invalid!(self), } } @@ -1090,10 +1004,13 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { v = (v << 4) | (c.to_digit(16).unwrap() as u32); } if let Some(c) = char::from_u32(v) { - fmt::Debug::fmt(&c, self.out) + if let Some(out) = &mut self.out { + fmt::Debug::fmt(&c, out)?; + } } else { - invalid!(self) + invalid!(self); } + Ok(()) } }