diff --git a/plugins/dwarf/dwarf_export/src/lib.rs b/plugins/dwarf/dwarf_export/src/lib.rs index e46433458..4bc0ab94b 100644 --- a/plugins/dwarf/dwarf_export/src/lib.rs +++ b/plugins/dwarf/dwarf_export/src/lib.rs @@ -131,7 +131,7 @@ fn export_type( AttributeValue::Data2(t.width() as u16), ); - for struct_member in t.get_structure().unwrap().members().unwrap() { + for struct_member in t.get_structure().unwrap().members() { let struct_member_die_uid = dwarf.unit.add(structure_die_uid, constants::DW_TAG_member); dwarf.unit.get_mut(struct_member_die_uid).set( @@ -378,8 +378,8 @@ fn export_types( ) { for t in &bv.types() { export_type( - t.name().to_string(), - &t.type_object(), + t.name.to_string(), + &t.ty, bv, defined_types, dwarf, diff --git a/plugins/dwarf/dwarf_import/src/die_handlers.rs b/plugins/dwarf/dwarf_import/src/die_handlers.rs index d04aa0cbb..ea551bd5a 100644 --- a/plugins/dwarf/dwarf_import/src/die_handlers.rs +++ b/plugins/dwarf/dwarf_import/src/die_handlers.rs @@ -142,7 +142,7 @@ pub(crate) fn handle_enum( pub(crate) fn handle_typedef( debug_info_builder: &mut DebugInfoBuilder, entry_type: Option, - typedef_name: &String, + typedef_name: &str, ) -> (Option>, bool) { // All base types have: // DW_AT_name @@ -152,7 +152,7 @@ pub(crate) fn handle_typedef( // This will fail in the case where we have a typedef to a type that doesn't exist (failed to parse, incomplete, etc) if let Some(entry_type_offset) = entry_type { if let Some(t) = debug_info_builder.get_type(entry_type_offset) { - return (Some(t.get_type()), typedef_name != t.get_name()); + return (Some(t.get_type()), typedef_name != t.name); } } @@ -304,13 +304,14 @@ pub(crate) fn handle_function( // Alias function type in the case that it contains itself if let Some(name) = debug_info_builder_context.get_name(dwarf, unit, entry) { + let ntr = Type::named_type_from_type( + &name, + &Type::function(return_type.as_ref(), &[], false), + ); debug_info_builder.add_type( get_uid(dwarf, unit, entry), - &name, - Type::named_type_from_type( - &name, - &Type::function::<&binaryninja::types::Type>(return_type.as_ref(), &[], false), - ), + name, + ntr, false, ); } diff --git a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs index 0cc93d710..0f66f5cbc 100644 --- a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs +++ b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs @@ -100,16 +100,12 @@ impl FunctionInfoBuilder { // TODO : Don't make this pub...fix the value thing pub(crate) struct DebugType { - name: String, - ty: Ref, - commit: bool, + pub name: String, + pub ty: Ref, + pub commit: bool, } impl DebugType { - pub fn get_name(&self) -> &String { - &self.name - } - pub fn get_type(&self) -> Ref { self.ty.clone() } @@ -327,7 +323,7 @@ impl DebugInfoBuilder { self.types.values() } - pub(crate) fn add_type(&mut self, type_uid: TypeUID, name: &String, t: Ref, commit: bool) { + pub(crate) fn add_type(&mut self, type_uid: TypeUID, name: String, t: Ref, commit: bool) { if let Some(DebugType { name: existing_name, ty: existing_type, @@ -396,7 +392,7 @@ impl DebugInfoBuilder { // Either get the known type or use a 0 confidence void type so we at least get the name applied let ty = match type_uid { - Some(uid) => Conf::new(self.get_type(uid).unwrap().get_type(), 128), + Some(uid) => Conf::new(self.get_type(uid).unwrap().ty.clone(), 128), None => Conf::new(Type::void(), 0) }; let function = &mut self.functions[function_index]; @@ -465,8 +461,8 @@ impl DebugInfoBuilder { if let Some((_existing_name, existing_type_uid)) = self.data_variables.insert(address, (name, type_uid)) { - let existing_type = self.get_type(existing_type_uid).unwrap().get_type(); - let new_type = self.get_type(type_uid).unwrap().get_type(); + let existing_type = self.get_type(existing_type_uid).unwrap().ty.as_ref(); + let new_type = self.get_type(type_uid).unwrap().ty.as_ref(); if existing_type_uid != type_uid || existing_type != new_type { warn!("DWARF info contains duplicate data variable definition. Overwriting data variable at 0x{:08x} (`{}`) with `{}`", @@ -549,7 +545,7 @@ impl DebugInfoBuilder { fn get_function_type(&self, function: &FunctionInfoBuilder) -> Ref { let return_type = match function.return_type { - Some(return_type_id) => Conf::new(self.get_type(return_type_id).unwrap().get_type(), 128), + Some(return_type_id) => Conf::new(self.get_type(return_type_id).unwrap().ty.clone(), 128), _ => Conf::new(Type::void(), 0), }; @@ -559,7 +555,7 @@ impl DebugInfoBuilder { .filter_map(|parameter| match parameter { Some((name, 0)) => Some(FunctionParameter::new(Type::void(), name.clone(), None)), Some((name, uid)) => Some(FunctionParameter::new( - self.get_type(*uid).unwrap().get_type(), + self.get_type(*uid).unwrap().ty.clone(), name.clone(), None, )), @@ -610,8 +606,8 @@ impl DebugInfoBuilder { let symbol_full_name = symbol.full_name(); // If our name has fewer namespaces than the existing name, assume we lost the namespace info - if simplify_str_to_fqn(func_full_name, true).len() - < simplify_str_to_fqn(symbol_full_name.clone(), true).len() + if simplify_str_to_fqn(func_full_name, true).items.len() + < simplify_str_to_fqn(symbol_full_name.clone(), true).items.len() { func.full_name = Some(symbol_full_name.to_string()); } diff --git a/plugins/dwarf/dwarf_import/src/helpers.rs b/plugins/dwarf/dwarf_import/src/helpers.rs index 6109a13d5..d8c2a4c7c 100644 --- a/plugins/dwarf/dwarf_import/src/helpers.rs +++ b/plugins/dwarf/dwarf_import/src/helpers.rs @@ -408,7 +408,7 @@ pub(crate) fn get_build_id(view: &BinaryView) -> Result { } -pub(crate) fn download_debug_info(build_id: &String, view: &BinaryView) -> Result, String> { +pub(crate) fn download_debug_info(build_id: &str, view: &BinaryView) -> Result, String> { let settings = Settings::new(""); let debug_server_urls = settings.get_string_list("network.debuginfodServers", Some(view), None); @@ -479,7 +479,7 @@ pub(crate) fn download_debug_info(build_id: &String, view: &BinaryView) -> Resul } -pub(crate) fn find_local_debug_file_for_build_id(build_id: &String, view: &BinaryView) -> Option { +pub(crate) fn find_local_debug_file_for_build_id(build_id: &str, view: &BinaryView) -> Option { let settings = Settings::new(""); let debug_dirs_enabled = settings.get_bool("analysis.debugInfo.enableDebugDirectories", Some(view), None); @@ -522,7 +522,7 @@ pub(crate) fn find_local_debug_file_for_build_id(build_id: &String, view: &Binar } -pub(crate) fn load_debug_info_for_build_id(build_id: &String, view: &BinaryView) -> (Option>, bool) { +pub(crate) fn load_debug_info_for_build_id(build_id: &str, view: &BinaryView) -> (Option>, bool) { if let Some(debug_file_path) = find_local_debug_file_for_build_id(build_id, view) { return ( diff --git a/plugins/dwarf/dwarf_import/src/types.rs b/plugins/dwarf/dwarf_import/src/types.rs index 4914b501c..700df3da6 100644 --- a/plugins/dwarf/dwarf_import/src/types.rs +++ b/plugins/dwarf/dwarf_import/src/types.rs @@ -141,13 +141,14 @@ fn do_structure_parse( // This reference type will be used by any children to grab while we're still building this type // it will also be how any other types refer to this struct if let Some(full_name) = &full_name { + let ntr = Type::named_type_from_type( + full_name, + &Type::structure(&structure_builder.finalize()), + ); debug_info_builder.add_type( get_uid(dwarf, unit, entry), - &full_name, - Type::named_type_from_type( - full_name.clone(), - &Type::structure(&structure_builder.finalize()), - ), + full_name.to_owned(), + ntr, false, ); } else { @@ -155,10 +156,14 @@ fn do_structure_parse( // These get overwritten in the last step with the actual type, however, so this // is either perfectly fine or breaking a bunch of NTRs let full_name = format!("anonymous_structure_{:x}", get_uid(dwarf, unit, entry)); + let ntr = Type::named_type_from_type( + &full_name, + &Type::structure(&structure_builder.finalize()), + ); debug_info_builder.add_type( get_uid(dwarf, unit, entry), - &full_name, - Type::named_type_from_type(&full_name, &Type::structure(&structure_builder.finalize())), + full_name, + ntr, false, ); } @@ -224,14 +229,14 @@ fn do_structure_parse( if let Some(full_name) = full_name { debug_info_builder.add_type( get_uid(dwarf, unit, entry) + 1, // TODO : This is super broke (uid + 1 is not guaranteed to be unique) - &full_name, + full_name, finalized_structure, true, ); } else { debug_info_builder.add_type( get_uid(dwarf, unit, entry), - &format!("{}", finalized_structure), + finalized_structure.to_string(), finalized_structure, false, // Don't commit anonymous unions (because I think it'll break things) ); @@ -451,10 +456,10 @@ pub(crate) fn get_type( } .unwrap_or_else(|| { commit = false; - format!("{}", type_def) + type_def.to_string() }); - debug_info_builder.add_type(entry_uid, &name, type_def, commit); + debug_info_builder.add_type(entry_uid, name, type_def, commit); Some(entry_uid) } else { None diff --git a/plugins/idb_import/src/types.rs b/plugins/idb_import/src/types.rs index 4d12aa638..547462146 100644 --- a/plugins/idb_import/src/types.rs +++ b/plugins/idb_import/src/types.rs @@ -162,7 +162,7 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { } TranslateTypeResult::PartiallyTranslated(og_ty, error) => { TranslateTypeResult::PartiallyTranslated( - Type::named_type_from_type(&String::from_utf8_lossy(&ty.name), og_ty), + Type::named_type_from_type(String::from_utf8_lossy(&ty.name), og_ty), error .as_ref() .map(|x| BnTypeError::Typedef(Box::new(x.clone()))) @@ -170,7 +170,7 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { ) } TranslateTypeResult::Translated(og_ty) => TranslateTypeResult::Translated( - Type::named_type_from_type(&String::from_utf8_lossy(&ty.name), og_ty), + Type::named_type_from_type(String::from_utf8_lossy(&ty.name), og_ty), ), } } diff --git a/plugins/pdb-ng/src/parser.rs b/plugins/pdb-ng/src/parser.rs index a47836063..931e731e4 100644 --- a/plugins/pdb-ng/src/parser.rs +++ b/plugins/pdb-ng/src/parser.rs @@ -29,10 +29,7 @@ use binaryninja::debuginfo::{DebugFunctionInfo, DebugInfo}; use binaryninja::platform::Platform; use binaryninja::rc::Ref; use binaryninja::settings::Settings; -use binaryninja::types::{ - EnumerationBuilder, NamedTypeReference, - NamedTypeReferenceClass, StructureBuilder, StructureType, Type, TypeClass, -}; +use binaryninja::types::{EnumerationBuilder, NamedTypeReference, NamedTypeReferenceClass, QualifiedName, StructureBuilder, StructureType, Type, TypeClass}; use binaryninja::variable::NamedDataVariableWithType; use crate::symbol_parser::{ParsedDataSymbol, ParsedProcedure, ParsedSymbol}; use crate::type_parser::ParsedType; @@ -67,13 +64,13 @@ pub struct PDBParserInstance<'a, S: Source<'a> + 'a> { /// TypeIndex -> ParsedType enum used during parsing pub(crate) indexed_types: BTreeMap, /// QName -> Binja Type for finished types - pub(crate) named_types: BTreeMap>, + pub(crate) named_types: BTreeMap>, /// Raw (mangled) name -> TypeIndex for resolving forward references pub(crate) full_type_indices: BTreeMap, /// Stack of types we're currently parsing pub(crate) type_stack: Vec, /// Stack of parent types we're parsing nested types inside of - pub(crate) namespace_stack: Vec, + pub(crate) namespace_stack: QualifiedName, /// Type Index -> Does it return on the stack pub(crate) type_default_returnable: BTreeMap, @@ -287,9 +284,9 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { fn collect_name( &self, name: &NamedTypeReference, - unknown_names: &mut HashMap, + unknown_names: &mut HashMap, ) { - let used_name = name.name().to_string(); + let used_name = name.name(); if let Some(&found) = unknown_names.get(&used_name) { @@ -312,20 +309,16 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { fn collect_names( &self, ty: &Type, - unknown_names: &mut HashMap, + unknown_names: &mut HashMap, ) { match ty.type_class() { TypeClass::StructureTypeClass => { if let Some(structure) = ty.get_structure() { - if let Some(members) = structure.members() { - for member in members { - self.collect_names(member.ty.contents.as_ref(), unknown_names); - } + for member in structure.members() { + self.collect_names(member.ty.contents.as_ref(), unknown_names); } - if let Some(bases) = structure.base_structures() { - for base in bases { - self.collect_name(base.ty.as_ref(), unknown_names); - } + for base in structure.base_structures() { + self.collect_name(base.ty.as_ref(), unknown_names); } } } @@ -368,7 +361,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { .bv .types() .iter() - .map(|qnat| qnat.name().string()) + .map(|qnat| qnat.name) .collect::>(); for ty in &self.named_types { @@ -409,7 +402,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { match class { NamedTypeReferenceClass::UnknownNamedTypeClass | NamedTypeReferenceClass::TypedefNamedTypeClass => { - self.debug_info.add_type(name, Type::void().as_ref(), &[]); // TODO : Components + self.debug_info.add_type(&name, Type::void().as_ref(), &[]); // TODO : Components } NamedTypeReferenceClass::ClassNamedTypeClass | NamedTypeReferenceClass::StructNamedTypeClass @@ -431,7 +424,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { structure.set_alignment(1); self.debug_info.add_type( - name, + &name, Type::structure(structure.finalize().as_ref()).as_ref(), &[], // TODO : Components ); @@ -439,7 +432,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { NamedTypeReferenceClass::EnumNamedTypeClass => { let enumeration = EnumerationBuilder::new(); self.debug_info.add_type( - name, + &name, Type::enumeration( enumeration.finalize().as_ref(), self.arch.default_integer_size().try_into()?, diff --git a/plugins/pdb-ng/src/symbol_parser.rs b/plugins/pdb-ng/src/symbol_parser.rs index aff333a16..4dee8560d 100644 --- a/plugins/pdb-ng/src/symbol_parser.rs +++ b/plugins/pdb-ng/src/symbol_parser.rs @@ -260,7 +260,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) }); if old_exists { - self.log(|| format!("Clobbering old definition")); + self.log(|| "Clobbering old definition"); } best_symbols.insert(raw_name.clone(), sym); } @@ -318,7 +318,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) }); if old_exists { - self.log(|| format!("Clobbering old definition")); + self.log(|| "Clobbering old definition"); } best_functions.insert(raw_name.clone(), sym); } @@ -444,10 +444,10 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { match p { Ok(SymbolData::ScopeEnd) | Ok(SymbolData::InlineSiteEnd) if popped => {} Ok(SymbolData::ScopeEnd) | Ok(SymbolData::InlineSiteEnd) if !popped => { - self.log(|| format!("Did not pop at a scope end??? WTF??")); + self.log(|| "Did not pop at a scope end??? WTF??"); } _ if popped => { - self.log(|| format!("Popped but not at a scope end??? WTF??")); + self.log(|| "Popped but not at a scope end??? WTF??"); } _ => {} } @@ -714,7 +714,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } fn handle_scope_end_symbol(&mut self, _index: SymbolIndex) -> Result> { - self.log(|| format!("Got ScopeEnd symbol")); + self.log(|| "Got ScopeEnd symbol"); Ok(None) } @@ -794,7 +794,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let rva = data.offset.to_rva(&self.address_map).unwrap_or_default(); let raw_name = data.name.to_string().to_string(); let (t, name) = self.demangle_to_type(&raw_name, rva)?; - let name = name.map(|n| n.string()); + let name = name.map(|n| n.to_string()); // Sometimes the demangler REALLY knows what type this is supposed to be, and the // data symbol is actually wrong. So in those cases, let the demangler take precedence @@ -848,7 +848,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let rva = data.offset.to_rva(&self.address_map).unwrap_or_default(); let raw_name = data.name.to_string().to_string(); let (t, name) = self.demangle_to_type(&raw_name, rva)?; - let name = name.map(|n| n.string()); + let name = name.map(|n| n.to_string()); let name = SymbolNames { raw_name, @@ -1112,7 +1112,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // Generally proc symbols have real types, but use the demangler just in case the microsoft // public pdbs have the function type as `void` let (t, name) = self.demangle_to_type(&raw_name, rva)?; - let mut name = name.map(|n| n.string()); + let mut name = name.map(|n| n.to_string()); // Some proc symbols don't have a mangled name, so try and look up their name if name.is_none() || name.as_ref().expect("just failed none") == &raw_name { @@ -1384,12 +1384,12 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { &mut self, _index: SymbolIndex, ) -> Result> { - self.log(|| format!("Got InlineSiteEnd symbol")); + self.log(|| "Got InlineSiteEnd symbol"); Ok(None) } fn handle_procedure_end_symbol(&mut self, _index: SymbolIndex) -> Result> { - self.log(|| format!("Got ProcedureEnd symbol")); + self.log(|| "Got ProcedureEnd symbol"); Ok(None) } @@ -1476,7 +1476,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let address = self.bv.start() + rva.0 as u64; let (t, name) = self.demangle_to_type(&raw_name, rva)?; - let name = name.map(|n| n.string()); + let name = name.map(|n| n.to_string()); let mut fn_type = t; // These have the same name as their target, so look that up @@ -1786,9 +1786,9 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { rva: Rva, ) -> Result<(Option>>, Option)> { let (mut t, mut name) = match demangle_ms(&self.arch, raw_name, true) { - Ok((Some(t), name)) => (Some(Conf::new(t, DEMANGLE_CONFIDENCE)), name), - Ok((_, name)) => (None, name), - _ => (None, vec![raw_name.clone()]), + Some((name, Some(t))) => (Some(Conf::new(t, DEMANGLE_CONFIDENCE)), name), + Some((name, _)) => (None, name), + _ => (None, QualifiedName::new(vec![raw_name.clone()])), }; if let Some(ty) = t.as_ref() { @@ -1864,7 +1864,8 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { for (search_name, search_types) in name_to_type.iter() { if last_name.contains(search_name) { for search_type in search_types { - if let Some(ty) = self.named_types.get(search_type) { + let qualified_search_type = QualifiedName::from(search_type); + if let Some(ty) = self.named_types.get(&qualified_search_type) { // Fallback in case we don't find a specific one t = Some(Conf::new( Type::named_type_from_type(search_type, ty.as_ref()), @@ -1880,8 +1881,8 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { self.make_lengthy_type(ty, self.bv.start() + rva.0 as u64)? { // See if we have a type with this length - let lengthy_name = - format!("${}$_extraBytes_{}", search_type, length); + let lengthy_name: QualifiedName = + format!("${}$_extraBytes_{}", search_type, length).into(); if let Some(ty) = self.named_types.get(&lengthy_name) { // Wow! @@ -1901,9 +1902,9 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } // VTables have types on their data symbols, - if let Some((class_name, last)) = name.join("::").rsplit_once("::") { + if let Some((last, class_name)) = name.split_last() { if last.contains("`vftable'") { - let mut vt_name = class_name.to_string() + "::" + "VTable"; + let mut vt_name = class_name.with_item("VTable"); if last.contains("{for") { // DerivedClass::`vftable'{for `BaseClass'} let mut base_name = last.to_owned(); @@ -1911,7 +1912,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { base_name.drain((base_name.len() - "'}".len())..(base_name.len())); // Multiply inherited classes have multiple vtable types // TODO: Do that - vt_name = base_name + "::" + "VTable"; + vt_name = QualifiedName::new(vec![base_name, "VTable".to_string()]); } vt_name = vt_name @@ -1921,7 +1922,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { if let Some(ty) = self.named_types.get(&vt_name) { t = Some(Conf::new( - Type::named_type_from_type(&vt_name, ty.as_ref()), + Type::named_type_from_type(vt_name, ty.as_ref()), DEMANGLE_CONFIDENCE, )); } else { @@ -1933,13 +1934,13 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { if let Some(ty) = self.named_types.get(&vt_name) { t = Some(Conf::new( - Type::named_type_from_type(&vt_name, ty.as_ref()), + Type::named_type_from_type(vt_name, ty.as_ref()), DEMANGLE_CONFIDENCE, )); } else { t = Some(Conf::new( Type::named_type_from_type( - &vt_name, + vt_name, Type::structure(StructureBuilder::new().finalize().as_ref()) .as_ref(), ), @@ -1982,9 +1983,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let structure = base_type .get_structure() .ok_or(anyhow!("Expected structure"))?; - let mut members = structure - .members() - .ok_or(anyhow!("Expected structure to have members"))?; + let mut members = structure.members(); let last_member = members .last_mut() .ok_or(anyhow!("Not enough members"))?; diff --git a/plugins/pdb-ng/src/type_parser.rs b/plugins/pdb-ng/src/type_parser.rs index 195736949..9996bbcad 100644 --- a/plugins/pdb-ng/src/type_parser.rs +++ b/plugins/pdb-ng/src/type_parser.rs @@ -136,7 +136,7 @@ pub struct ParsedMemberFunction { #[derive(Debug, Clone)] pub struct VirtualBaseClass { /// Base class name - pub base_name: String, + pub base_name: QualifiedName, /// Base class type pub base_type: Ref, /// Offset in this class where the base's fields are located @@ -153,7 +153,7 @@ pub enum ParsedType { /// No info other than type data Bare(Ref), /// Named fully parsed class/enum/union/etc type - Named(String, Ref), + Named(QualifiedName, Ref), /// Function procedure Procedure(ParsedProcedureType), /// Bitfield entries @@ -163,7 +163,7 @@ pub enum ParsedType { /// One member in a structure/union Member(ParsedMember), /// Base class name and offset details - BaseClass(String, StructureMember), + BaseClass(QualifiedName, StructureMember), /// One member in an enumeration Enumerate(EnumerationMember), /// List of arguments to a function @@ -260,7 +260,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) -> Result<()> { // Hack: This is needed for primitive types but it's not defined in the pdb itself self.named_types - .insert("HRESULT".to_string(), Type::int(4, true)); + .insert("HRESULT".into(), Type::int(4, true)); let type_information = self.pdb.type_information()?; let mut finder = type_information.finder(); @@ -352,8 +352,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let name = ty .get_named_type_reference() .ok_or(anyhow!("expected ntr"))? - .name() - .to_string(); + .name(); if Self::is_name_anonymous(&name) { continue; } @@ -361,7 +360,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { continue; } // If the bv has this type, DebugInfo will just update us to reference it - if let Some(_) = self.bv.get_type_by_name(&name) { + if let Some(_) = self.bv.get_type_by_name(name.to_owned()) { continue; } @@ -406,8 +405,9 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // Cleanup a couple builtin names for &name in BUILTIN_NAMES { - if self.named_types.contains_key(name) { - self.named_types.remove(name); + let builtin_qualified_name = QualifiedName::from(name); + if self.named_types.contains_key(&builtin_qualified_name) { + self.named_types.remove(&builtin_qualified_name); self.log(|| format!("Remove builtin type {}", name)); } } @@ -423,10 +423,11 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let mut remove_names = vec![]; for (name, _) in &self.named_types { - if uint_regex.is_match(name) { + let name_str = name.to_string(); + if uint_regex.is_match(&name_str) { remove_names.push(name.clone()); } - if float_regex.is_match(name) { + if float_regex.is_match(&name_str) { remove_names.push(name.clone()); } } @@ -446,7 +447,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) -> Result>> { match self.indexed_types.get(index) { Some(ParsedType::Bare(ty)) => Ok(Some(ty.clone())), - Some(ParsedType::Named(name, ty)) => Ok(Some(Type::named_type_from_type(name, &ty))), + Some(ParsedType::Named(name, ty)) => Ok(Some(Type::named_type_from_type(name.clone(), &ty))), Some(ParsedType::Procedure(ParsedProcedureType { method_type, raw_method_type, @@ -749,8 +750,8 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) -> Result>> { self.log(|| format!("Got Class type: {:x?}", data)); - let raw_class_name = &data.name.to_string(); - let class_name = raw_class_name.to_string(); + let raw_class_name = data.name.to_string(); + let class_name = QualifiedName::from(raw_class_name); self.log(|| format!("Named: {}", class_name)); @@ -758,7 +759,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // Try and find it first if let Some(existing) = self.named_types.get(&class_name) { return Ok(Some(Box::new(ParsedType::Bare( - Type::named_type_from_type(&class_name, existing), + Type::named_type_from_type(class_name, existing), )))); } @@ -949,6 +950,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { &format!( "`{}`", self.namespace_stack + .items .last() .ok_or_else(|| anyhow!("Expected class in ns stack"))? ), @@ -982,7 +984,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { _ => NamedTypeReferenceClass::StructNamedTypeClass, }; bases.push(BaseStructure::new( - NamedTypeReference::new(ntr_class, name.into()), + NamedTypeReference::new(ntr_class, name.clone()), base.offset, base.ty.contents.width(), )); @@ -1014,13 +1016,14 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { _ => NamedTypeReferenceClass::StructNamedTypeClass, }; bases.push(BaseStructure::new( - NamedTypeReference::new(ntr_class, base_name.into()), + NamedTypeReference::new(ntr_class, base_name.clone()), *base_offset, base_type.width(), )); warn!( "Class `{}` uses virtual inheritance. Type information may be inaccurate.", self.namespace_stack + .items .last() .ok_or_else(|| anyhow!("Expected class in ns stack"))? ); @@ -1033,6 +1036,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { warn!( "Class `{}` has multiple base classes. Type information may be inaccurate.", self.namespace_stack + .items .last() .ok_or_else(|| anyhow!("Expected class in ns stack"))? ); @@ -1051,13 +1055,8 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { for base_class in &base_classes { match base_class { ParsedType::BaseClass(base_name, _base_type) => { - let mut vt_base_name = base_name - .split("::") - .into_iter() - .map(|s| s.to_string()) - .collect::>(); - vt_base_name.push("VTable".to_string()); - let vt_base_name = vt_base_name.join("::"); + let mut vt_base_name = base_name.clone(); + vt_base_name.items.push("VTable".to_string()); match self.named_types.get(&vt_base_name) { Some(vt_base_type) @@ -1129,18 +1128,18 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let vt_type = Type::structure(vt.finalize().as_ref()); // Need to insert a new named type for the vtable - let mut vt_name = self - .namespace_stack - .last() - .ok_or_else(|| anyhow!("Expected class in ns stack"))? - .clone(); - vt_name += "::VTable"; + if self.namespace_stack.is_empty() { + return Err(anyhow!("Expected class in ns stack")); + } + + let mut vt_name = self.namespace_stack.clone(); + vt_name.items.push("VTable".to_string()); self.named_types.insert(vt_name.clone(), vt_type.clone()); let vt_pointer = Type::pointer( &self.arch, &Conf::new( - Type::named_type_from_type(&QualifiedName::from(vt_name), vt_type.as_ref()), + Type::named_type_from_type(vt_name, vt_type.as_ref()), MAX_CONFIDENCE, ), ); @@ -1369,10 +1368,10 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) -> Result>> { self.log(|| format!("Got Nested type: {:x?}", data)); let mut class_name_ns = self.namespace_stack.clone(); - class_name_ns.push(data.name.to_string().to_string()); + class_name_ns.push(data.name.to_string().into()); let ty = self.type_index_to_bare(data.nested_type, finder, false)?; Ok(Some(Box::new(ParsedType::Named( - class_name_ns.join("::"), + class_name_ns, ty, )))) } @@ -1393,8 +1392,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let name = t .get_named_type_reference() .ok_or(anyhow!("Expected NTR to have NTR"))? - .name() - .to_string(); + .name(); (name, t.clone()) } e => return Err(anyhow!("Unexpected base class type: {:x?}", e)), @@ -1402,7 +1400,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // Try to resolve the full base type let resolved_type = match self.try_type_index_to_bare(data.base_class, finder, true)? { - Some(ty) => Type::named_type_from_type(&member_name, ty.as_ref()), + Some(ty) => Type::named_type_from_type(member_name.clone(), ty.as_ref()), None => t.clone(), }; @@ -1418,7 +1416,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { member_name.clone(), StructureMember::new( Conf::new(resolved_type, MAX_CONFIDENCE), - member_name, + member_name.to_string(), base_offset as u64, access, scope, @@ -1439,8 +1437,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let name = t .get_named_type_reference() .ok_or(anyhow!("Expected NTR to have NTR"))? - .name() - .to_string(); + .name(); (name, t.clone()) } e => return Err(anyhow!("Unexpected base class type: {:x?}", e)), @@ -1627,15 +1624,15 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) -> Result>> { self.log(|| format!("Got Enumeration type: {:x?}", data)); - let raw_enum_name = &data.name.to_string(); - let enum_name = raw_enum_name.to_string(); + let raw_enum_name = data.name.to_string(); + let enum_name = QualifiedName::from(raw_enum_name); self.log(|| format!("Named: {}", enum_name)); if data.properties.forward_reference() { // Try and find it first if let Some(existing) = self.named_types.get(&enum_name) { return Ok(Some(Box::new(ParsedType::Bare( - Type::named_type_from_type(&enum_name, existing), + Type::named_type_from_type(enum_name, existing), )))); } @@ -1755,15 +1752,15 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) -> Result>> { self.log(|| format!("Got Union type: {:x?}", data)); - let raw_union_name = &data.name.to_string(); - let union_name = raw_union_name.to_string(); + let raw_union_name = data.name.to_string(); + let union_name = QualifiedName::from(raw_union_name); self.log(|| format!("Named: {}", union_name)); if data.properties.forward_reference() { // Try and find it first if let Some(existing) = self.named_types.get(&union_name) { return Ok(Some(Box::new(ParsedType::Bare( - Type::named_type_from_type(&union_name, existing), + Type::named_type_from_type(union_name, existing), )))); } @@ -1996,7 +1993,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let (mut type_, inner) = match self.handle_type_index(index, finder)? { Some(ParsedType::Bare(ty)) => (ty.clone(), None), Some(ParsedType::Named(name, ty)) => { - (Type::named_type_from_type(name, &ty), Some(ty.clone())) + (Type::named_type_from_type(name.to_owned(), &ty), Some(ty.clone())) } Some(ParsedType::Procedure(ParsedProcedureType { method_type, .. })) => { (method_type.clone(), Some(method_type.clone())) @@ -2016,10 +2013,9 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let name = type_ .get_named_type_reference() .ok_or(anyhow!("expected ntr"))? - .name() - .to_string(); + .name(); if let Some(full_ntr) = self.named_types.get(&name) { - type_ = Type::named_type_from_type(&name, full_ntr.as_ref()); + type_ = Type::named_type_from_type(name, full_ntr.as_ref()); } } } @@ -2060,8 +2056,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let name = type_ .get_named_type_reference() .ok_or(anyhow!("expected ntr"))? - .name() - .to_string(); + .name(); if Self::is_name_anonymous(&name) { if let Some(inner) = inner.as_ref() { type_ = inner.clone(); @@ -2095,9 +2090,12 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } /// Is this name one of the stupid microsoft unnamed type names - fn is_name_anonymous(name: &String) -> bool { - let name_string = name.split("::").last().unwrap_or("").to_string(); - return name_string == "" || name_string.starts_with(" bool { + match name.items.last() { + Some(item) if item == "" => true, + Some(item) if item.starts_with(" true, + _ => false, + } } /// Find a calling convention in the platform diff --git a/plugins/warp/src/convert.rs b/plugins/warp/src/convert.rs index 370fadcae..f4a5b71df 100644 --- a/plugins/warp/src/convert.rs +++ b/plugins/warp/src/convert.rs @@ -102,17 +102,15 @@ pub fn to_bn_symbol_at_address(view: &BinaryView, symbol: &Symbol, addr: u64) -> let mut symbol_builder = BNSymbol::builder(symbol_type, &symbol.name, addr); // Demangle symbol name (short is with simplifications). if let Some(arch) = view.default_arch() { - if let Ok((_, full_name_list)) = + if let Some((full_name, _)) = binaryninja::demangle::demangle_generic(&arch, raw_name, Some(view), false) { - let full_name = full_name_list.join("::"); - symbol_builder = symbol_builder.full_name(&full_name); + symbol_builder = symbol_builder.full_name(full_name); } - if let Ok((_, short_name_list)) = + if let Some((short_name, _)) = binaryninja::demangle::demangle_generic(&arch, raw_name, Some(view), false) { - let short_name = short_name_list.join("::"); - symbol_builder = symbol_builder.short_name(&short_name); + symbol_builder = symbol_builder.short_name(short_name); } } symbol_builder.create() @@ -160,7 +158,6 @@ pub fn from_bn_type_internal( let mut members = raw_struct .members() - .unwrap() .into_iter() .map(|raw_member| { let bit_offset = bytes_to_bits(raw_member.offset); @@ -185,26 +182,24 @@ pub fn from_bn_type_internal( .collect::>(); // Add base structures as flattened members - if let Some(base_structs) = raw_struct.base_structures() { - let base_to_member_iter = base_structs.iter().map(|base_struct| { - let bit_offset = bytes_to_bits(base_struct.offset); - let mut modifiers = StructureMemberModifiers::empty(); - modifiers.set(StructureMemberModifiers::Flattened, true); - let base_struct_ty = from_bn_type_internal( - view, - visited_refs, - &BNType::named_type(&base_struct.ty), - 255, - ); - StructureMember { - name: base_struct_ty.name.to_owned(), - offset: bit_offset, - ty: base_struct_ty, - modifiers, - } - }); - members.extend(base_to_member_iter); - } + let base_to_member_iter = raw_struct.base_structures().into_iter().map(|base_struct| { + let bit_offset = bytes_to_bits(base_struct.offset); + let mut modifiers = StructureMemberModifiers::empty(); + modifiers.set(StructureMemberModifiers::Flattened, true); + let base_struct_ty = from_bn_type_internal( + view, + visited_refs, + &BNType::named_type(&base_struct.ty), + 255, + ); + StructureMember { + name: base_struct_ty.name.to_owned(), + offset: bit_offset, + ty: base_struct_ty, + modifiers, + } + }); + members.extend(base_to_member_iter); // TODO: Check if union let struct_class = StructureClass::new(members); @@ -603,8 +598,8 @@ mod tests { let converted_types: Vec<_> = bv .types() .iter() - .map(|t| { - let ty = from_bn_type(&bv, &t.type_object(), u8::MAX); + .map(|qualified_name_and_type| { + let ty = from_bn_type(&bv, &qualified_name_and_type.ty, u8::MAX); (TypeGUID::from(&ty), ty) }) .collect(); @@ -628,7 +623,7 @@ mod tests { .types() .iter() .map(|t| { - let ty = from_bn_type(&inital_bv, &t.type_object(), u8::MAX); + let ty = from_bn_type(&inital_bv, &t.ty, u8::MAX); (TypeGUID::from(&ty), ty) }) .collect(); @@ -649,7 +644,7 @@ mod tests { .types() .iter() .map(|t| { - let ty = from_bn_type(&second_bv, &t.type_object(), u8::MAX); + let ty = from_bn_type(&second_bv, &t.ty, u8::MAX); (TypeGUID::from(&ty), ty) }) .collect(); diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs index 128df6af1..831157b28 100644 --- a/rust/src/demangle.rs +++ b/rust/src/demangle.rs @@ -58,7 +58,7 @@ pub fn demangle_generic( } } -pub fn demangle_llvm(mangled_name: S, simplify: bool) -> Option> { +pub fn demangle_llvm(mangled_name: S, simplify: bool) -> Option { let mangled_name_bwn = mangled_name.into_bytes_with_nul(); let mangled_name_ptr = mangled_name_bwn.as_ref(); let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut(); @@ -75,13 +75,13 @@ pub fn demangle_llvm(mangled_name: S, simplify: bool) -> Opt match res { true => { assert!(!out_name.is_null()); - let names = unsafe { ArrayGuard::::new(out_name, out_size, ()) } + let names: Vec<_> = unsafe { ArrayGuard::::new(out_name, out_size, ()) } .iter() .map(str::to_string) .collect(); unsafe { BNFreeDemangledName(&mut out_name, out_size) }; - Some(names) + Some(names.into()) } false => None, } @@ -91,7 +91,7 @@ pub fn demangle_gnu3( arch: &CoreArchitecture, mangled_name: S, simplify: bool, -) -> Option<(Vec, Option>)> { +) -> Option<(QualifiedName, Option>)> { let mangled_name_bwn = mangled_name.into_bytes_with_nul(); let mangled_name_ptr = mangled_name_bwn.as_ref(); let mut out_type: *mut BNType = std::ptr::null_mut(); @@ -111,7 +111,7 @@ pub fn demangle_gnu3( match res { true => { assert!(!out_name.is_null()); - let names = unsafe { ArrayGuard::::new(out_name, out_size, ()) } + let names: Vec<_> = unsafe { ArrayGuard::::new(out_name, out_size, ()) } .iter() .map(str::to_string) .collect(); @@ -122,7 +122,7 @@ pub fn demangle_gnu3( false => Some(unsafe { Type::ref_from_raw(out_type) }), }; - Some((names, out_type)) + Some((names.into(), out_type)) } false => None, } @@ -132,7 +132,7 @@ pub fn demangle_ms( arch: &CoreArchitecture, mangled_name: S, simplify: bool, -) -> Option<(Vec, Option>)> { +) -> Option<(QualifiedName, Option>)> { let mangled_name_bwn = mangled_name.into_bytes_with_nul(); let mangled_name_ptr = mangled_name_bwn.as_ref(); @@ -153,7 +153,7 @@ pub fn demangle_ms( match res { true => { assert!(!out_name.is_null()); - let names = unsafe { ArrayGuard::::new(out_name, out_size, ()) } + let names: Vec<_> = unsafe { ArrayGuard::::new(out_name, out_size, ()) } .iter() .map(str::to_string) .collect(); @@ -164,7 +164,7 @@ pub fn demangle_ms( false => Some(unsafe { Type::ref_from_raw(out_type) }), }; - Some((names, out_type)) + Some((names.into(), out_type)) } false => None, } diff --git a/rust/src/string.rs b/rust/src/string.rs index 25e0eee3b..52458c03d 100644 --- a/rust/src/string.rs +++ b/rust/src/string.rs @@ -80,7 +80,7 @@ impl BnString { /// memory previously managed by the `BnString`. /// /// This is typically used to pass a string back through the core where the core is expected to free. - pub(crate) fn into_raw(self) -> *mut c_char { + pub fn into_raw(self) -> *mut c_char { let res = self.raw; // we're surrendering ownership over the *mut c_char to // the core, so ensure we don't free it diff --git a/rust/src/symbol.rs b/rust/src/symbol.rs index dedbd3e00..c73546f97 100644 --- a/rust/src/symbol.rs +++ b/rust/src/symbol.rs @@ -119,12 +119,12 @@ pub struct SymbolBuilder { } impl SymbolBuilder { - pub fn new(ty: SymbolType, raw_name: &str, addr: u64) -> Self { + pub fn new>(ty: SymbolType, raw_name: T, addr: u64) -> Self { Self { ty, binding: Binding::None, addr, - raw_name: raw_name.to_owned(), + raw_name: raw_name.into(), short_name: None, full_name: None, ordinal: 0, @@ -136,13 +136,13 @@ impl SymbolBuilder { self } - pub fn short_name(mut self, short_name: &str) -> Self { - self.short_name = Some(short_name.to_owned()); + pub fn short_name>(mut self, short_name: T) -> Self { + self.short_name = Some(short_name.into()); self } - pub fn full_name(mut self, full_name: &str) -> Self { - self.full_name = Some(full_name.to_owned()); + pub fn full_name>(mut self, full_name: T) -> Self { + self.full_name = Some(full_name.into()); self } diff --git a/rust/src/types.rs b/rust/src/types.rs index a18a12ee3..f986a5d68 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -29,7 +29,9 @@ use crate::{ use crate::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE}; use crate::string::{raw_to_string, strings_to_string_list}; use crate::variable::{Variable, VariableSourceType}; +use std::borrow::Cow; use std::num::NonZeroUsize; +use std::ops::{Index, IndexMut}; use std::{ collections::HashSet, ffi::CStr, @@ -328,8 +330,8 @@ impl TypeBuilder { } } - pub fn named_type_from_type>(name: T, t: &Type) -> Self { - let mut raw_name = BNQualifiedName::from(name.as_ref()); + pub fn named_type_from_type>(name: T, t: &Type) -> Self { + let mut raw_name = BNQualifiedName::from(name.into()); // TODO: This cant be right... let id = BnString::new(""); @@ -706,8 +708,8 @@ impl Type { } } - pub fn named_type_from_type>(name: T, t: &Type) -> Ref { - let mut raw_name = BNQualifiedName::from(name.as_ref()); + pub fn named_type_from_type>(name: T, t: &Type) -> Ref { + let mut raw_name = BNQualifiedName::from(name.into()); // TODO: No id is present for this call? let id = BnString::new(""); @@ -899,8 +901,8 @@ impl Type { } } - pub fn generate_auto_demangled_type_id>(name: T) -> BnString { - let mut raw_name = BNQualifiedName::from(name.as_ref()); + pub fn generate_auto_demangled_type_id>(name: T) -> BnString { + let mut raw_name = BNQualifiedName::from(name.into()); unsafe { BnString::from_raw(BNGenerateAutoDemangledTypeId(&mut raw_name)) } } } @@ -1587,7 +1589,7 @@ impl Structure { let mut count = 0; let members_raw_ptr: *mut BNStructureMember = BNGetStructureMembers(self.handle, &mut count); - // TODO: Debug assert members_raw_ptr. + debug_assert!(!members_raw_ptr.is_null()); match members_raw_ptr.is_null() { false => { let members_raw = std::slice::from_raw_parts(members_raw_ptr, count); @@ -1600,10 +1602,10 @@ impl Structure { } } - // TODO: Omit `Option` and pass empty vec? pub fn base_structures(&self) -> Vec { let mut count = 0; let bases_raw_ptr = unsafe { BNGetBaseStructuresForStructure(self.handle, &mut count) }; + debug_assert!(!bases_raw_ptr.is_null()); match bases_raw_ptr.is_null() { false => { let bases_raw = unsafe { std::slice::from_raw_parts(bases_raw_ptr, count) }; @@ -1650,6 +1652,7 @@ impl ToOwned for Structure { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct StructureMember { pub ty: Conf>, + // TODO: Shouldnt this be a QualifiedName? The ffi says no... pub name: String, pub offset: u64, pub access: MemberAccess, @@ -1918,7 +1921,7 @@ impl Debug for NamedTypeReference { } // TODO: Document usage, specifically how to make a qualified name and why it exists. -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] pub struct QualifiedName { pub items: Vec, // TODO: Make this Option where default is "::". @@ -1926,9 +1929,87 @@ pub struct QualifiedName { } impl QualifiedName { - pub fn new(items: Vec, seperator: String) -> Self { + pub fn new(items: Vec) -> Self { + Self::new_with_seperator(items, "::".to_string()) + } + + pub fn new_with_seperator(items: Vec, seperator: String) -> Self { Self { items, seperator } } + + pub fn with_item(&self, item: impl Into) -> Self { + let mut items = self.items.clone(); + items.push(item.into()); + Self::new_with_seperator(items, self.seperator.clone()) + } + + pub fn push(&mut self, item: String) { + self.items.push(item); + } + + pub fn pop(&mut self) -> Option { + self.items.pop() + } + + pub fn insert(&mut self, index: usize, item: String) { + if index <= self.items.len() { + self.items.insert(index, item); + } + } + + pub fn split_last(&self) -> Option<(String, QualifiedName)> { + self.items.split_last().map(|(a, b)| { + ( + a.to_owned(), + QualifiedName::new_with_seperator(b.to_vec(), self.seperator.clone()), + ) + }) + } + + /// Replaces all occurrences of a substring with another string in all items of the `QualifiedName` + /// and returns an owned version of the modified `QualifiedName`. + /// + /// # Example + /// + /// ``` + /// use binaryninja::types::QualifiedName; + /// + /// let qualified_name = QualifiedName::new(vec!["my::namespace".to_string(), "mytype".to_string()]); + /// let replaced = qualified_name.replace("my", "your"); + /// assert_eq!(replaced.items, vec!["your::namespace".to_string(), "yourtype".to_string()]); + /// ``` + pub fn replace(&self, from: &str, to: &str) -> Self { + Self { + items: self + .items + .iter() + .map(|item| item.replace(from, to)) + .collect(), + seperator: self.seperator.clone(), + } + } + + /// Returns the last item, or `None` if it is empty. + pub fn last(&self) -> Option<&String> { + self.items.last() + } + + /// Returns a mutable reference to the last item, or `None` if it is empty. + pub fn last_mut(&mut self) -> Option<&mut String> { + self.items.last_mut() + } + + pub fn len(&self) -> usize { + self.items.len() + } + + /// A [`QualifiedName`] is empty if it has no items. + /// + /// If you want to know if the unqualified name is empty (i.e. no characters) + /// you must first convert the qualified name to unqualified via [`QualifiedName::to_string`]. + pub fn is_empty(&self) -> bool { + self.items.is_empty() + } } impl From for QualifiedName { @@ -1984,16 +2065,6 @@ impl From<&QualifiedName> for BNQualifiedName { } } -impl From<&str> for QualifiedName { - fn from(value: &str) -> Self { - Self { - items: vec![value.to_string()], - // TODO: See comment in struct def. - seperator: String::from("::"), - } - } -} - impl From for QualifiedName { fn from(value: String) -> Self { Self { @@ -2004,13 +2075,27 @@ impl From for QualifiedName { } } +impl From<&str> for QualifiedName { + fn from(value: &str) -> Self { + Self::from(value.to_string()) + } +} + +impl From<&String> for QualifiedName { + fn from(value: &String) -> Self { + Self::from(value.to_owned()) + } +} + +impl From> for QualifiedName { + fn from(value: Cow<'_, str>) -> Self { + Self::from(value.to_string()) + } +} + impl From> for QualifiedName { fn from(value: Vec) -> Self { - Self { - items: value, - // TODO: See comment in struct def. - seperator: String::from("::"), - } + Self::new(value) } } @@ -2024,6 +2109,26 @@ impl From> for QualifiedName { } } +impl From for String { + fn from(value: QualifiedName) -> Self { + value.to_string() + } +} + +impl Index for QualifiedName { + type Output = String; + + fn index(&self, index: usize) -> &Self::Output { + &self.items[index] + } +} + +impl IndexMut for QualifiedName { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.items[index] + } +} + impl Display for QualifiedName { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.items.join(&self.seperator)) diff --git a/rust/src/workflow.rs b/rust/src/workflow.rs index a31238e61..236e0cb73 100644 --- a/rust/src/workflow.rs +++ b/rust/src/workflow.rs @@ -126,7 +126,7 @@ impl AnalysisContext { where I: IntoIterator>, { - let blocks: Vec<_> = blocks.into_iter().map(|block| block).collect(); + let blocks: Vec<_> = blocks.into_iter().collect(); let mut blocks_raw: Vec<*mut BNBasicBlock> = blocks.iter().map(|block| block.handle).collect(); unsafe { BNSetBasicBlockList(self.handle.as_ptr(), blocks_raw.as_mut_ptr(), blocks.len()) }