use super::{DemangleWrite, DemangleNodeType, DemangleOptions};
use error::{self, Result};
use index_str::IndexStr;
use std::cell::Cell;
#[cfg(feature = "logging")]
use std::cell::RefCell;
use std::fmt::{self, Write};
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops;
use std::ptr;
use subs::{Substitutable, SubstitutionTable};
use string::String;
use vec::Vec;
use boxed::Box;
struct AutoLogParse;
#[cfg(feature = "logging")]
thread_local! {
static LOG_DEPTH: RefCell<usize> = RefCell::new(0);
}
impl AutoLogParse {
#[cfg(feature = "logging")]
fn new(production: &'static str, input: IndexStr<'_>) -> AutoLogParse {
LOG_DEPTH.with(|depth| {
if *depth.borrow() == 0 {
println!();
}
let indent: String = (0..*depth.borrow() * 4).map(|_| ' ').collect();
log!(
"{}({} \"{}\"",
indent,
production,
String::from_utf8_lossy(input.as_ref())
);
*depth.borrow_mut() += 1;
});
AutoLogParse
}
#[cfg(not(feature = "logging"))]
#[inline(always)]
fn new(_: &'static str, _: IndexStr) -> AutoLogParse {
AutoLogParse
}
}
#[cfg(feature = "logging")]
impl Drop for AutoLogParse {
fn drop(&mut self) {
LOG_DEPTH.with(|depth| {
*depth.borrow_mut() -= 1;
let indent: String = (0..*depth.borrow() * 4).map(|_| ' ').collect();
log!("{})", indent);
});
}
}
macro_rules! try_begin_parse {
( $production:expr , $ctx:expr , $input:expr ) => {
let _log = AutoLogParse::new($production, $input);
let _auto_check_recursion = AutoParseRecursion::new($ctx)?;
}
}
struct AutoLogDemangle;
impl AutoLogDemangle {
#[cfg(feature = "logging")]
fn new<P, W>(
production: &P,
ctx: &DemangleContext<W>,
scope: Option<ArgScopeStack>,
is_inner: bool,
) -> AutoLogDemangle
where
P: ?Sized + fmt::Debug,
W: DemangleWrite,
{
LOG_DEPTH.with(|depth| {
if *depth.borrow() == 0 {
println!();
}
let indent: String = (0..*depth.borrow() * 4).map(|_| ' ').collect();
log!("{}(", indent);
log!(
"{} {}{:?}",
indent,
if is_inner { "as_inner: " } else { "" },
production
);
log!("{} inner = {:?}", indent, ctx.inner);
log!("{} scope = {:?}", indent, scope);
*depth.borrow_mut() += 1;
});
AutoLogDemangle
}
#[cfg(not(feature = "logging"))]
#[inline(always)]
fn new<P, W>(
_: &P,
_: &DemangleContext<W>,
_: Option<ArgScopeStack>,
_: bool,
) -> AutoLogDemangle
where
P: ?Sized + fmt::Debug,
W: DemangleWrite,
{
AutoLogDemangle
}
}
#[cfg(feature = "logging")]
impl Drop for AutoLogDemangle {
fn drop(&mut self) {
LOG_DEPTH.with(|depth| {
*depth.borrow_mut() -= 1;
let indent: String = (0..*depth.borrow() * 4).map(|_| ' ').collect();
log!("{})", indent);
});
}
}
macro_rules! try_begin_demangle {
( $production:expr, $ctx:expr, $scope:expr ) => {{
let _log = AutoLogDemangle::new($production, $ctx, $scope, false);
&mut AutoParseDemangle::new($ctx)?
}}
}
macro_rules! try_begin_demangle_as_inner {
( $production:expr, $ctx:expr, $scope:expr ) => {{
let _log = AutoLogDemangle::new($production, $ctx, $scope, true);
&mut AutoParseDemangle::new($ctx)?
}}
}
#[derive(Debug, Default, Clone, Copy)]
struct ParseContextState {
recursion_level: u32,
in_conversion: bool,
}
#[derive(Debug, Clone)]
pub struct ParseContext {
max_recursion: u32,
state: Cell<ParseContextState>,
}
impl Default for ParseContext {
fn default() -> ParseContext {
ParseContext {
max_recursion: 64,
state: Cell::new(ParseContextState::default()),
}
}
}
impl ParseContext {
pub fn recursion_level(&self) -> u32 {
self.state.get().recursion_level
}
#[inline]
fn enter_recursion(&self) -> error::Result<()> {
let mut state = self.state.get();
let new_recursion_level = state.recursion_level + 1;
if new_recursion_level >= self.max_recursion {
log!("Hit too much recursion at level {}", self.max_recursion);
Err(error::Error::TooMuchRecursion)
} else {
state.recursion_level = new_recursion_level;
self.state.set(state);
Ok(())
}
}
#[inline]
fn exit_recursion(&self) {
let mut state = self.state.get();
debug_assert!(state.recursion_level >= 1);
state.recursion_level -= 1;
self.state.set(state);
}
#[inline]
fn in_conversion(&self) -> bool {
self.state.get().in_conversion
}
fn set_in_conversion(&self, in_conversion: bool) -> bool {
let mut state = self.state.get();
let previously_in_conversion = state.in_conversion;
state.in_conversion = in_conversion;
self.state.set(state);
previously_in_conversion
}
}
struct AutoParseRecursion<'a>(&'a ParseContext);
impl<'a> AutoParseRecursion<'a> {
#[inline]
fn new(ctx: &'a ParseContext) -> error::Result<AutoParseRecursion<'a>> {
ctx.enter_recursion()?;
Ok(AutoParseRecursion(ctx))
}
}
impl<'a> Drop for AutoParseRecursion<'a> {
#[inline]
fn drop(&mut self) {
self.0.exit_recursion();
}
}
#[doc(hidden)]
pub trait Parse: Sized {
fn parse<'a, 'b>(
ctx: &'a ParseContext,
subs: &'a mut SubstitutionTable,
input: IndexStr<'b>,
) -> Result<(Self, IndexStr<'b>)>;
}
trait GetTemplateArgs {
fn get_template_args<'a>(&'a self, subs: &'a SubstitutionTable) -> Option<&'a TemplateArgs>;
}
#[derive(Debug)]
pub(crate) enum LeafName<'a> {
SourceName(&'a SourceName),
WellKnownComponent(&'a WellKnownComponent),
Closure(&'a ClosureTypeName),
UnnamedType(&'a UnnamedTypeName),
}
impl<'subs, W> DemangleAsLeaf<'subs, W> for LeafName<'subs>
where
W: 'subs + DemangleWrite,
{
fn demangle_as_leaf<'me, 'ctx>(
&'me self,
ctx: &'ctx mut DemangleContext<'subs, W>,
) -> fmt::Result {
match *self {
LeafName::SourceName(sn) => sn.demangle(ctx, None),
LeafName::Closure(c) => c.demangle(ctx, None),
LeafName::WellKnownComponent(wkc) => wkc.demangle_as_leaf(ctx),
LeafName::UnnamedType(utn) => utn.demangle_as_leaf(ctx),
}
}
}
pub(crate) trait GetLeafName<'a> {
fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option<LeafName<'a>>;
}
pub(crate) trait IsCtorDtorConversion {
fn is_ctor_dtor_conversion(&self, subs: &SubstitutionTable) -> bool;
}
trait ArgScope<'me, 'ctx>: fmt::Debug {
fn leaf_name(&'me self) -> Result<LeafName<'ctx>>;
fn get_template_arg(&'me self, index: usize) -> Result<(&'ctx TemplateArg, &'ctx TemplateArgs)>;
fn get_function_arg(&'me self, index: usize) -> Result<&'ctx Type>;
}
#[derive(Copy, Clone, Debug)]
pub struct ArgScopeStack<'prev, 'subs>
where
'subs: 'prev,
{
item: &'subs dyn ArgScope<'subs, 'subs>,
in_arg: Option<(usize, &'subs TemplateArgs)>,
prev: Option<&'prev ArgScopeStack<'prev, 'subs>>,
}
trait ArgScopeStackExt<'prev, 'subs>: Copy {
fn push(
&'prev self,
item: &'subs dyn ArgScope<'subs, 'subs>,
) -> Option<ArgScopeStack<'prev, 'subs>>;
}
impl<'prev, 'subs> ArgScopeStackExt<'prev, 'subs> for Option<ArgScopeStack<'prev, 'subs>> {
fn push(
&'prev self,
item: &'subs dyn ArgScope<'subs, 'subs>,
) -> Option<ArgScopeStack<'prev, 'subs>> {
log!("ArgScopeStack::push: {:?}", item);
Some(ArgScopeStack {
prev: self.as_ref(),
in_arg: None,
item: item,
})
}
}
impl<'prev, 'subs> ArgScope<'prev, 'subs> for Option<ArgScopeStack<'prev, 'subs>> {
fn leaf_name(&'prev self) -> Result<LeafName<'subs>> {
let mut scope = self.as_ref();
while let Some(s) = scope {
if let Ok(c) = s.item.leaf_name() {
return Ok(c);
}
scope = s.prev;
}
Err(error::Error::BadLeafNameReference)
}
fn get_template_arg(&'prev self, idx: usize) -> Result<(&'subs TemplateArg, &'subs TemplateArgs)> {
let mut scope = self.as_ref();
while let Some(s) = scope {
if let Ok((arg, args)) = s.item.get_template_arg(idx) {
if let Some((in_idx, in_args)) = s.in_arg {
if args as *const TemplateArgs == in_args as *const TemplateArgs &&
in_idx <= idx {
return Err(error::Error::ForwardTemplateArgReference);
}
}
return Ok((arg, args));
}
scope = s.prev;
}
Err(error::Error::BadTemplateArgReference)
}
fn get_function_arg(&'prev self, idx: usize) -> Result<&'subs Type> {
let mut scope = self.as_ref();
while let Some(s) = scope {
if let Ok(arg) = s.item.get_function_arg(idx) {
return Ok(arg);
}
scope = s.prev;
}
Err(error::Error::BadFunctionArgReference)
}
}
#[derive(Debug, Copy, Clone)]
struct DemangleState {
pub recursion_level: u32,
}
struct AutoParseDemangle<'a, 'b, W: 'a + DemangleWrite>(&'b mut DemangleContext<'a, W>);
impl<'a, 'b, W: 'a + DemangleWrite> AutoParseDemangle<'a, 'b, W> {
#[inline]
fn new(ctx: &'b mut DemangleContext<'a, W>) -> std::result::Result<Self, fmt::Error> {
ctx.enter_recursion()?;
Ok(AutoParseDemangle(ctx))
}
}
impl<'a, 'b, W: 'a + DemangleWrite> std::ops::Deref for AutoParseDemangle<'a, 'b, W> {
type Target = DemangleContext<'a, W>;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<'a, 'b, W: 'a + DemangleWrite> std::ops::DerefMut for AutoParseDemangle<'a, 'b, W> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0
}
}
impl<'a, 'b, W: 'a + DemangleWrite> Drop for AutoParseDemangle<'a, 'b, W> {
#[inline]
fn drop(&mut self) {
self.0.exit_recursion();
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct DemangleContext<'a, W>
where
W: 'a + DemangleWrite,
{
subs: &'a SubstitutionTable,
max_recursion: u32,
inner: Vec<&'a dyn DemangleAsInner<'a, W>>,
input: &'a [u8],
source_name: Option<&'a str>,
out: &'a mut W,
bytes_written: usize,
last_char_written: Option<char>,
is_lambda_arg: bool,
is_template_prefix: bool,
is_template_prefix_in_nested_name: bool,
is_template_argument_pack: bool,
show_params: bool,
state: Cell<DemangleState>,
}
impl<'a, W> fmt::Write for DemangleContext<'a, W>
where
W: 'a + DemangleWrite,
{
fn write_str(&mut self, s: &str) -> fmt::Result {
if s.is_empty() {
return Ok(());
}
log!("DemangleContext::write: '{}'", s);
self.out.write_string(s).map(|_| {
self.last_char_written = s.chars().last();
self.bytes_written += s.len();
})
}
}
impl<'a, W> DemangleContext<'a, W>
where
W: 'a + DemangleWrite,
{
pub fn new(
subs: &'a SubstitutionTable,
input: &'a [u8],
options: DemangleOptions,
out: &'a mut W,
) -> DemangleContext<'a, W> {
DemangleContext {
subs: subs,
max_recursion: 128,
inner: vec![],
input: input,
source_name: None,
out: out,
bytes_written: 0,
last_char_written: None,
is_lambda_arg: false,
is_template_prefix: false,
is_template_prefix_in_nested_name: false,
is_template_argument_pack: false,
show_params: !options.no_params,
state: Cell::new(DemangleState {
recursion_level: 0,
}),
}
}
pub fn recursion_level(&self) -> u32 {
self.state.get().recursion_level
}
#[inline]
fn enter_recursion(&self) -> fmt::Result {
let mut state = self.state.get();
let new_recursion_level = state.recursion_level + 1;
if new_recursion_level >= self.max_recursion {
log!("Hit too much recursion at level {}", self.max_recursion);
Err(Default::default())
} else {
state.recursion_level = new_recursion_level;
self.state.set(state);
Ok(())
}
}
#[inline]
fn exit_recursion(&self) {
let mut state = self.state.get();
debug_assert!(state.recursion_level >= 1);
state.recursion_level -= 1;
self.state.set(state);
}
#[inline]
fn ensure(&mut self, ch: char) -> fmt::Result {
if self.last_char_written == Some(ch) {
Ok(())
} else {
write!(self, "{}", ch)?;
Ok(())
}
}
#[inline]
fn ensure_space(&mut self) -> fmt::Result {
self.ensure(' ')
}
#[inline]
fn push_inner(&mut self, item: &'a dyn DemangleAsInner<'a, W>) {
log!("DemangleContext::push_inner: {:?}", item);
self.inner.push(item);
}
#[inline]
fn pop_inner(&mut self) -> Option<&'a dyn DemangleAsInner<'a, W>> {
let popped = self.inner.pop();
log!("DemangleContext::pop_inner: {:?}", popped);
popped
}
#[inline]
fn pop_inner_if(&mut self, inner: &'a dyn DemangleAsInner<'a, W>) -> bool {
let last = match self.inner.last() {
None => return false,
Some(last) => *last,
};
if ptr::eq(last, inner) {
self.inner.pop();
true
} else {
false
}
}
fn demangle_inner_prefixes<'prev>(
&mut self,
scope: Option<ArgScopeStack<'prev, 'a>>,
) -> fmt::Result {
log!("DemangleContext::demangle_inner_prefixes");
let mut new_inner = vec![];
while let Some(inner) = self.pop_inner() {
if inner
.downcast_to_function_type()
.map_or(false, |f| !f.cv_qualifiers.is_empty())
{
log!(
"DemangleContext::demangle_inner_prefixes: not a prefix, saving: {:?}",
inner
);
new_inner.push(inner);
} else {
log!(
"DemangleContext::demangle_inner_prefixes: demangling prefix: {:?}",
inner
);
inner.demangle_as_inner(self, scope)?;
}
}
new_inner.reverse();
self.inner = new_inner;
Ok(())
}
fn demangle_inners<'prev>(
&mut self,
scope: Option<ArgScopeStack<'prev, 'a>>,
) -> fmt::Result {
while let Some(inner) = self.pop_inner() {
inner.demangle_as_inner(self, scope)?;
}
Ok(())
}
fn set_source_name(&mut self, start: usize, end: usize) {
let ident = &self.input[start..end];
self.source_name = std::str::from_utf8(ident).ok();
}
fn push_demangle_node(&mut self, t: DemangleNodeType) {
self.out.push_demangle_node(t);
}
fn pop_demangle_node(&mut self) {
self.out.pop_demangle_node();
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct AutoDemangleContextInnerBarrier<'ctx, 'a, W>
where
W: 'a + DemangleWrite,
'a: 'ctx,
{
ctx: &'ctx mut DemangleContext<'a, W>,
saved_inner: Vec<&'a dyn DemangleAsInner<'a, W>>,
}
impl<'ctx, 'a, W> AutoDemangleContextInnerBarrier<'ctx, 'a, W>
where
W: 'a + DemangleWrite,
'a: 'ctx,
{
pub fn new(ctx: &'ctx mut DemangleContext<'a, W>) -> Self {
let mut saved_inner = vec![];
mem::swap(&mut saved_inner, &mut ctx.inner);
AutoDemangleContextInnerBarrier {
ctx: ctx,
saved_inner: saved_inner,
}
}
}
impl<'ctx, 'a, W> ops::Deref for AutoDemangleContextInnerBarrier<'ctx, 'a, W>
where
W: 'a + DemangleWrite,
'a: 'ctx,
{
type Target = DemangleContext<'a, W>;
fn deref(&self) -> &Self::Target {
self.ctx
}
}
impl<'ctx, 'a, W> ops::DerefMut for AutoDemangleContextInnerBarrier<'ctx, 'a, W>
where
W: 'a + DemangleWrite,
'a: 'ctx,
{
fn deref_mut(&mut self) -> &mut Self::Target {
self.ctx
}
}
impl<'ctx, 'a, W> Drop for AutoDemangleContextInnerBarrier<'ctx, 'a, W>
where
W: 'a + DemangleWrite,
'a: 'ctx,
{
fn drop(&mut self) {
if !self.ctx.inner.is_empty() {
log!("Context inner was not emptied, did demangling fail?");
}
mem::swap(&mut self.saved_inner, &mut self.ctx.inner);
}
}
macro_rules! inner_barrier {
( $ctx:ident ) => {
let mut _ctx = AutoDemangleContextInnerBarrier::new($ctx);
let $ctx = &mut _ctx;
}
}
#[doc(hidden)]
pub trait Demangle<'subs, W>: fmt::Debug
where
W: 'subs + DemangleWrite,
{
fn demangle<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result;
}
#[doc(hidden)]
pub trait DemangleAsInner<'subs, W>: Demangle<'subs, W>
where
W: 'subs + DemangleWrite,
{
fn demangle_as_inner<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result {
self.demangle(ctx, scope)
}
fn downcast_to_type(&self) -> Option<&Type> {
None
}
fn downcast_to_function_type(&self) -> Option<&FunctionType> {
None
}
fn downcast_to_array_type(&self) -> Option<&ArrayType> {
None
}
fn downcast_to_pointer_to_member(&self) -> Option<&PointerToMemberType> {
None
}
fn is_qualified(&self) -> bool {
false
}
}
pub(crate) trait DemangleAsLeaf<'subs, W>
where
W: 'subs + DemangleWrite,
{
fn demangle_as_leaf<'me, 'ctx>(
&'me self,
ctx: &'ctx mut DemangleContext<'subs, W>,
) -> fmt::Result;
}
macro_rules! reference_newtype {
( $newtype_name:ident , $oldtype:ty ) => {
#[derive(Debug)]
struct $newtype_name($oldtype);
impl $newtype_name {
#[allow(clippy::ptr_arg)]
#[allow(unsafe_code)]
fn new(types: &$oldtype) -> &$newtype_name {
unsafe {
&*(types as *const $oldtype as *const $newtype_name)
}
}
}
impl Drop for $newtype_name {
fn drop(&mut self) {
unreachable!("Dropping implies we dereferenced and took ownership, which \
is not safe for this newtype");
}
}
impl ops::Deref for $newtype_name {
type Target = $oldtype;
fn deref(&self) -> &Self::Target {
&self.0
}
}
}
}
reference_newtype!(FunctionArgList, Vec<TypeHandle>);
reference_newtype!(FunctionArgListAndReturnType, Vec<TypeHandle>);
reference_newtype!(FunctionArgSlice, [TypeHandle]);
impl<'subs, W> Demangle<'subs, W> for FunctionArgSlice
where
W: 'subs + DemangleWrite,
{
fn demangle<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result {
let ctx = try_begin_demangle!(self, ctx, scope);
let mut saw_needs_paren = false;
let (needs_space, needs_paren) = ctx.inner
.iter()
.rev()
.map(|inner| {
if inner.downcast_to_pointer_to_member().is_some() {
(true, true)
} else {
match inner.downcast_to_type() {
Some(&Type::Qualified(..))
| Some(&Type::Complex(_))
| Some(&Type::Imaginary(_))
| Some(&Type::PointerToMember(_)) => (true, true),
Some(&Type::PointerTo(_))
| Some(&Type::LvalueRef(_))
| Some(&Type::RvalueRef(_)) => (false, true),
_ => (false, false),
}
}
})
.take_while(|&(_, needs_paren)| {
if saw_needs_paren {
false
} else {
saw_needs_paren |= needs_paren;
true
}
})
.fold((false, false), |(space, paren), (next_space, next_paren)| {
(space || next_space, paren || next_paren)
});
if needs_paren {
let needs_space = needs_space || match ctx.last_char_written {
Some('(') | Some('*') => false,
_ => true,
};
if needs_space {
ctx.ensure_space()?;
}
write!(ctx, "(")?;
}
ctx.demangle_inner_prefixes(scope)?;
if needs_paren {
write!(ctx, ")")?;
}
write!(ctx, "(")?;
if self.len() == 1 && self[0].is_void() {
write!(ctx, ")")?;
return Ok(());
}
let mut need_comma = false;
for arg in self.iter() {
if need_comma {
write!(ctx, ", ")?;
}
arg.demangle(ctx, scope)?;
need_comma = true;
}
write!(ctx, ")")?;
ctx.demangle_inners(scope)
}
}
impl<'subs, W> Demangle<'subs, W> for FunctionArgList
where
W: 'subs + DemangleWrite,
{
fn demangle<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result {
FunctionArgSlice::new(&self.0[..]).demangle(ctx, scope)
}
}
impl<'subs, W> DemangleAsInner<'subs, W> for FunctionArgList
where
W: 'subs + DemangleWrite,
{
}
impl<'subs, W> Demangle<'subs, W> for FunctionArgListAndReturnType
where
W: 'subs + DemangleWrite,
{
fn demangle<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result {
FunctionArgSlice::new(&self.0[1..]).demangle(ctx, scope)
}
}
impl<'subs, W> DemangleAsInner<'subs, W> for FunctionArgListAndReturnType
where
W: 'subs + DemangleWrite,
{
}
macro_rules! define_handle {
(
$(#[$attr:meta])*
pub enum $typename:ident
) => {
define_handle! {
$(#[$attr])*
pub enum $typename {}
}
};
(
$(#[$attr:meta])*
pub enum $typename:ident {
$(
$( #[$extra_attr:meta] )*
extra $extra_variant:ident ( $extra_variant_ty:ty ),
)*
}
) => {
$(#[$attr])*
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum $typename {
WellKnown(WellKnownComponent),
BackReference(usize),
$(
$( #[$extra_attr] )*
$extra_variant( $extra_variant_ty ),
)*
}
impl $typename {
pub fn back_reference(&self) -> Option<usize> {
match *self {
$typename::BackReference(n) => Some(n),
_ => None,
}
}
}
impl<'subs, W> Demangle<'subs, W> for $typename
where
W: 'subs + DemangleWrite
{
#[inline]
fn demangle<'prev, 'ctx>(&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>)
-> fmt::Result {
match *self {
$typename::WellKnown(ref comp) => comp.demangle(ctx, scope),
$typename::BackReference(idx) => ctx.subs[idx].demangle(ctx, scope),
$(
$typename::$extra_variant(ref extra) => extra.demangle(ctx, scope),
)*
}
}
}
impl<'a> GetLeafName<'a> for $typename {
fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option<LeafName<'a>> {
match *self {
$typename::WellKnown(ref wk) => wk.get_leaf_name(subs),
$typename::BackReference(idx) => {
subs.get(idx).and_then(|s| s.get_leaf_name(subs))
}
$(
$typename::$extra_variant(ref e) => e.get_leaf_name(subs),
)*
}
}
}
};
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NonSubstitution(usize);
impl<'subs, W> Demangle<'subs, W> for NonSubstitution
where
W: 'subs + DemangleWrite,
{
fn demangle<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result {
ctx.subs.non_substitution(self.0).demangle(ctx, scope)
}
}
impl<'a> GetLeafName<'a> for NonSubstitution {
fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option<LeafName<'a>> {
subs.get_non_substitution(self.0)
.and_then(|ns| ns.get_leaf_name(subs))
}
}
macro_rules! define_vocabulary {
( $(#[$attr:meta])* pub enum $typename:ident {
$($variant:ident ( $mangled:expr, $printable:expr )),*
} ) => {
$(#[$attr])*
pub enum $typename {
$(
#[doc=$printable]
$variant
),*
}
impl Parse for $typename {
fn parse<'a, 'b>(ctx: &'a ParseContext,
_subs: &'a mut SubstitutionTable,
input: IndexStr<'b>)
-> Result<($typename, IndexStr<'b>)> {
try_begin_parse!(stringify!($typename), ctx, input);
let mut found_prefix = false;
$(
if let Some((head, tail)) = input.try_split_at($mangled.len()) {
if head.as_ref() == $mangled {
return Ok(($typename::$variant, tail));
}
} else {
found_prefix |= 0 < input.len() &&
input.len() < $mangled.len() &&
input.as_ref() == &$mangled[..input.len()];
}
)*
if input.is_empty() || found_prefix {
Err(error::Error::UnexpectedEnd)
} else {
Err(error::Error::UnexpectedText)
}
}
}
impl<'subs, W> Demangle<'subs, W> for $typename
where
W: 'subs + DemangleWrite,
{
fn demangle<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>
) -> fmt::Result {
let ctx = try_begin_demangle!(self, ctx, scope);
write!(ctx, "{}", match *self {
$(
$typename::$variant => $printable
),*
})
}
}
impl $typename {
#[allow(dead_code)]
#[inline]
fn starts_with(byte: u8) -> bool {
$(
if $mangled[0] == byte {
return true;
}
)*
false
}
}
};
( $(#[$attr:meta])* pub enum $typename:ident {
$($variant:ident ( $mangled:expr, $printable:expr, $userdata:expr)),*
}
impl $typename2:ident {
fn $fn_name:ident(&self) -> $userdata_ty:ty;
} ) => {
define_vocabulary! {
$(#[$attr])*
pub enum $typename {
$(
$variant ( $mangled, $printable )
),*
}
}
impl $typename2 {
fn $fn_name(&self) -> $userdata_ty {
match *self {
$(
$typename2::$variant => $userdata,
)*
}
}
}
};
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MangledName {
Encoding(Encoding, Vec<CloneSuffix>),
BlockInvoke(Encoding, Option<isize>),
Type(TypeHandle),
GlobalCtorDtor(GlobalCtorDtor),
}
impl Parse for MangledName {
fn parse<'a, 'b>(
ctx: &'a ParseContext,
subs: &'a mut SubstitutionTable,
input: IndexStr<'b>,
) -> Result<(MangledName, IndexStr<'b>)> {
try_begin_parse!("MangledName", ctx, input);
if let Ok(tail) = consume(b"_Z", input).or_else(|_| consume(b"__Z", input)) {
let (encoding, tail) = Encoding::parse(ctx, subs, tail)?;
let (clone_suffixes, tail) = zero_or_more(ctx, subs, tail)?;
return Ok((MangledName::Encoding(encoding, clone_suffixes), tail));
}
if let Ok(tail) = consume(b"___Z", input).or_else(|_| consume(b"____Z", input)) {
let (encoding, tail) = Encoding::parse(ctx, subs, tail)?;
let tail = consume(b"_block_invoke", tail)?;
let tail_opt = match consume(b"_", tail) {
Ok(tail) => Some(parse_number(10, false, tail)?),
Err(_) => parse_number(10, false, tail).ok(),
};
let (digits, tail) = match tail_opt {
Some((digits, tail)) => (Some(digits), tail),
None => (None, tail),
};
return Ok((MangledName::BlockInvoke(encoding, digits), tail));
}
if let Ok(tail) = consume(b"_GLOBAL_", input) {
let (global_ctor_dtor, tail) = GlobalCtorDtor::parse(ctx, subs, tail)?;
return Ok((MangledName::GlobalCtorDtor(global_ctor_dtor), tail));
}
let (ty, tail) = TypeHandle::parse(ctx, subs, input)?;
Ok((MangledName::Type(ty), tail))
}
}
impl<'subs, W> Demangle<'subs, W> for MangledName
where
W: 'subs + DemangleWrite,
{
fn demangle<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result {
let ctx = try_begin_demangle!(self, ctx, scope);
match *self {
MangledName::Encoding(ref enc, ref cs) => {
enc.demangle(ctx, scope)?;
if !cs.is_empty() && ctx.show_params {
for clone_suffix in cs {
clone_suffix.demangle(ctx, scope)?;
}
}
Ok(())
},
MangledName::BlockInvoke(ref enc, _) => {
write!(ctx, "invocation function for block in ")?;
enc.demangle(ctx, scope)?;
Ok(())
}
MangledName::Type(ref ty) => ty.demangle(ctx, scope),
MangledName::GlobalCtorDtor(ref gcd) => gcd.demangle(ctx, scope),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Encoding {
Function(Name, BareFunctionType),
Data(Name),
Special(SpecialName),
}
impl Parse for Encoding {
fn parse<'a, 'b>(
ctx: &'a ParseContext,
subs: &'a mut SubstitutionTable,
input: IndexStr<'b>,
) -> Result<(Encoding, IndexStr<'b>)> {
try_begin_parse!("Encoding", ctx, input);
if let Ok((name, tail)) = Name::parse(ctx, subs, input) {
if let Ok((ty, tail)) = BareFunctionType::parse(ctx, subs, tail) {
return Ok((Encoding::Function(name, ty), tail));
} else {
return Ok((Encoding::Data(name), tail));
}
}
let (name, tail) = SpecialName::parse(ctx, subs, input)?;
Ok((Encoding::Special(name), tail))
}
}
impl<'subs, W> Demangle<'subs, W> for Encoding
where
W: 'subs + DemangleWrite,
{
fn demangle<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result {
let ctx = try_begin_demangle!(self, ctx, scope);
inner_barrier!(ctx);
match *self {
Encoding::Function(ref name, ref fun_ty) => {
debug_assert!(!fun_ty.0.is_empty());
let scope = if let Some(leaf) = name.get_leaf_name(ctx.subs) {
match leaf {
LeafName::SourceName(leaf) => scope.push(leaf),
LeafName::WellKnownComponent(leaf) => scope.push(leaf),
LeafName::Closure(leaf) => scope.push(leaf),
LeafName::UnnamedType(leaf) => scope.push(leaf),
}
} else {
scope
};
let scope = if let Some(template_args) = name.get_template_args(ctx.subs) {
let scope = scope.push(template_args);
if ctx.show_params && !name.is_ctor_dtor_conversion(ctx.subs) {
fun_ty.0[0].demangle(ctx, scope)?;
write!(ctx, " ")?;
}
scope
} else {
scope
};
if ctx.show_params {
ctx.push_inner(self);
name.demangle(ctx, scope)?;
if ctx.pop_inner_if(self) {
self.demangle_as_inner(ctx, scope)?;
}
} else {
name.demangle(ctx, scope)?;
}
Ok(())
}
Encoding::Data(ref name) => name.demangle(ctx, scope),
Encoding::Special(ref name) => name.demangle(ctx, scope),
}
}
}
impl<'subs, W> DemangleAsInner<'subs, W> for Encoding
where
W: 'subs + DemangleWrite,
{
fn demangle_as_inner<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result {
if let Encoding::Function(ref name, ref fun_ty) = *self {
let (scope, function_args) =
if let Some(template_args) = name.get_template_args(ctx.subs) {
let scope = scope.push(template_args);
let function_args = FunctionArgListAndReturnType::new(&fun_ty.0);
(scope, function_args as &dyn DemangleAsInner<W>)
} else {
let function_args = FunctionArgList::new(&fun_ty.0);
(scope, function_args as &dyn DemangleAsInner<W>)
};
function_args.demangle_as_inner(ctx, scope)
} else {
unreachable!("we only push Encoding::Function onto the inner stack");
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CloneSuffix(CloneTypeIdentifier, Vec<isize>);
impl Parse for CloneSuffix {
fn parse<'a, 'b>(
ctx: &'a ParseContext,
subs: &'a mut SubstitutionTable,
input: IndexStr<'b>,
) -> Result<(CloneSuffix, IndexStr<'b>)> {
try_begin_parse!("CloneSuffix", ctx, input);
let tail = consume(b".", input)?;
let (identifier, mut tail) = CloneTypeIdentifier::parse(ctx, subs, tail)?;
let mut numbers = Vec::with_capacity(1);
while let Ok((n, t)) = consume(b".", tail).and_then(|t| parse_number(10, false, t)) {
numbers.push(n);
tail = t;
}
let clone_suffix = CloneSuffix(identifier, numbers);
Ok((clone_suffix, tail))
}
}
impl<'subs, W> Demangle<'subs, W> for CloneSuffix
where
W: 'subs + DemangleWrite,
{
fn demangle<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result {
let ctx = try_begin_demangle!(self, ctx, scope);
write!(ctx, " [clone")?;
self.0.demangle(ctx, scope)?;
for nonnegative in &self.1 {
write!(ctx, ".{}", nonnegative)?;
}
write!(ctx, "]")?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GlobalCtorDtor {
Ctor(Box<MangledName>),
Dtor(Box<MangledName>),
}
impl Parse for GlobalCtorDtor {
fn parse<'a, 'b>(
ctx: &'a ParseContext,
subs: &'a mut SubstitutionTable,
input: IndexStr<'b>,
) -> Result<(GlobalCtorDtor, IndexStr<'b>)> {
try_begin_parse!("GlobalCtorDtor", ctx, input);
let tail = match input.next_or(error::Error::UnexpectedEnd)? {
(b'_', t) | (b'.', t) | (b'$', t) => t,
_ => return Err(error::Error::UnexpectedText),
};
match tail.next_or(error::Error::UnexpectedEnd)? {
(b'I', tail) => {
let tail = consume(b"_", tail)?;
let (name, tail) = MangledName::parse(ctx, subs, tail)?;
Ok((GlobalCtorDtor::Ctor(Box::new(name)), tail))
}
(b'D', tail) => {
let tail = consume(b"_", tail)?;
let (name, tail) = MangledName::parse(ctx, subs, tail)?;
Ok((GlobalCtorDtor::Dtor(Box::new(name)), tail))
}
_ => Err(error::Error::UnexpectedText),
}
}
}
impl<'subs, W> Demangle<'subs, W> for GlobalCtorDtor
where
W: 'subs + DemangleWrite,
{
fn demangle<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result {
let ctx = try_begin_demangle!(self, ctx, scope);
inner_barrier!(ctx);
let saved_show_params = ctx.show_params;
ctx.show_params = true;
let ret = match *self {
GlobalCtorDtor::Ctor(ref name) => {
write!(ctx, "global constructors keyed to ")?;
name.demangle(ctx, scope)
}
GlobalCtorDtor::Dtor(ref name) => {
write!(ctx, "global destructors keyed to ")?;
name.demangle(ctx, scope)
}
};
ctx.show_params = saved_show_params;
ret
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Name {
Nested(NestedName),
Unscoped(UnscopedName),
UnscopedTemplate(UnscopedTemplateNameHandle, TemplateArgs),
Local(LocalName),
}
impl Parse for Name {
fn parse<'a, 'b>(
ctx: &'a ParseContext,
subs: &'a mut SubstitutionTable,
input: IndexStr<'b>,
) -> Result<(Name, IndexStr<'b>)> {
try_begin_parse!("Name", ctx, input);
if let Ok((name, tail)) = NestedName::parse(ctx, subs, input) {
return Ok((Name::Nested(name), tail));
}
if let Ok((name, tail)) = UnscopedName::parse(ctx, subs, input) {
if tail.peek() == Some(b'I') {
let name = UnscopedTemplateName(name);
let idx = subs.insert(Substitutable::UnscopedTemplateName(name));
let handle = UnscopedTemplateNameHandle::BackReference(idx);
let (args, tail) = TemplateArgs::parse(ctx, subs, tail)?;
return Ok((Name::UnscopedTemplate(handle, args), tail));
} else {
return Ok((Name::Unscoped(name), tail));
}
}
if let Ok((name, tail)) = UnscopedTemplateNameHandle::parse(ctx, subs, input) {
let (args, tail) = TemplateArgs::parse(ctx, subs, tail)?;
return Ok((Name::UnscopedTemplate(name, args), tail));
}
let (name, tail) = LocalName::parse(ctx, subs, input)?;
Ok((Name::Local(name), tail))
}
}
impl<'subs, W> Demangle<'subs, W> for Name
where
W: 'subs + DemangleWrite,
{
fn demangle<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result {
let ctx = try_begin_demangle!(self, ctx, scope);
match *self {
Name::Nested(ref nested) => nested.demangle(ctx, scope),
Name::Unscoped(ref unscoped) => unscoped.demangle(ctx, scope),
Name::UnscopedTemplate(ref template, ref args) => {
template.demangle(ctx, scope.push(args))?;
args.demangle(ctx, scope)
}
Name::Local(ref local) => local.demangle(ctx, scope),
}
}
}
impl GetTemplateArgs for Name {
fn get_template_args<'a>(&'a self, subs: &'a SubstitutionTable) -> Option<&'a TemplateArgs> {
match *self {
Name::UnscopedTemplate(_, ref args) => Some(args),
Name::Nested(ref nested) => nested.get_template_args(subs),
Name::Local(ref local) => local.get_template_args(subs),
Name::Unscoped(_) => None,
}
}
}
impl<'a> GetLeafName<'a> for Name {
fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option<LeafName<'a>> {
match *self {
Name::UnscopedTemplate(ref templ, _) => templ.get_leaf_name(subs),
Name::Nested(ref nested) => nested.get_leaf_name(subs),
Name::Unscoped(ref unscoped) => unscoped.get_leaf_name(subs),
Name::Local(ref local) => local.get_leaf_name(subs),
}
}
}
impl IsCtorDtorConversion for Name {
fn is_ctor_dtor_conversion(&self, subs: &SubstitutionTable) -> bool {
match *self {
Name::Unscoped(ref unscoped) => unscoped.is_ctor_dtor_conversion(subs),
Name::Nested(ref nested) => nested.is_ctor_dtor_conversion(subs),
Name::Local(_) |
Name::UnscopedTemplate(..) => false,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum UnscopedName {
Unqualified(UnqualifiedName),
Std(UnqualifiedName),
}
impl Parse for UnscopedName {
fn parse<'a, 'b>(
ctx: &'a ParseContext,
subs: &'a mut SubstitutionTable,
input: IndexStr<'b>,
) -> Result<(UnscopedName, IndexStr<'b>)> {
try_begin_parse!("UnscopedName", ctx, input);
if let Ok(tail) = consume(b"St", input) {
let (name, tail) = UnqualifiedName::parse(ctx, subs, tail)?;
return Ok((UnscopedName::Std(name), tail));
}
let (name, tail) = UnqualifiedName::parse(ctx, subs, input)?;
Ok((UnscopedName::Unqualified(name), tail))
}
}
impl<'subs, W> Demangle<'subs, W> for UnscopedName
where
W: 'subs + DemangleWrite,
{
fn demangle<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result {
let ctx = try_begin_demangle!(self, ctx, scope);
match *self {
UnscopedName::Unqualified(ref unqualified) => unqualified.demangle(ctx, scope),
UnscopedName::Std(ref std) => {
write!(ctx, "std::")?;
std.demangle(ctx, scope)
}
}
}
}
impl<'a> GetLeafName<'a> for UnscopedName {
fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option<LeafName<'a>> {
match *self {
UnscopedName::Unqualified(ref name) | UnscopedName::Std(ref name) => {
name.get_leaf_name(subs)
}
}
}
}
impl IsCtorDtorConversion for UnscopedName {
fn is_ctor_dtor_conversion(&self, subs: &SubstitutionTable) -> bool {
match *self {
UnscopedName::Unqualified(ref name) | UnscopedName::Std(ref name) => name.is_ctor_dtor_conversion(subs),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnscopedTemplateName(UnscopedName);
define_handle! {
pub enum UnscopedTemplateNameHandle {
extra NonSubstitution(NonSubstitution),
}
}
impl Parse for UnscopedTemplateNameHandle {
fn parse<'a, 'b>(
ctx: &'a ParseContext,
subs: &'a mut SubstitutionTable,
input: IndexStr<'b>,
) -> Result<(UnscopedTemplateNameHandle, IndexStr<'b>)> {
try_begin_parse!("UnscopedTemplateNameHandle", ctx, input);
if let Ok((name, tail)) = UnscopedName::parse(ctx, subs, input) {
let name = UnscopedTemplateName(name);
let idx = subs.insert(Substitutable::UnscopedTemplateName(name));
let handle = UnscopedTemplateNameHandle::BackReference(idx);
return Ok((handle, tail));
}
let (sub, tail) = Substitution::parse(ctx, subs, input)?;
match sub {
Substitution::WellKnown(component) => {
Ok((UnscopedTemplateNameHandle::WellKnown(component), tail))
}
Substitution::BackReference(idx) => {
Ok((UnscopedTemplateNameHandle::BackReference(idx), tail))
}
}
}
}
impl<'subs, W> Demangle<'subs, W> for UnscopedTemplateName
where
W: 'subs + DemangleWrite,
{
fn demangle<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result {
let ctx = try_begin_demangle!(self, ctx, scope);
self.0.demangle(ctx, scope)
}
}
impl<'a> GetLeafName<'a> for UnscopedTemplateName {
fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option<LeafName<'a>> {
self.0.get_leaf_name(subs)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum NestedName {
Unqualified(
CvQualifiers,
Option<RefQualifier>,
PrefixHandle,
UnqualifiedName,
),
Template(CvQualifiers, Option<RefQualifier>, PrefixHandle),
}
impl Parse for NestedName {
fn parse<'a, 'b>(
ctx: &'a ParseContext,
subs: &'a mut SubstitutionTable,
input: IndexStr<'b>,
) -> Result<(NestedName, IndexStr<'b>)> {
try_begin_parse!("NestedName", ctx, input);
let tail = consume(b"N", input)?;
let (cv_qualifiers, tail) = if let Ok((q, tail)) = CvQualifiers::parse(ctx, subs, tail) {
(q, tail)
} else {
(Default::default(), tail)
};
let (ref_qualifier, tail) = if let Ok((r, tail)) = RefQualifier::parse(ctx, subs, tail) {
(Some(r), tail)
} else {
(None, tail)
};
let (prefix, tail) = PrefixHandle::parse(ctx, subs, tail)?;
let tail = consume(b"E", tail)?;
let substitutable = match prefix {
PrefixHandle::BackReference(idx) => subs.get(idx),
PrefixHandle::NonSubstitution(NonSubstitution(idx)) => subs.get_non_substitution(idx),
PrefixHandle::WellKnown(_) => None,
};
match substitutable {
Some(&Substitutable::Prefix(Prefix::Nested(ref prefix, ref name))) => Ok((
NestedName::Unqualified(cv_qualifiers, ref_qualifier, prefix.clone(), name.clone()),
tail,
)),
Some(&Substitutable::Prefix(Prefix::Template(..))) => Ok((
NestedName::Template(cv_qualifiers, ref_qualifier, prefix),
tail,
)),
_ => Err(error::Error::UnexpectedText),
}
}
}
impl NestedName {