Skip to content

Commit f80ecd0

Browse files
trumankemesare
authored andcommitted
Fix vtable type name parsing
1 parent 742370b commit f80ecd0

File tree

1 file changed

+80
-48
lines changed

1 file changed

+80
-48
lines changed

plugins/pdb-ng/src/symbol_parser.rs

Lines changed: 80 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1885,54 +1885,17 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> {
18851885
}
18861886
}
18871887

1888-
// VTables have types on their data symbols,
1889-
if let Some((last, class_name)) = name.split_last() {
1890-
if last.contains("`vftable'") {
1891-
let mut vt_name = class_name.with_item("VTable");
1892-
if last.contains("{for") {
1893-
// DerivedClass::`vftable'{for `BaseClass'}
1894-
let mut base_name = last.to_owned();
1895-
base_name.drain(0..("`vftable'{for `".len()));
1896-
base_name.drain((base_name.len() - "'}".len())..(base_name.len()));
1897-
// Multiply inherited classes have multiple vtable types
1898-
// TODO: Do that
1899-
vt_name = QualifiedName::new(vec![base_name, "VTable".to_string()]);
1900-
}
1901-
1902-
vt_name = vt_name
1903-
.replace("class ", "")
1904-
.replace("struct ", "")
1905-
.replace("enum ", "");
1906-
1907-
if let Some(ty) = self.named_types.get(&vt_name) {
1908-
t = Some(Conf::new(
1909-
Type::named_type_from_type(vt_name, ty.as_ref()),
1910-
DEMANGLE_CONFIDENCE,
1911-
));
1912-
} else {
1913-
// Sometimes the demangler has trouble with `class Foo` in templates
1914-
vt_name = vt_name
1915-
.replace("class ", "")
1916-
.replace("struct ", "")
1917-
.replace("enum ", "");
1918-
1919-
if let Some(ty) = self.named_types.get(&vt_name) {
1920-
t = Some(Conf::new(
1921-
Type::named_type_from_type(vt_name, ty.as_ref()),
1922-
DEMANGLE_CONFIDENCE,
1923-
));
1924-
} else {
1925-
t = Some(Conf::new(
1926-
Type::named_type_from_type(
1927-
vt_name,
1928-
Type::structure(StructureBuilder::new().finalize().as_ref())
1929-
.as_ref(),
1930-
),
1931-
DEMANGLE_CONFIDENCE,
1932-
));
1933-
}
1934-
}
1935-
}
1888+
// VTables have types on their data symbols
1889+
// Format: "ClassName::`vftable'" or "ClassName::`vftable'{for `BaseClass'}"
1890+
if let Some(vt_name) = parse_vtable_type_name(&name) {
1891+
let ty =
1892+
self.named_types.get(&vt_name).cloned().unwrap_or_else(|| {
1893+
Type::structure(StructureBuilder::new().finalize().as_ref())
1894+
});
1895+
t = Some(Conf::new(
1896+
Type::named_type_from_type(vt_name, ty.as_ref()),
1897+
DEMANGLE_CONFIDENCE,
1898+
));
19361899
}
19371900

19381901
if let Some(last_name) = name.last_mut() {
@@ -2041,3 +2004,72 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> {
20412004
}
20422005
}
20432006
}
2007+
2008+
/// Parse a vtable symbol name and return the parts for the vtable type.
2009+
/// Takes the last element of (e.g., `"ClassName::`vftable'{for `BaseClass'}"`)
2010+
/// and converts it to a vtable type name (e.g., `["ClassName", "BaseClass", "VTable"]`).
2011+
fn parse_vtable_type_name(name: &QualifiedName) -> Option<QualifiedName> {
2012+
let (last, qualified_name_base) = name.items.split_last()?;
2013+
let (class_name, rest) = last.split_once("::`vftable'")?;
2014+
2015+
let clean = |s: &str| {
2016+
s.replace("class ", "")
2017+
.replace("struct ", "")
2018+
.replace("enum ", "")
2019+
};
2020+
2021+
let mut parts = qualified_name_base.to_vec();
2022+
parts.extend(class_name.split("::").map(|s| clean(s)));
2023+
2024+
if let Some(base_class) = rest
2025+
.split_once("{for `")
2026+
.and_then(|(_, base_start)| base_start.split_once("'}"))
2027+
.map(|(base, _)| base)
2028+
{
2029+
parts.push(clean(base_class));
2030+
}
2031+
2032+
parts.push("VTable".to_string());
2033+
Some(QualifiedName::new(parts))
2034+
}
2035+
2036+
#[cfg(test)]
2037+
mod tests {
2038+
use super::*;
2039+
2040+
/// (input_demangled_name, expected_output)
2041+
#[rustfmt::skip]
2042+
const VTABLE_TEST_CASES: &[(&str, &[&str])] = &[
2043+
// Simple vtables
2044+
("Base::`vftable'", &["Base", "VTable"]),
2045+
// Multiple inheritance with {for}
2046+
("MultiDerived::`vftable'{for `InterfaceA'}", &["MultiDerived", "InterfaceA", "VTable"]),
2047+
// Simple templates
2048+
("Container<int32_t>::`vftable'", &["Container<int32_t>", "VTable"]),
2049+
("Pair<int32_t,char>::`vftable'", &["Pair<int32_t,char>", "VTable"]),
2050+
("FixedArray<int32_t,10>::`vftable'", &["FixedArray<int32_t,10>", "VTable"]),
2051+
// Namespaced classes
2052+
("outer::inner::NamespacedTemplate<int32_t>::`vftable'", &["outer", "inner", "NamespacedTemplate<int32_t>", "VTable"]),
2053+
// Nested class inside template
2054+
("complex::HasNested<int32_t>::Nested::`vftable'", &["complex", "HasNested<int32_t>", "Nested", "VTable"]),
2055+
// Template with {for} clause
2056+
("TemplateMultiDerived<int32_t>::`vftable'{for `TemplateInterfaceA<int32_t>'}", &["TemplateMultiDerived<int32_t>", "TemplateInterfaceA<int32_t>", "VTable"]),
2057+
("TemplateDiamond<int32_t>::`vftable'{for `TemplateLeftVirtual<int32_t>'}", &["TemplateDiamond<int32_t>", "TemplateLeftVirtual<int32_t>", "VTable"]),
2058+
// Nested templates - note: "class " prefix gets stripped
2059+
("Wrapper<class Container<int32_t> >::`vftable'", &["Wrapper<Container<int32_t> >", "VTable"]),
2060+
// Class/struct/enum keyword removal
2061+
("Wrapper<class Foo>::`vftable'", &["Wrapper<Foo>", "VTable"]),
2062+
("Container<struct Bar>::`vftable'", &["Container<Bar>", "VTable"]),
2063+
("Holder<enum Baz>::`vftable'", &["Holder<Baz>", "VTable"]),
2064+
];
2065+
2066+
#[test]
2067+
fn test_vtable_type_name_parsing() {
2068+
for (input, expected) in VTABLE_TEST_CASES {
2069+
let name = QualifiedName::new(vec![input.to_string()]);
2070+
let result = parse_vtable_type_name(&name);
2071+
let expected_qn = QualifiedName::new(expected.into_iter().cloned());
2072+
assert_eq!(result, Some(expected_qn), "Failed for input: {}", input);
2073+
}
2074+
}
2075+
}

0 commit comments

Comments
 (0)