Skip to content

Commit

Permalink
Demangle explicitly named object parameters
Browse files Browse the repository at this point in the history
This is a patch that allows for the ability to demangle code like:
```
struct Foo {
  void bar(this Foo && self);
};
```

An example mangling is `_ZNH1S3fooES_` which then demangles to
`S::foo(this S)`.

The proposal can be found here: itanium-cxx-abi/cxx-abi#148
  • Loading branch information
Saldivarcher committed Jan 2, 2025
1 parent 15bbde1 commit d1b6b2f
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 21 deletions.
107 changes: 86 additions & 21 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,8 @@ where
// argument pack.
is_template_argument_pack: bool,

is_explicit_obj_param: bool,

// Whether to show function parameters.
// This must be set to true before calling `demangle` on `Encoding`
// unless that call is via the toplevel call to `MangledName::demangle`.
Expand Down Expand Up @@ -615,6 +617,7 @@ where
is_template_prefix: false,
is_template_prefix_in_nested_name: false,
is_template_argument_pack: false,
is_explicit_obj_param: false,
show_params: !options.no_params,
show_return_type: !options.no_return_type,
show_expression_literal_types: !options.hide_expression_literal_types,
Expand Down Expand Up @@ -1037,6 +1040,12 @@ where
}

let mut need_comma = false;

// Only the first param should have `this`.
if ctx.is_explicit_obj_param {
write!(ctx, "this ")?;
}

for arg in self.iter() {
if need_comma {
write!(ctx, ", ")?;
Expand Down Expand Up @@ -1938,19 +1947,28 @@ impl<'a> GetLeafName<'a> for UnscopedTemplateName {
/// ```text
/// <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
/// ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E
/// ::= N H <prefix> <unqualified-name> E
/// ::= N H <template-prefix> <template-args> E
/// ```
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum NestedName {
/// A nested name.
Unqualified(
CvQualifiers,
Option<RefQualifier>,
Option<ExplicitObjectParameter>,
PrefixHandle,
UnqualifiedName,
),

/// A nested template name. The `<template-args>` are part of the `PrefixHandle`.
Template(CvQualifiers, Option<RefQualifier>, PrefixHandle),
Template(
CvQualifiers,
Option<RefQualifier>,
Option<ExplicitObjectParameter>,
PrefixHandle,
),
}

impl Parse for NestedName {
Expand All @@ -1963,19 +1981,28 @@ impl Parse for NestedName {

let tail = consume(b"N", input)?;

let (cv_qualifiers, tail) =
if let Ok((q, tail)) = try_recurse!(CvQualifiers::parse(ctx, subs, tail)) {
(q, tail)
} else {
(Default::default(), tail)
};
let (cv_qualifiers, ref_qualifier, explicit_obj_param, tail) = match tail.peek() {
Some(b'H') => {
let (explicit_obj_param, tail) = ExplicitObjectParameter::parse(ctx, subs, tail)?;
(Default::default(), None, Some(explicit_obj_param), tail)
}
_ => {
let (cv_qualifiers, tail) =
if let Ok((q, tail)) = try_recurse!(CvQualifiers::parse(ctx, subs, tail)) {
(q, tail)
} else {
(Default::default(), tail)
};

let (ref_qualifier, tail) =
if let Ok((r, tail)) = try_recurse!(RefQualifier::parse(ctx, subs, tail)) {
(Some(r), tail)
} else {
(None, tail)
};
let (ref_qualifier, tail) =
if let Ok((r, tail)) = try_recurse!(RefQualifier::parse(ctx, subs, tail)) {
(Some(r), tail)
} else {
(None, tail)
};
(cv_qualifiers, ref_qualifier, None, tail)
}
};

let (prefix, tail) = PrefixHandle::parse(ctx, subs, tail)?;
let tail = consume(b"E", tail)?;
Expand All @@ -1988,11 +2015,17 @@ impl Parse for NestedName {

match substitutable {
Some(&Substitutable::Prefix(Prefix::Nested(ref prefix, ref name))) => Ok((
NestedName::Unqualified(cv_qualifiers, ref_qualifier, prefix.clone(), name.clone()),
NestedName::Unqualified(
cv_qualifiers,
ref_qualifier,
explicit_obj_param,
prefix.clone(),
name.clone(),
),
tail,
)),
Some(&Substitutable::Prefix(Prefix::Template(..))) => Ok((
NestedName::Template(cv_qualifiers, ref_qualifier, prefix),
NestedName::Template(cv_qualifiers, ref_qualifier, explicit_obj_param, prefix),
tail,
)),
_ => Err(error::Error::UnexpectedText),
Expand All @@ -2017,12 +2050,20 @@ impl NestedName {
}
}

/// Get the ref-qualifier for this name, if one exists.
pub fn has_explicit_obj_param(&self) -> bool {
match *self {
NestedName::Unqualified(_, _, Some(ref _r), ..)
| NestedName::Template(_, _, Some(ref _r), ..) => true,
_ => false,
}
}
// Not public because the prefix means different things for different
// variants, and for `::Template` it actually contains part of what
// conceptually belongs to `<nested-name>`.
fn prefix(&self) -> &PrefixHandle {
match *self {
NestedName::Unqualified(_, _, ref p, _) | NestedName::Template(_, _, ref p) => p,
NestedName::Unqualified(_, _, _, ref p, _) | NestedName::Template(_, _, _, ref p) => p,
}
}
}
Expand All @@ -2039,7 +2080,7 @@ where
let ctx = try_begin_demangle!(self, ctx, scope);

match *self {
NestedName::Unqualified(_, _, ref p, ref name) => {
NestedName::Unqualified(_, _, _, ref p, ref name) => {
ctx.push_demangle_node(DemangleNodeType::NestedName);
p.demangle(ctx, scope)?;
if name.accepts_double_colon() {
Expand All @@ -2048,13 +2089,17 @@ where
name.demangle(ctx, scope)?;
ctx.pop_demangle_node();
}
NestedName::Template(_, _, ref p) => {
NestedName::Template(_, _, _, ref p) => {
ctx.is_template_prefix_in_nested_name = true;
p.demangle(ctx, scope)?;
ctx.is_template_prefix_in_nested_name = false;
}
}

if self.has_explicit_obj_param() {
ctx.is_explicit_obj_param = true;
}

if let Some(inner) = ctx.pop_inner() {
inner.demangle_as_inner(ctx, scope)?;
}
Expand All @@ -2075,7 +2120,7 @@ where
impl GetTemplateArgs for NestedName {
fn get_template_args<'a>(&'a self, subs: &'a SubstitutionTable) -> Option<&'a TemplateArgs> {
match *self {
NestedName::Template(_, _, ref prefix) => prefix.get_template_args(subs),
NestedName::Template(_, _, _, ref prefix) => prefix.get_template_args(subs),
_ => None,
}
}
Expand All @@ -2084,10 +2129,10 @@ impl GetTemplateArgs for NestedName {
impl<'a> GetLeafName<'a> for NestedName {
fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option<LeafName<'a>> {
match *self {
NestedName::Unqualified(_, _, ref prefix, ref name) => name
NestedName::Unqualified(_, _, _, ref prefix, ref name) => name
.get_leaf_name(subs)
.or_else(|| prefix.get_leaf_name(subs)),
NestedName::Template(_, _, ref prefix) => prefix.get_leaf_name(subs),
NestedName::Template(_, _, _, ref prefix) => prefix.get_leaf_name(subs),
}
}
}
Expand Down Expand Up @@ -4018,6 +4063,19 @@ define_vocabulary! {
}
}

define_vocabulary! {
/// A <ref-qualifier> production.
///
/// ```text
/// <ref-qualifier> ::= R # & ref-qualifier
/// ::= O # && ref-qualifier
/// ```
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ExplicitObjectParameter {
ExplicitObjectParameter(b"H", "this")
}
}

define_vocabulary! {
/// A one of the standard variants of the <builtin-type> production.
///
Expand Down Expand Up @@ -8538,6 +8596,7 @@ mod tests {
Ok => {
b"NS0_3abcE..." => {
Name::Nested(NestedName::Unqualified(CvQualifiers::default(),
None,
None,
PrefixHandle::BackReference(1),
UnqualifiedName::Source(SourceName(Identifier {
Expand Down Expand Up @@ -8660,6 +8719,7 @@ mod tests {
const_: true,
},
Some(RefQualifier::RValueRef),
None,
PrefixHandle::BackReference(0),
UnqualifiedName::Source(
SourceName(Identifier {
Expand All @@ -8677,6 +8737,7 @@ mod tests {
const_: false,
},
Some(RefQualifier::RValueRef),
None,
PrefixHandle::BackReference(0),
UnqualifiedName::Source(
SourceName(Identifier {
Expand All @@ -8694,6 +8755,7 @@ mod tests {
const_: false,
},
None,
None,
PrefixHandle::BackReference(0),
UnqualifiedName::Source(
SourceName(Identifier {
Expand All @@ -8711,6 +8773,7 @@ mod tests {
const_: true,
},
Some(RefQualifier::RValueRef),
None,
PrefixHandle::NonSubstitution(NonSubstitution(0))),
b"...",
[
Expand All @@ -8732,6 +8795,7 @@ mod tests {
const_: false,
},
Some(RefQualifier::RValueRef),
None,
PrefixHandle::NonSubstitution(NonSubstitution(0))),
b"...",
[
Expand All @@ -8753,6 +8817,7 @@ mod tests {
const_: false,
},
None,
None,
PrefixHandle::NonSubstitution(NonSubstitution(0))),
b"...",
[
Expand Down
11 changes: 11 additions & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,17 @@ demangles!(
"deluge::gui::menu_item::MasterTranspose::MenuItem(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)"
);

demangles!(_ZNH1S3fooES_, "S::foo(this S)");

demangles!(_ZNH1S3barILi5EiEEvS_T0_, "void S::bar<5, int>(this S, int)");

demangles!(_ZNH1S3bazERKS_, "S::baz(this S const&)");

demangles!(
_ZZNH2ns3Foo3fooES0_iENH4Foo24foo2EOKS1_,
"ns::Foo::foo(this ns::Foo, int)::Foo2::foo2(this Foo2 const&&)"
);

// This symbol previously ran into some mutual recursion and unbounded growth of the substitution table.
// See <https://github.com/gimli-rs/cpp_demangle/issues/277> and <https://github.com/getsentry/symbolic/issues/477>
#[test]
Expand Down

0 comments on commit d1b6b2f

Please sign in to comment.