Skip to content

Commit 9eea600

Browse files
authored
Demangle explicitly named object parameters (#299)
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
1 parent 47ca4bb commit 9eea600

File tree

2 files changed

+116
-27
lines changed

2 files changed

+116
-27
lines changed

src/ast.rs

Lines changed: 107 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,9 @@ where
556556
// argument pack.
557557
is_template_argument_pack: bool,
558558

559+
// We are currently demangling an object with an explicit named parameter.
560+
is_explicit_obj_param: bool,
561+
559562
// Whether to show function parameters.
560563
// This must be set to true before calling `demangle` on `Encoding`
561564
// unless that call is via the toplevel call to `MangledName::demangle`.
@@ -615,6 +618,7 @@ where
615618
is_template_prefix: false,
616619
is_template_prefix_in_nested_name: false,
617620
is_template_argument_pack: false,
621+
is_explicit_obj_param: false,
618622
show_params: !options.no_params,
619623
show_return_type: !options.no_return_type,
620624
show_expression_literal_types: !options.hide_expression_literal_types,
@@ -1036,6 +1040,12 @@ where
10361040
return Ok(());
10371041
}
10381042

1043+
// Only the first param should have `this`.
1044+
if ctx.is_explicit_obj_param {
1045+
write!(ctx, "this ")?;
1046+
ctx.is_explicit_obj_param = false;
1047+
}
1048+
10391049
let mut need_comma = false;
10401050
for arg in self.iter() {
10411051
if need_comma {
@@ -1938,6 +1948,8 @@ impl<'a> GetLeafName<'a> for UnscopedTemplateName {
19381948
/// ```text
19391949
/// <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
19401950
/// ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E
1951+
/// ::= N H <prefix> <unqualified-name> E
1952+
/// ::= N H <template-prefix> <template-args> E
19411953
/// ```
19421954
#[derive(Clone, Debug, PartialEq, Eq)]
19431955
pub enum NestedName {
@@ -1951,6 +1963,16 @@ pub enum NestedName {
19511963

19521964
/// A nested template name. The `<template-args>` are part of the `PrefixHandle`.
19531965
Template(CvQualifiers, Option<RefQualifier>, PrefixHandle),
1966+
1967+
/// A nested name with an explicit object.
1968+
UnqualifiedExplicitObject(
1969+
Option<PrefixHandle>,
1970+
UnqualifiedName,
1971+
ExplicitObjectParameter,
1972+
),
1973+
1974+
/// A nested template name with an explicit object.
1975+
TemplateExplicitObject(PrefixHandle, ExplicitObjectParameter),
19541976
}
19551977

19561978
impl Parse for NestedName {
@@ -1963,19 +1985,29 @@ impl Parse for NestedName {
19631985

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

1966-
let (cv_qualifiers, tail) =
1967-
if let Ok((q, tail)) = try_recurse!(CvQualifiers::parse(ctx, subs, tail)) {
1968-
(q, tail)
1969-
} else {
1970-
(Default::default(), tail)
1971-
};
1988+
let (cv_qualifiers, ref_qualifier, explicit_obj_param, tail) = match tail.peek() {
1989+
Some(b'H') => {
1990+
let (explicit_obj_param, tail) = ExplicitObjectParameter::parse(ctx, subs, tail)?;
1991+
(Default::default(), None, Some(explicit_obj_param), tail)
1992+
}
1993+
_ => {
1994+
let (cv_qualifiers, tail) =
1995+
if let Ok((q, tail)) = try_recurse!(CvQualifiers::parse(ctx, subs, tail)) {
1996+
(q, tail)
1997+
} else {
1998+
(Default::default(), tail)
1999+
};
19722000

1973-
let (ref_qualifier, tail) =
1974-
if let Ok((r, tail)) = try_recurse!(RefQualifier::parse(ctx, subs, tail)) {
1975-
(Some(r), tail)
1976-
} else {
1977-
(None, tail)
1978-
};
2001+
let (ref_qualifier, tail) =
2002+
if let Ok((r, tail)) = try_recurse!(RefQualifier::parse(ctx, subs, tail)) {
2003+
(Some(r), tail)
2004+
} else {
2005+
(None, tail)
2006+
};
2007+
2008+
(cv_qualifiers, ref_qualifier, None, tail)
2009+
}
2010+
};
19792011

19802012
let (prefix, tail) = PrefixHandle::parse(ctx, subs, tail)?;
19812013
let tail = consume(b"E", tail)?;
@@ -1986,12 +2018,12 @@ impl Parse for NestedName {
19862018
PrefixHandle::WellKnown(_) => None,
19872019
};
19882020

1989-
match substitutable {
1990-
Some(&Substitutable::Prefix(Prefix::Unqualified(ref name))) => Ok((
2021+
match (substitutable, explicit_obj_param) {
2022+
(Some(&Substitutable::Prefix(Prefix::Unqualified(ref name))), None) => Ok((
19912023
NestedName::Unqualified(cv_qualifiers, ref_qualifier, None, name.clone()),
19922024
tail,
19932025
)),
1994-
Some(&Substitutable::Prefix(Prefix::Nested(ref prefix, ref name))) => Ok((
2026+
(Some(&Substitutable::Prefix(Prefix::Nested(ref prefix, ref name))), None) => Ok((
19952027
NestedName::Unqualified(
19962028
cv_qualifiers,
19972029
ref_qualifier,
@@ -2000,20 +2032,38 @@ impl Parse for NestedName {
20002032
),
20012033
tail,
20022034
)),
2003-
Some(&Substitutable::Prefix(Prefix::Template(..))) => Ok((
2035+
(Some(&Substitutable::Prefix(Prefix::Template(..))), None) => Ok((
20042036
NestedName::Template(cv_qualifiers, ref_qualifier, prefix),
20052037
tail,
20062038
)),
2039+
(Some(&Substitutable::Prefix(Prefix::Unqualified(ref name))), Some(param)) => Ok((
2040+
NestedName::UnqualifiedExplicitObject(None, name.clone(), param),
2041+
tail,
2042+
)),
2043+
(Some(&Substitutable::Prefix(Prefix::Nested(ref prefix, ref name))), Some(param)) => {
2044+
Ok((
2045+
NestedName::UnqualifiedExplicitObject(
2046+
Some(prefix.clone()),
2047+
name.clone(),
2048+
param,
2049+
),
2050+
tail,
2051+
))
2052+
}
2053+
(Some(&Substitutable::Prefix(Prefix::Template(..))), Some(param)) => {
2054+
Ok((NestedName::TemplateExplicitObject(prefix, param), tail))
2055+
}
20072056
_ => Err(error::Error::UnexpectedText),
20082057
}
20092058
}
20102059
}
20112060

20122061
impl NestedName {
20132062
/// Get the CV-qualifiers for this name.
2014-
pub fn cv_qualifiers(&self) -> &CvQualifiers {
2063+
pub fn cv_qualifiers(&self) -> Option<&CvQualifiers> {
20152064
match *self {
2016-
NestedName::Unqualified(ref q, ..) | NestedName::Template(ref q, ..) => q,
2065+
NestedName::Unqualified(ref q, ..) | NestedName::Template(ref q, ..) => Some(q),
2066+
_ => None,
20172067
}
20182068
}
20192069

@@ -2031,8 +2081,20 @@ impl NestedName {
20312081
// conceptually belongs to `<nested-name>`.
20322082
fn prefix(&self) -> Option<&PrefixHandle> {
20332083
match *self {
2034-
NestedName::Unqualified(_, _, ref p, _) => p.as_ref(),
2035-
NestedName::Template(_, _, ref p) => Some(p),
2084+
NestedName::Unqualified(_, _, ref p, _)
2085+
| NestedName::UnqualifiedExplicitObject(ref p, ..) => p.as_ref(),
2086+
NestedName::Template(_, _, ref p) | NestedName::TemplateExplicitObject(ref p, _) => {
2087+
Some(p)
2088+
}
2089+
}
2090+
}
2091+
2092+
/// Check to see if the object has an explicit named parameter.
2093+
pub fn has_explicit_obj_param(&self) -> bool {
2094+
match *self {
2095+
NestedName::UnqualifiedExplicitObject(_, _, ref _e)
2096+
| NestedName::TemplateExplicitObject(_, ref _e) => true,
2097+
_ => false,
20362098
}
20372099
}
20382100
}
@@ -2049,7 +2111,8 @@ where
20492111
let ctx = try_begin_demangle!(self, ctx, scope);
20502112

20512113
match *self {
2052-
NestedName::Unqualified(_, _, ref p, ref name) => {
2114+
NestedName::Unqualified(_, _, ref p, ref name)
2115+
| NestedName::UnqualifiedExplicitObject(ref p, ref name, _) => {
20532116
ctx.push_demangle_node(DemangleNodeType::NestedName);
20542117
if let Some(p) = p.as_ref() {
20552118
p.demangle(ctx, scope)?;
@@ -2058,19 +2121,25 @@ where
20582121
name.demangle(ctx, scope)?;
20592122
ctx.pop_demangle_node();
20602123
}
2061-
NestedName::Template(_, _, ref p) => {
2124+
NestedName::Template(_, _, ref p) | NestedName::TemplateExplicitObject(ref p, _) => {
20622125
ctx.is_template_prefix_in_nested_name = true;
20632126
p.demangle(ctx, scope)?;
20642127
ctx.is_template_prefix_in_nested_name = false;
20652128
}
20662129
}
20672130

2131+
if self.has_explicit_obj_param() {
2132+
ctx.is_explicit_obj_param = true;
2133+
}
2134+
20682135
if let Some(inner) = ctx.pop_inner() {
20692136
inner.demangle_as_inner(ctx, scope)?;
20702137
}
20712138

2072-
if self.cv_qualifiers() != &CvQualifiers::default() && ctx.show_params {
2073-
self.cv_qualifiers().demangle(ctx, scope)?;
2139+
if let Some(cv_qualifiers) = self.cv_qualifiers() {
2140+
if cv_qualifiers != &CvQualifiers::default() && ctx.show_params {
2141+
cv_qualifiers.demangle(ctx, scope)?;
2142+
}
20742143
}
20752144

20762145
if let Some(ref refs) = self.ref_qualifier() {
@@ -2085,7 +2154,8 @@ where
20852154
impl GetTemplateArgs for NestedName {
20862155
fn get_template_args<'a>(&'a self, subs: &'a SubstitutionTable) -> Option<&'a TemplateArgs> {
20872156
match *self {
2088-
NestedName::Template(_, _, ref prefix) => prefix.get_template_args(subs),
2157+
NestedName::Template(_, _, ref prefix)
2158+
| NestedName::TemplateExplicitObject(ref prefix, _) => prefix.get_template_args(subs),
20892159
_ => None,
20902160
}
20912161
}
@@ -2094,10 +2164,12 @@ impl GetTemplateArgs for NestedName {
20942164
impl<'a> GetLeafName<'a> for NestedName {
20952165
fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option<LeafName<'a>> {
20962166
match *self {
2097-
NestedName::Unqualified(_, _, ref prefix, ref name) => name
2167+
NestedName::Unqualified(_, _, ref prefix, ref name)
2168+
| NestedName::UnqualifiedExplicitObject(ref prefix, ref name, _) => name
20982169
.get_leaf_name(subs)
20992170
.or_else(|| prefix.as_ref().and_then(|p| p.get_leaf_name(subs))),
2100-
NestedName::Template(_, _, ref prefix) => prefix.get_leaf_name(subs),
2171+
NestedName::Template(_, _, ref prefix)
2172+
| NestedName::TemplateExplicitObject(ref prefix, _) => prefix.get_leaf_name(subs),
21012173
}
21022174
}
21032175
}
@@ -4064,6 +4136,14 @@ define_vocabulary! {
40644136
}
40654137
}
40664138

4139+
define_vocabulary! {
4140+
/// A named explicit object parameter.
4141+
#[derive(Clone, Debug, PartialEq, Eq)]
4142+
pub enum ExplicitObjectParameter {
4143+
ExplicitObjectParameter(b"H", "this")
4144+
}
4145+
}
4146+
40674147
define_vocabulary! {
40684148
/// A one of the standard variants of the <builtin-type> production.
40694149
///

tests/tests.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,15 @@ demangles!(_Z1fDSDRj, "f(_Sat unsigned _Fract)");
644644
demangles!(_Z1fDSDRl, "f(_Sat long _Fract)");
645645
demangles!(_Z1fDSDRm, "f(_Sat unsigned long _Fract)");
646646

647+
// Test explicit named object parameter
648+
demangles!(_ZNH1S3fooES_, "S::foo(this S)");
649+
demangles!(_ZNH1S3barILi5EiEEvS_T0_, "void S::bar<5, int>(this S, int)");
650+
demangles!(_ZNH1S3bazERKS_, "S::baz(this S const&)");
651+
demangles!(
652+
_ZZNH2ns3Foo3fooES0_iENH4Foo24foo2EOKS1_,
653+
"ns::Foo::foo(this ns::Foo, int)::Foo2::foo2(this Foo2 const&&)"
654+
);
655+
647656
// This symbol previously ran into some mutual recursion and unbounded growth of the substitution table.
648657
// See <https://github.com/gimli-rs/cpp_demangle/issues/277> and <https://github.com/getsentry/symbolic/issues/477>
649658
#[test]

0 commit comments

Comments
 (0)