From 64e8b3e083343783c5b3b6329ea940f375b057b3 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 5 Nov 2022 12:56:23 -0400 Subject: [PATCH] Fix qualifier ordering, support volatile pointers & add DemangleOptions --- Cargo.toml | 2 +- README.md | 4 +- src/lib.rs | 253 +++++++++++++++++++++++++++++++++++----------------- src/main.rs | 9 +- 4 files changed, 180 insertions(+), 88 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e2c9032..6882fd4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cwdemangle" -version = "0.1.2" +version = "0.1.3" edition = "2018" authors = ["Luke Street "] license = "MIT OR Apache-2.0" diff --git a/README.md b/README.md index 52c61c9..907ebe1 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ cwdemangle 'BuildLight__9CGuiLightCFv' Library: ```rust -use cwdemangle::demangle; +use cwdemangle::{demangle, DemangleOptions}; -if let Some(result) = demangle("BuildLight__9CGuiLightCFv") { +if let Some(result) = demangle("BuildLight__9CGuiLightCFv", &DemangleOptions::default()) { println!("{}", result); } else { eprintln!("Couldn't demangle symbol (not a C++ symbol?)"); diff --git a/src/lib.rs b/src/lib.rs index 1fbc8ba..ea84f2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,19 +1,44 @@ use std::str::FromStr; +pub struct DemangleOptions { + /// Replace `(void)` function parameters with `()` + pub omit_empty_parameters: bool, +} + +impl Default for DemangleOptions { + fn default() -> Self { DemangleOptions { omit_empty_parameters: true } } +} + fn parse_qualifiers(mut str: &str) -> (String, String, &str) { let mut pre = String::new(); let mut post = String::new(); for c in str.chars() { match c { - 'P' => post.push('*'), - 'R' => post.push('&'), + 'P' => { + if pre.is_empty() { + post.insert(0, '*'); + } else { + post.insert_str(0, format!("* {}", pre.trim_end()).as_str()); + pre.clear(); + } + } + 'R' => { + if pre.is_empty() { + post.insert(0, '&'); + } else { + post.insert_str(0, format!("& {}", pre.trim_end()).as_str()); + pre.clear(); + } + } 'C' => pre.push_str("const "), + 'V' => pre.push_str("volatile "), 'U' => pre.push_str("unsigned "), 'S' => pre.push_str("signed "), _ => break, }; str = &str[1..]; } + post.truncate(post.trim_end().len()); (pre, post, str) } @@ -26,7 +51,10 @@ fn parse_digits(str: &str) -> Option<(usize, &str)> { } } -fn demangle_template_args(mut str: &str) -> Option<(&str, String)> { +fn demangle_template_args<'a>( + mut str: &'a str, + options: &DemangleOptions, +) -> Option<(&'a str, String)> { let tmpl_args = if let Some(start_idx) = str.find('<') { let end_idx = str.rfind('>')?; if end_idx < start_idx { @@ -36,7 +64,7 @@ fn demangle_template_args(mut str: &str) -> Option<(&str, String)> { str = &str[..start_idx]; let mut tmpl_args = "<".to_string(); while !args.is_empty() { - let (arg, arg_post, rest) = demangle_arg(args)?; + let (arg, arg_post, rest) = demangle_arg(args, options)?; tmpl_args += arg.as_str(); tmpl_args += arg_post.as_str(); if rest.is_empty() { @@ -54,7 +82,7 @@ fn demangle_template_args(mut str: &str) -> Option<(&str, String)> { Some((str, tmpl_args)) } -fn demangle_name(str: &str) -> Option<(String, String, &str)> { +fn demangle_name<'a>(str: &'a str, options: &DemangleOptions) -> Option<(String, String, &'a str)> { let (size, rest) = parse_digits(str)?; // hack for template argument constants if rest.is_empty() || rest.starts_with(',') { @@ -64,11 +92,14 @@ fn demangle_name(str: &str) -> Option<(String, String, &str)> { if rest.len() < size { return None; } - let (name, args) = demangle_template_args(&rest[..size])?; + let (name, args) = demangle_template_args(&rest[..size], options)?; Some((name.to_string(), format!("{}{}", name, args), &rest[size..])) } -fn demangle_qualified_name(mut str: &str) -> Option<(String, String, &str)> { +fn demangle_qualified_name<'a>( + mut str: &'a str, + options: &DemangleOptions, +) -> Option<(String, String, &'a str)> { if str.starts_with('Q') { if str.len() < 3 { return None; @@ -78,7 +109,7 @@ fn demangle_qualified_name(mut str: &str) -> Option<(String, String, &str)> { let mut last_class = String::new(); let mut qualified = String::new(); for i in 0..count { - let (class_name, full, rest) = demangle_name(str)?; + let (class_name, full, rest) = demangle_name(str, options)?; qualified += full.as_str(); last_class = class_name; str = rest; @@ -88,17 +119,20 @@ fn demangle_qualified_name(mut str: &str) -> Option<(String, String, &str)> { } Some((last_class, qualified, str)) } else { - demangle_name(str) + demangle_name(str, options) } } -fn demangle_arg(mut str: &str) -> Option<(String, String, &str)> { +fn demangle_arg<'a>( + mut str: &'a str, + options: &DemangleOptions, +) -> Option<(String, String, &'a str)> { let mut result = String::new(); let (mut pre, mut post, rest) = parse_qualifiers(str); result += pre.as_str(); str = rest; if str.starts_with('Q') || str.starts_with(|c: char| c.is_ascii_digit()) { - let (_, qualified, rest) = demangle_qualified_name(str)?; + let (_, qualified, rest) = demangle_qualified_name(str, options)?; result += qualified.as_str(); result += post.as_str(); return Some((result, String::new(), rest)); @@ -107,7 +141,7 @@ fn demangle_arg(mut str: &str) -> Option<(String, String, &str)> { let mut const_member = false; if str.starts_with('M') { is_member = true; - let (_, member, rest) = demangle_qualified_name(&str[1..])?; + let (_, member, rest) = demangle_qualified_name(&str[1..], options)?; pre = format!("{}::*{}", member, pre); if !rest.starts_with('F') { return None; @@ -126,17 +160,17 @@ fn demangle_arg(mut str: &str) -> Option<(String, String, &str)> { } else { return None; } - } else if post.ends_with('*') { - post = post[..post.len() - 1].to_string(); + } else if post.starts_with('*') { + post = post[1..].trim_start().to_string(); pre = format!("*{}", pre); } else { return None; } - let (args, rest) = demangle_function_args(str)?; + let (args, rest) = demangle_function_args(str, options)?; if !rest.starts_with('_') { return None; } - let (ret_pre, ret_post, rest) = demangle_arg(&rest[1..])?; + let (ret_pre, ret_post, rest) = demangle_arg(&rest[1..], options)?; let const_str = if const_member { " const" } else { "" }; let res_pre = format!("{} ({}{}", ret_pre, pre, post); let res_post = format!(")({}){}{}", args, const_str, ret_post); @@ -147,7 +181,7 @@ fn demangle_arg(mut str: &str) -> Option<(String, String, &str)> { if !rest.starts_with('_') { return None; } - let (arg_pre, arg_post, rest) = demangle_arg(&rest[1..])?; + let (arg_pre, arg_post, rest) = demangle_arg(&rest[1..], options)?; if !post.is_empty() { post = format!("({})", post); } @@ -173,13 +207,16 @@ fn demangle_arg(mut str: &str) -> Option<(String, String, &str)> { Some((result, String::new(), &str[1..])) } -fn demangle_function_args(mut str: &str) -> Option<(String, &str)> { +fn demangle_function_args<'a>( + mut str: &'a str, + options: &DemangleOptions, +) -> Option<(String, &'a str)> { let mut result = String::new(); while !str.is_empty() { if !result.is_empty() { result += ", "; } - let (arg, arg_post, rest) = demangle_arg(str)?; + let (arg, arg_post, rest) = demangle_arg(str, options)?; result += arg.as_str(); result += arg_post.as_str(); str = rest; @@ -190,12 +227,16 @@ fn demangle_function_args(mut str: &str) -> Option<(String, &str)> { Some((result, str)) } -fn demangle_special_function(str: &str, class_name: &str) -> Option { +fn demangle_special_function( + str: &str, + class_name: &str, + options: &DemangleOptions, +) -> Option { if let Some(rest) = str.strip_prefix("op") { - let (arg_pre, arg_post, _) = demangle_arg(rest)?; + let (arg_pre, arg_post, _) = demangle_arg(rest, options)?; return Some(format!("operator {}{}", arg_pre, arg_post)); } - let (op, args) = demangle_template_args(str)?; + let (op, args) = demangle_template_args(str, options)?; Some(format!( "{}{}", match op { @@ -250,7 +291,7 @@ fn demangle_special_function(str: &str, class_name: &str) -> Option { )) } -pub fn demangle(mut str: &str) -> Option { +pub fn demangle(mut str: &str, options: &DemangleOptions) -> Option { if !str.is_ascii() { return None; } @@ -294,7 +335,7 @@ pub fn demangle(mut str: &str) -> Option { fn_name = fn_name_out.to_string(); } } else { - let (name, args) = demangle_template_args(fn_name_out)?; + let (name, args) = demangle_template_args(fn_name_out, options)?; fn_name = format!("{}{}", name, args); } @@ -319,13 +360,13 @@ pub fn demangle(mut str: &str) -> Option { } let mut class_name = String::new(); if !str.starts_with('F') { - let (name, qualified_name, rest) = demangle_qualified_name(str)?; + let (name, qualified_name, rest) = demangle_qualified_name(str, options)?; class_name = name; qualified = qualified_name; str = rest; } if special { - fn_name = demangle_special_function(fn_name.as_str(), class_name.as_str())?; + fn_name = demangle_special_function(fn_name.as_str(), class_name.as_str(), options)?; } if str.starts_with('C') { str = &str[1..]; @@ -333,8 +374,8 @@ pub fn demangle(mut str: &str) -> Option { } if str.starts_with('F') { str = &str[1..]; - let (args, rest) = demangle_function_args(str)?; - if args == "void" { + let (args, rest) = demangle_function_args(str, options)?; + if options.omit_empty_parameters && args == "void" { fn_name = format!("{}()", fn_name); } else { fn_name = format!("{}({})", fn_name, args); @@ -343,7 +384,7 @@ pub fn demangle(mut str: &str) -> Option { } if str.starts_with('_') { str = &str[1..]; - let (ret_pre, ret_post, rest) = demangle_arg(str)?; + let (ret_pre, ret_post, rest) = demangle_arg(str, options)?; return_type_pre = ret_pre; return_type_post = ret_post; str = rest; @@ -372,13 +413,15 @@ mod tests { #[test] fn test_demangle_template_args() { + let options = DemangleOptions::default(); assert_eq!( - demangle_template_args("single_ptr<10CModelData>"), + demangle_template_args("single_ptr<10CModelData>", &options), Some(("single_ptr", "".to_string())) ); assert_eq!( demangle_template_args( - "basic_string,Q24rstl17rmemory_allocator>" + "basic_string,Q24rstl17rmemory_allocator>", + &options ), Some(( "basic_string", @@ -389,12 +432,16 @@ mod tests { #[test] fn test_demangle_name() { + let options = DemangleOptions::default(); assert_eq!( - demangle_name("24single_ptr<10CModelData>"), + demangle_name("24single_ptr<10CModelData>", &options), Some(("single_ptr".to_string(), "single_ptr".to_string(), "")) ); assert_eq!( - demangle_name("66basic_string,Q24rstl17rmemory_allocator>"), + demangle_name( + "66basic_string,Q24rstl17rmemory_allocator>", + &options + ), Some(( "basic_string".to_string(), "basic_string, rstl::rmemory_allocator>" @@ -406,17 +453,19 @@ mod tests { #[test] fn test_demangle_qualified_name() { + let options = DemangleOptions::default(); assert_eq!( - demangle_qualified_name("6CActor"), + demangle_qualified_name("6CActor", &options), Some(("CActor".to_string(), "CActor".to_string(), "")) ); assert_eq!( - demangle_qualified_name("Q29CVector3f4EDim"), + demangle_qualified_name("Q29CVector3f4EDim", &options), Some(("EDim".to_string(), "CVector3f::EDim".to_string(), "")) ); assert_eq!( demangle_qualified_name( - "Q24rstl66basic_string,Q24rstl17rmemory_allocator>" + "Q24rstl66basic_string,Q24rstl17rmemory_allocator>", + &options ), Some(( "basic_string".to_string(), @@ -429,181 +478,219 @@ mod tests { #[test] fn test_demangle_arg() { - assert_eq!(demangle_arg("v"), Some(("void".to_string(), "".to_string(), ""))); - assert_eq!(demangle_arg("b"), Some(("bool".to_string(), "".to_string(), ""))); + let options = DemangleOptions::default(); + assert_eq!(demangle_arg("v", &options), Some(("void".to_string(), "".to_string(), ""))); + assert_eq!(demangle_arg("b", &options), Some(("bool".to_string(), "".to_string(), ""))); assert_eq!( - demangle_arg("RC9CVector3fUc"), + demangle_arg("RC9CVector3fUc", &options), Some(("const CVector3f&".to_string(), "".to_string(), "Uc")) ); assert_eq!( - demangle_arg("Q24rstl14char_traits,"), + demangle_arg("Q24rstl14char_traits,", &options), Some(("rstl::char_traits".to_string(), "".to_string(), ",")) ); assert_eq!( - demangle_arg("PFPCcPCc_v"), + demangle_arg("PFPCcPCc_v", &options), Some(("void (*".to_string(), ")(const char*, const char*)".to_string(), "")) - ) + ); + assert_eq!( + demangle_arg("RCPCVPCVUi", &options), + Some(( + "const volatile unsigned int* const volatile* const&".to_string(), + "".to_string(), + "" + )) + ); } #[test] fn test_demangle_function_args() { - assert_eq!(demangle_function_args("v"), Some(("void".to_string(), ""))); - assert_eq!(demangle_function_args("b"), Some(("bool".to_string(), ""))); + let options = DemangleOptions::default(); + assert_eq!(demangle_function_args("v", &options), Some(("void".to_string(), ""))); + assert_eq!(demangle_function_args("b", &options), Some(("bool".to_string(), ""))); assert_eq!( - demangle_function_args("RC9CVector3fUc_x"), + demangle_function_args("RC9CVector3fUc_x", &options), Some(("const CVector3f&, unsigned char".to_string(), "_x")) ); } #[test] fn test_demangle() { - assert_eq!(demangle("cfunction"), None); - assert_eq!(demangle("__dt__6CActorFv"), Some("CActor::~CActor()".to_string())); + let options = DemangleOptions::default(); + assert_eq!(demangle("cfunction", &options), None); + assert_eq!(demangle("__dt__6CActorFv", &options), Some("CActor::~CActor()".to_string())); assert_eq!( - demangle("GetSfxHandle__6CActorCFv"), + demangle("GetSfxHandle__6CActorCFv", &options), Some("CActor::GetSfxHandle() const".to_string()) ); assert_eq!( - demangle("mNull__Q24rstl66basic_string,Q24rstl17rmemory_allocator>"), + demangle("mNull__Q24rstl66basic_string,Q24rstl17rmemory_allocator>", &options), Some("rstl::basic_string, rstl::rmemory_allocator>::mNull".to_string()) ); assert_eq!( - demangle("__ct__Q34rstl495red_black_tree,Q24rstl17rmemory_allocator>>,Q24rstl7less,Q24rstl17rmemory_allocator>>>,0,Q24rstl215select1st,Q24rstl17rmemory_allocator>>,Q24rstl7less,Q24rstl17rmemory_allocator>>>>,Q24rstl8less,Q24rstl17rmemory_allocator>8iteratorFPQ34rstl495red_black_tree,Q24rstl17rmemory_allocator>>,Q24rstl7less,Q24rstl17rmemory_allocator>>>,0,Q24rstl215select1st,Q24rstl17rmemory_allocator>>,Q24rstl7less,Q24rstl17rmemory_allocator>>>>,Q24rstl8less,Q24rstl17rmemory_allocator>4nodePCQ34rstl495red_black_tree,Q24rstl17rmemory_allocator>>,Q24rstl7less,Q24rstl17rmemory_allocator>>>,0,Q24rstl215select1st,Q24rstl17rmemory_allocator>>,Q24rstl7less,Q24rstl17rmemory_allocator>>>>,Q24rstl8less,Q24rstl17rmemory_allocator>6header"), + demangle("__ct__Q34rstl495red_black_tree,Q24rstl17rmemory_allocator>>,Q24rstl7less,Q24rstl17rmemory_allocator>>>,0,Q24rstl215select1st,Q24rstl17rmemory_allocator>>,Q24rstl7less,Q24rstl17rmemory_allocator>>>>,Q24rstl8less,Q24rstl17rmemory_allocator>8iteratorFPQ34rstl495red_black_tree,Q24rstl17rmemory_allocator>>,Q24rstl7less,Q24rstl17rmemory_allocator>>>,0,Q24rstl215select1st,Q24rstl17rmemory_allocator>>,Q24rstl7less,Q24rstl17rmemory_allocator>>>>,Q24rstl8less,Q24rstl17rmemory_allocator>4nodePCQ34rstl495red_black_tree,Q24rstl17rmemory_allocator>>,Q24rstl7less,Q24rstl17rmemory_allocator>>>,0,Q24rstl215select1st,Q24rstl17rmemory_allocator>>,Q24rstl7less,Q24rstl17rmemory_allocator>>>>,Q24rstl8less,Q24rstl17rmemory_allocator>6header", &options), Some("rstl::red_black_tree, rstl::rmemory_allocator>>, rstl::less, rstl::rmemory_allocator>>>, 0, rstl::select1st, rstl::rmemory_allocator>>, rstl::less, rstl::rmemory_allocator>>>>, rstl::less, rstl::rmemory_allocator>::iterator::iterator(rstl::red_black_tree, rstl::rmemory_allocator>>, rstl::less, rstl::rmemory_allocator>>>, 0, rstl::select1st, rstl::rmemory_allocator>>, rstl::less, rstl::rmemory_allocator>>>>, rstl::less, rstl::rmemory_allocator>::node*, const rstl::red_black_tree, rstl::rmemory_allocator>>, rstl::less, rstl::rmemory_allocator>>>, 0, rstl::select1st, rstl::rmemory_allocator>>, rstl::less, rstl::rmemory_allocator>>>>, rstl::less, rstl::rmemory_allocator>::header*)".to_string()), ); assert_eq!( - demangle("for_each,l>>__3stdFPP12MultiEmitterPP12MultiEmitterQ23std51binder2nd,l>_Q23std51binder2nd,l>"), + demangle("for_each,l>>__3stdFPP12MultiEmitterPP12MultiEmitterQ23std51binder2nd,l>_Q23std51binder2nd,l>", &options), Some("std::binder2nd, long> std::for_each, long>>(MultiEmitter**, MultiEmitter**, std::binder2nd, long>)".to_string()) ); assert_eq!( - demangle("__ct__Q43std3tr16detail383function_imp,Q33std3tr1228tuple,Q53std3tr112placeholders6detail5ph<2>,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat>>,0,1>FRCQ43std3tr16detail383function_imp,Q33std3tr1228tuple,Q53std3tr112placeholders6detail5ph<2>,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat>>,0,1>"), + demangle("__ct__Q43std3tr16detail383function_imp,Q33std3tr1228tuple,Q53std3tr112placeholders6detail5ph<2>,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat>>,0,1>FRCQ43std3tr16detail383function_imp,Q33std3tr1228tuple,Q53std3tr112placeholders6detail5ph<2>,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat>>,0,1>", &options), Some("std::tr1::detail::function_imp, std::tr1::tuple, std::tr1::placeholders::detail::ph<2>, std::tr1::nat, std::tr1::nat, std::tr1::nat, std::tr1::nat, std::tr1::nat, std::tr1::nat, std::tr1::nat>>, 0, 1>::function_imp(const std::tr1::detail::function_imp, std::tr1::tuple, std::tr1::placeholders::detail::ph<2>, std::tr1::nat, std::tr1::nat, std::tr1::nat, std::tr1::nat, std::tr1::nat, std::tr1::nat, std::tr1::nat>>, 0, 1>&)".to_string()) ); assert_eq!( - demangle("createJointController<11IKJointCtrl>__2MRFP11IKJointCtrlPC9LiveActorUsM11IKJointCtrlFPCvPvPQ29JGeometry64TPosition3>>RC19JointControllerInfo_bM11IKJointCtrlFPCvPvPQ29JGeometry64TPosition3>>RC19JointControllerInfo_b_P15JointController"), + demangle("createJointController<11IKJointCtrl>__2MRFP11IKJointCtrlPC9LiveActorUsM11IKJointCtrlFPCvPvPQ29JGeometry64TPosition3>>RC19JointControllerInfo_bM11IKJointCtrlFPCvPvPQ29JGeometry64TPosition3>>RC19JointControllerInfo_b_P15JointController", &options), Some("JointController* MR::createJointController(IKJointCtrl*, const LiveActor*, unsigned short, bool (IKJointCtrl::*)(JGeometry::TPosition3>>*, const JointControllerInfo&), bool (IKJointCtrl::*)(JGeometry::TPosition3>>*, const JointControllerInfo&))".to_string()) ); assert_eq!( - demangle("execCommand__12JASSeqParserFP8JASTrackM12JASSeqParserFPCvPvP8JASTrackPUl_lUlPUl"), + demangle("execCommand__12JASSeqParserFP8JASTrackM12JASSeqParserFPCvPvP8JASTrackPUl_lUlPUl", &options), Some("JASSeqParser::execCommand(JASTrack*, long (JASSeqParser::*)(JASTrack*, unsigned long*), unsigned long, unsigned long*)".to_string()) ); assert_eq!( - demangle("AddWidgetFnMap__10CGuiWidgetFiM10CGuiWidgetFPCvPvP15CGuiFunctionDefP18CGuiControllerInfo_i"), + demangle("AddWidgetFnMap__10CGuiWidgetFiM10CGuiWidgetFPCvPvP15CGuiFunctionDefP18CGuiControllerInfo_i", &options), Some("CGuiWidget::AddWidgetFnMap(int, int (CGuiWidget::*)(CGuiFunctionDef*, CGuiControllerInfo*))".to_string()) ); assert_eq!( - demangle("BareFn__FPFPCcPv_v_v"), + demangle("BareFn__FPFPCcPv_v_v", &options), Some("void BareFn(void (*)(const char*, void*))".to_string()) ); assert_eq!( - demangle("BareFn__FPFPCcPv_v_PFPCvPv_v"), + demangle("BareFn__FPFPCcPv_v_PFPCvPv_v", &options), Some("void (* BareFn(void (*)(const char*, void*)))(const void*, void*)".to_string()) ); assert_eq!( - demangle("SomeFn__FRCPFPFPCvPv_v_RCPFPCvPv_v"), - Some("SomeFn(void (*const & (*const &)(void (*)(const void*, void*)))(const void*, void*))".to_string()) + demangle("SomeFn__FRCPFPFPCvPv_v_RCPFPCvPv_v", &options), + Some("SomeFn(void (*const& (*const&)(void (*)(const void*, void*)))(const void*, void*))".to_string()) ); assert_eq!( - demangle("SomeFn__Q29Namespace5ClassCFRCMQ29Namespace5ClassFPCvPCvMQ29Namespace5ClassFPCvPCvPCvPv_v_RCMQ29Namespace5ClassFPCvPCvPCvPv_v"), + demangle("SomeFn__Q29Namespace5ClassCFRCMQ29Namespace5ClassFPCvPCvMQ29Namespace5ClassFPCvPCvPCvPv_v_RCMQ29Namespace5ClassFPCvPCvPCvPv_v", &options), Some("Namespace::Class::SomeFn(void (Namespace::Class::*const & (Namespace::Class::*const &)(void (Namespace::Class::*)(const void*, void*) const) const)(const void*, void*) const) const".to_string()) ); assert_eq!( - demangle("__pl__FRC9CRelAngleRC9CRelAngle"), + demangle("__pl__FRC9CRelAngleRC9CRelAngle", &options), Some("operator+(const CRelAngle&, const CRelAngle&)".to_string()) ); assert_eq!( - demangle("destroy__4rstlFPUiPUi"), + demangle("destroy__4rstlFPUiPUi", &options), Some("rstl::destroy(unsigned int*, unsigned int*)".to_string()) ); assert_eq!( - demangle("__opb__33TFunctor2CFv"), + demangle("__opb__33TFunctor2CFv", &options), Some( - "TFunctor2::operator bool() const".to_string() + "TFunctor2::operator bool() const".to_string() ) ); assert_eq!( - demangle("__opRC25TToken<15CCharLayoutInfo>__31TLockedToken<15CCharLayoutInfo>CFv"), + demangle( + "__opRC25TToken<15CCharLayoutInfo>__31TLockedToken<15CCharLayoutInfo>CFv", + &options + ), Some( "TLockedToken::operator const TToken&() const" .to_string() ) ); assert_eq!( - demangle("uninitialized_copy,Q24rstl17rmemory_allocator>,PQ224CSpawnSystemKeyframeData24CSpawnSystemKeyframeInfo>__4rstlFQ24rstl198pointer_iterator,Q24rstl17rmemory_allocator>Q24rstl198pointer_iterator,Q24rstl17rmemory_allocator>PQ224CSpawnSystemKeyframeData24CSpawnSystemKeyframeInfo"), + demangle("uninitialized_copy,Q24rstl17rmemory_allocator>,PQ224CSpawnSystemKeyframeData24CSpawnSystemKeyframeInfo>__4rstlFQ24rstl198pointer_iterator,Q24rstl17rmemory_allocator>Q24rstl198pointer_iterator,Q24rstl17rmemory_allocator>PQ224CSpawnSystemKeyframeData24CSpawnSystemKeyframeInfo", &options), Some("rstl::uninitialized_copy, rstl::rmemory_allocator>, CSpawnSystemKeyframeData::CSpawnSystemKeyframeInfo*>(rstl::pointer_iterator, rstl::rmemory_allocator>, rstl::pointer_iterator, rstl::rmemory_allocator>, CSpawnSystemKeyframeData::CSpawnSystemKeyframeInfo*)".to_string()) ); assert_eq!( - demangle("__rf__Q34rstl120list,Q24rstl17rmemory_allocator>14const_iteratorCFv"), + demangle("__rf__Q34rstl120list,Q24rstl17rmemory_allocator>14const_iteratorCFv", &options), Some("rstl::list, rstl::rmemory_allocator>::const_iterator::operator->() const".to_string()) ); assert_eq!( - demangle("ApplyRipples__FRC14CRippleManagerRA43_A43_Q220CFluidPlaneCPURender13SHFieldSampleRA22_A22_UcRA256_CfRQ220CFluidPlaneCPURender10SPatchInfo"), + demangle("ApplyRipples__FRC14CRippleManagerRA43_A43_Q220CFluidPlaneCPURender13SHFieldSampleRA22_A22_UcRA256_CfRQ220CFluidPlaneCPURender10SPatchInfo", &options), Some("ApplyRipples(const CRippleManager&, CFluidPlaneCPURender::SHFieldSample(&)[43][43], unsigned char(&)[22][22], const float(&)[256], CFluidPlaneCPURender::SPatchInfo&)".to_string()) ); assert_eq!( - demangle("CalculateFluidTextureOffset__14CFluidUVMotionCFfPA2_f"), + demangle("CalculateFluidTextureOffset__14CFluidUVMotionCFfPA2_f", &options), Some( "CFluidUVMotion::CalculateFluidTextureOffset(float, float(*)[2]) const".to_string() ) ); assert_eq!( - demangle("RenderNormals__FRA43_A43_CQ220CFluidPlaneCPURender13SHFieldSampleRA22_A22_CUcRCQ220CFluidPlaneCPURender10SPatchInfo"), + demangle("RenderNormals__FRA43_A43_CQ220CFluidPlaneCPURender13SHFieldSampleRA22_A22_CUcRCQ220CFluidPlaneCPURender10SPatchInfo", &options), Some("RenderNormals(const CFluidPlaneCPURender::SHFieldSample(&)[43][43], const unsigned char(&)[22][22], const CFluidPlaneCPURender::SPatchInfo&)".to_string()) ); assert_eq!( - demangle("Matrix__FfPA2_A3_f"), + demangle("Matrix__FfPA2_A3_f", &options), Some("Matrix(float, float(*)[2][3])".to_string()) ); assert_eq!( - demangle("__ct<12CStringTable>__31CObjOwnerDerivedFromIObjUntypedFRCQ24rstl24auto_ptr<12CStringTable>"), + demangle("__ct<12CStringTable>__31CObjOwnerDerivedFromIObjUntypedFRCQ24rstl24auto_ptr<12CStringTable>", &options), Some("CObjOwnerDerivedFromIObjUntyped::CObjOwnerDerivedFromIObjUntyped(const rstl::auto_ptr&)".to_string()) ); assert_eq!( - demangle("__vt__40TObjOwnerDerivedFromIObj<12CStringTable>"), + demangle("__vt__40TObjOwnerDerivedFromIObj<12CStringTable>", &options), Some("TObjOwnerDerivedFromIObj::__vtable".to_string()) ); assert_eq!( - demangle("__RTTI__40TObjOwnerDerivedFromIObj<12CStringTable>"), + demangle("__RTTI__40TObjOwnerDerivedFromIObj<12CStringTable>", &options), Some("TObjOwnerDerivedFromIObj::__RTTI".to_string()) ); assert_eq!( - demangle("__init__mNull__Q24rstl66basic_string,Q24rstl17rmemory_allocator>"), + demangle("__init__mNull__Q24rstl66basic_string,Q24rstl17rmemory_allocator>", &options), Some("rstl::basic_string, rstl::rmemory_allocator>::__init__mNull".to_string()) ); assert_eq!( - demangle("__dt__26__partial_array_destructorFv"), + demangle("__dt__26__partial_array_destructorFv", &options), Some("__partial_array_destructor::~__partial_array_destructor()".to_string()) ); assert_eq!( - demangle("__distance,1,Q24rstl52select1st>,Q24rstl21less<13TGameScriptId>,Q24rstl17rmemory_allocator>14const_iterator>__4rstlFQ34rstl195red_black_tree<13TGameScriptId,Q24rstl32pair<13TGameScriptId,9TUniqueId>,1,Q24rstl52select1st>,Q24rstl21less<13TGameScriptId>,Q24rstl17rmemory_allocator>14const_iteratorQ34rstl195red_black_tree<13TGameScriptId,Q24rstl32pair<13TGameScriptId,9TUniqueId>,1,Q24rstl52select1st>,Q24rstl21less<13TGameScriptId>,Q24rstl17rmemory_allocator>14const_iteratorQ24rstl20forward_iterator_tag"), + demangle("__distance,1,Q24rstl52select1st>,Q24rstl21less<13TGameScriptId>,Q24rstl17rmemory_allocator>14const_iterator>__4rstlFQ34rstl195red_black_tree<13TGameScriptId,Q24rstl32pair<13TGameScriptId,9TUniqueId>,1,Q24rstl52select1st>,Q24rstl21less<13TGameScriptId>,Q24rstl17rmemory_allocator>14const_iteratorQ34rstl195red_black_tree<13TGameScriptId,Q24rstl32pair<13TGameScriptId,9TUniqueId>,1,Q24rstl52select1st>,Q24rstl21less<13TGameScriptId>,Q24rstl17rmemory_allocator>14const_iteratorQ24rstl20forward_iterator_tag", &options), Some("rstl::__distance, 1, rstl::select1st>, rstl::less, rstl::rmemory_allocator>::const_iterator>(rstl::red_black_tree, 1, rstl::select1st>, rstl::less, rstl::rmemory_allocator>::const_iterator, rstl::red_black_tree, 1, rstl::select1st>, rstl::less, rstl::rmemory_allocator>::const_iterator, rstl::forward_iterator_tag)".to_string()) ); assert_eq!( - demangle("__ct__Q210Metrowerks683compressed_pair,Q33std131__multimap_do_transform,Q23std53allocator>,0>13value_compare,Q23std53allocator>>4node>,Q210Metrowerks337compressed_pair,PQ33std276__tree_deleter,Q33std131__multimap_do_transform,Q23std53allocator>,0>13value_compare,Q23std53allocator>>4node>>FRQ23std301allocator,Q33std131__multimap_do_transform,Q23std53allocator>,0>13value_compare,Q23std53allocator>>4node>Q210Metrowerks337compressed_pair,PQ33std276__tree_deleter,Q33std131__multimap_do_transform,Q23std53allocator>,0>13value_compare,Q23std53allocator>>4node>"), + demangle("__ct__Q210Metrowerks683compressed_pair,Q33std131__multimap_do_transform,Q23std53allocator>,0>13value_compare,Q23std53allocator>>4node>,Q210Metrowerks337compressed_pair,PQ33std276__tree_deleter,Q33std131__multimap_do_transform,Q23std53allocator>,0>13value_compare,Q23std53allocator>>4node>>FRQ23std301allocator,Q33std131__multimap_do_transform,Q23std53allocator>,0>13value_compare,Q23std53allocator>>4node>Q210Metrowerks337compressed_pair,PQ33std276__tree_deleter,Q33std131__multimap_do_transform,Q23std53allocator>,0>13value_compare,Q23std53allocator>>4node>", &options), Some("Metrowerks::compressed_pair, std::__multimap_do_transform, std::allocator>, 0>::value_compare, std::allocator>>::node>&, Metrowerks::compressed_pair, std::__tree_deleter, std::__multimap_do_transform, std::allocator>, 0>::value_compare, std::allocator>>::node*>>::compressed_pair(std::allocator, std::__multimap_do_transform, std::allocator>, 0>::value_compare, std::allocator>>::node>&, Metrowerks::compressed_pair, std::__tree_deleter, std::__multimap_do_transform, std::allocator>, 0>::value_compare, std::allocator>>::node*>)".to_string()) ); assert_eq!( - demangle("skBadString$localstatic3$GetNameByToken__31TTokenSet<18EScriptObjectState>CF18EScriptObjectState"), + demangle("skBadString$localstatic3$GetNameByToken__31TTokenSet<18EScriptObjectState>CF18EScriptObjectState", &options), Some("TTokenSet::GetNameByToken(EScriptObjectState) const::skBadString".to_string()) ); assert_eq!( - demangle("init$localstatic4$GetNameByToken__31TTokenSet<18EScriptObjectState>CF18EScriptObjectState"), + demangle("init$localstatic4$GetNameByToken__31TTokenSet<18EScriptObjectState>CF18EScriptObjectState", &options), Some("TTokenSet::GetNameByToken(EScriptObjectState) const::localstatic4 guard".to_string()) ); assert_eq!( - demangle("@LOCAL@GetAnmPlayPolicy__Q24nw4r3g3dFQ34nw4r3g3d9AnmPolicy@policyTable"), + demangle( + "@LOCAL@GetAnmPlayPolicy__Q24nw4r3g3dFQ34nw4r3g3d9AnmPolicy@policyTable", + &options + ), Some("nw4r::g3d::GetAnmPlayPolicy(nw4r::g3d::AnmPolicy)::policyTable".to_string()) ); assert_eq!( - demangle("@GUARD@GetAnmPlayPolicy__Q24nw4r3g3dFQ34nw4r3g3d9AnmPolicy@policyTable"), + demangle( + "@GUARD@GetAnmPlayPolicy__Q24nw4r3g3dFQ34nw4r3g3d9AnmPolicy@policyTable", + &options + ), Some( "nw4r::g3d::GetAnmPlayPolicy(nw4r::g3d::AnmPolicy)::policyTable guard".to_string() ) ); // Truncated symbol assert_eq!( - demangle("lower_bound,Q24rstl77vector,Q24rstl17rmemory_allocator>,Q24rstl17rmemory_allocator>,Ui,Q24rstl79pair_sorter_finder,Q24rstl8less>>__4rstlFQ24rstl180const_pointer_iterator,Q24rstl77vector,Q24rstl17rmemory_allocator>,Q24rstl17rmemory_allocator>Q24rstl180const_p"), + demangle("lower_bound,Q24rstl77vector,Q24rstl17rmemory_allocator>,Q24rstl17rmemory_allocator>,Ui,Q24rstl79pair_sorter_finder,Q24rstl8less>>__4rstlFQ24rstl180const_pointer_iterator,Q24rstl77vector,Q24rstl17rmemory_allocator>,Q24rstl17rmemory_allocator>Q24rstl180const_p", &options), None ); + assert_eq!( + demangle("test__FRCPCPCi", &options), + Some("test(const int* const* const&)".to_string()), + ); + } + + #[test] + fn test_demangle_options() { + let options = DemangleOptions { omit_empty_parameters: true }; + assert_eq!( + demangle("__dt__26__partial_array_destructorFv", &options), + Some("__partial_array_destructor::~__partial_array_destructor()".to_string()) + ); + let options = DemangleOptions { omit_empty_parameters: false }; + assert_eq!( + demangle("__dt__26__partial_array_destructorFv", &options), + Some("__partial_array_destructor::~__partial_array_destructor(void)".to_string()) + ); } } diff --git a/src/main.rs b/src/main.rs index d9cd8f0..0bfe080 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use argh::FromArgs; -use cwdemangle::demangle; +use cwdemangle::{demangle, DemangleOptions}; use crate::argh_cargo::from_env; @@ -11,11 +11,16 @@ struct Args { /// the symbol to demangle #[argh(positional)] symbol: String, + /// disable replacing `(void)` with `()` + #[argh(switch)] + keep_void: bool, } fn main() -> Result<(), &'static str> { let args: Args = from_env(); - return if let Some(symbol) = demangle(args.symbol.as_str()) { + return if let Some(symbol) = + demangle(args.symbol.as_str(), &DemangleOptions { omit_empty_parameters: !args.keep_void }) + { println!("{}", symbol); Ok(()) } else {