use crate::platform::Platform;
use crate::progress::{NoProgressCallback, ProgressCallback};
use crate::rc::{Array, Ref};
use crate::string::{raw_to_string, BnStrCompatible, BnString};
use crate::type_parser::{TypeParserError, TypeParserResult};
use crate::types::{QualifiedName, QualifiedNameAndType, Type};
use binaryninjacore_sys::*;
use std::collections::HashMap;
use std::ffi::{c_char, c_void};
use std::fmt::{Debug, Formatter};
use std::ptr::NonNull;
pub type TypeContainerType = BNTypeContainerType;
#[repr(transparent)]
pub struct TypeContainer {
pub handle: NonNull<BNTypeContainer>,
}
impl TypeContainer {
pub(crate) unsafe fn from_raw(handle: NonNull<BNTypeContainer>) -> Self {
let cloned_ptr = NonNull::new(BNDuplicateTypeContainer(handle.as_ptr()));
Self {
handle: cloned_ptr.unwrap(),
}
}
pub fn id(&self) -> BnString {
let result = unsafe { BNTypeContainerGetId(self.handle.as_ptr()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
pub fn name(&self) -> BnString {
let result = unsafe { BNTypeContainerGetName(self.handle.as_ptr()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
pub fn container_type(&self) -> TypeContainerType {
unsafe { BNTypeContainerGetType(self.handle.as_ptr()) }
}
pub fn is_mutable(&self) -> bool {
unsafe { BNTypeContainerIsMutable(self.handle.as_ptr()) }
}
pub fn platform(&self) -> Ref<Platform> {
let result = unsafe { BNTypeContainerGetPlatform(self.handle.as_ptr()) };
assert!(!result.is_null());
unsafe { Platform::ref_from_raw(result) }
}
pub fn add_types<I, T>(&self, types: I) -> bool
where
I: IntoIterator<Item = T>,
T: Into<QualifiedNameAndType>,
{
self.add_types_with_progress(types, NoProgressCallback)
}
pub fn add_types_with_progress<I, T, P>(&self, types: I, mut progress: P) -> bool
where
I: IntoIterator<Item = T>,
T: Into<QualifiedNameAndType>,
P: ProgressCallback,
{
let (raw_names, mut raw_types): (Vec<BNQualifiedName>, Vec<_>) = types
.into_iter()
.map(|t| {
let t = t.into();
(
QualifiedName::into_raw(t.name),
unsafe { Ref::into_raw(t.ty) }.handle,
)
})
.unzip();
let mut result_names = std::ptr::null_mut();
let mut result_ids = std::ptr::null_mut();
let mut result_count = 0;
let success = unsafe {
BNTypeContainerAddTypes(
self.handle.as_ptr(),
raw_names.as_ptr(),
raw_types.as_mut_ptr(),
raw_types.len(),
Some(P::cb_progress_callback),
&mut progress as *mut P as *mut c_void,
&mut result_names,
&mut result_ids,
&mut result_count,
)
};
for name in raw_names {
QualifiedName::free_raw(name);
}
for ty in raw_types {
let _ = unsafe { Type::ref_from_raw(ty) };
}
success
}
pub fn rename_type<T: Into<QualifiedName>, S: BnStrCompatible>(
&self,
name: T,
type_id: S,
) -> bool {
let type_id = type_id.into_bytes_with_nul();
let raw_name = QualifiedName::into_raw(name.into());
let success = unsafe {
BNTypeContainerRenameType(
self.handle.as_ptr(),
type_id.as_ref().as_ptr() as *const c_char,
&raw_name,
)
};
QualifiedName::free_raw(raw_name);
success
}
pub fn delete_type<S: BnStrCompatible>(&self, type_id: S) -> bool {
let type_id = type_id.into_bytes_with_nul();
unsafe {
BNTypeContainerDeleteType(
self.handle.as_ptr(),
type_id.as_ref().as_ptr() as *const c_char,
)
}
}
pub fn type_id<T: Into<QualifiedName>>(&self, name: T) -> Option<BnString> {
let mut result = std::ptr::null_mut();
let raw_name = QualifiedName::into_raw(name.into());
let success =
unsafe { BNTypeContainerGetTypeId(self.handle.as_ptr(), &raw_name, &mut result) };
QualifiedName::free_raw(raw_name);
success.then(|| unsafe { BnString::from_raw(result) })
}
pub fn type_name<S: BnStrCompatible>(&self, type_id: S) -> Option<QualifiedName> {
let type_id = type_id.into_bytes_with_nul();
let mut result = BNQualifiedName::default();
let success = unsafe {
BNTypeContainerGetTypeName(
self.handle.as_ptr(),
type_id.as_ref().as_ptr() as *const c_char,
&mut result,
)
};
success.then(|| QualifiedName::from_owned_raw(result))
}
pub fn type_by_id<S: BnStrCompatible>(&self, type_id: S) -> Option<Ref<Type>> {
let type_id = type_id.into_bytes_with_nul();
let mut result = std::ptr::null_mut();
let success = unsafe {
BNTypeContainerGetTypeById(
self.handle.as_ptr(),
type_id.as_ref().as_ptr() as *const c_char,
&mut result,
)
};
success.then(|| unsafe { Type::ref_from_raw(result) })
}
pub fn type_by_name<T: Into<QualifiedName>>(&self, name: T) -> Option<Ref<Type>> {
let mut result = std::ptr::null_mut();
let raw_name = QualifiedName::into_raw(name.into());
let success =
unsafe { BNTypeContainerGetTypeByName(self.handle.as_ptr(), &raw_name, &mut result) };
QualifiedName::free_raw(raw_name);
success.then(|| unsafe { Type::ref_from_raw(result) })
}
pub fn types(&self) -> Option<HashMap<String, (QualifiedName, Ref<Type>)>> {
let mut type_ids = std::ptr::null_mut();
let mut type_names = std::ptr::null_mut();
let mut type_types = std::ptr::null_mut();
let mut type_count = 0;
let success = unsafe {
BNTypeContainerGetTypes(
self.handle.as_ptr(),
&mut type_ids,
&mut type_names,
&mut type_types,
&mut type_count,
)
};
success.then(|| unsafe {
let raw_ids = std::slice::from_raw_parts(type_ids, type_count);
let raw_names = std::slice::from_raw_parts(type_names, type_count);
let raw_types = std::slice::from_raw_parts(type_types, type_count);
let mut map = HashMap::new();
for (idx, raw_id) in raw_ids.iter().enumerate() {
let id = raw_to_string(*raw_id).expect("Valid string");
let name = QualifiedName::from_raw(&raw_names[idx]);
let ty = Type::from_raw(raw_types[idx]).to_owned();
map.insert(id, (name, ty));
}
BNFreeStringList(type_ids, type_count);
BNFreeTypeNameList(type_names, type_count);
BNFreeTypeList(type_types, type_count);
map
})
}
pub fn type_ids(&self) -> Option<Array<BnString>> {
let mut type_ids = std::ptr::null_mut();
let mut type_count = 0;
let success = unsafe {
BNTypeContainerGetTypeIds(self.handle.as_ptr(), &mut type_ids, &mut type_count)
};
success.then(|| unsafe { Array::new(type_ids, type_count, ()) })
}
pub fn type_names(&self) -> Option<Array<QualifiedName>> {
let mut type_ids = std::ptr::null_mut();
let mut type_count = 0;
let success = unsafe {
BNTypeContainerGetTypeNames(self.handle.as_ptr(), &mut type_ids, &mut type_count)
};
success.then(|| unsafe { Array::new(type_ids, type_count, ()) })
}
pub fn type_names_and_ids(&self) -> Option<(Array<BnString>, Array<QualifiedName>)> {
let mut type_ids = std::ptr::null_mut();
let mut type_names = std::ptr::null_mut();
let mut type_count = 0;
let success = unsafe {
BNTypeContainerGetTypeNamesAndIds(
self.handle.as_ptr(),
&mut type_ids,
&mut type_names,
&mut type_count,
)
};
success.then(|| unsafe {
let ids = Array::new(type_ids, type_count, ());
let names = Array::new(type_names, type_count, ());
(ids, names)
})
}
pub fn parse_type_string<S: BnStrCompatible>(
&self,
source: S,
import_dependencies: bool,
) -> Result<QualifiedNameAndType, Array<TypeParserError>> {
let source = source.into_bytes_with_nul();
let mut result = BNQualifiedNameAndType::default();
let mut errors = std::ptr::null_mut();
let mut error_count = 0;
let success = unsafe {
BNTypeContainerParseTypeString(
self.handle.as_ptr(),
source.as_ref().as_ptr() as *const c_char,
import_dependencies,
&mut result,
&mut errors,
&mut error_count,
)
};
if success {
Ok(QualifiedNameAndType::from_owned_raw(result))
} else {
assert!(!errors.is_null());
Err(unsafe { Array::new(errors, error_count, ()) })
}
}
pub fn parse_types_from_source<S, F, O, D, A>(
&self,
source: S,
filename: F,
options: O,
include_directories: D,
auto_type_source: A,
import_dependencies: bool,
) -> Result<TypeParserResult, Array<TypeParserError>>
where
S: BnStrCompatible,
F: BnStrCompatible,
O: IntoIterator,
O::Item: BnStrCompatible,
D: IntoIterator,
D::Item: BnStrCompatible,
A: BnStrCompatible,
{
let source = source.into_bytes_with_nul();
let filename = filename.into_bytes_with_nul();
let options: Vec<_> = options
.into_iter()
.map(|o| o.into_bytes_with_nul())
.collect();
let options_raw: Vec<*const c_char> = options
.iter()
.map(|o| o.as_ref().as_ptr() as *const c_char)
.collect();
let include_directories: Vec<_> = include_directories
.into_iter()
.map(|d| d.into_bytes_with_nul())
.collect();
let include_directories_raw: Vec<*const c_char> = include_directories
.iter()
.map(|d| d.as_ref().as_ptr() as *const c_char)
.collect();
let auto_type_source = auto_type_source.into_bytes_with_nul();
let mut raw_result = BNTypeParserResult::default();
let mut errors = std::ptr::null_mut();
let mut error_count = 0;
let success = unsafe {
BNTypeContainerParseTypesFromSource(
self.handle.as_ptr(),
source.as_ref().as_ptr() as *const c_char,
filename.as_ref().as_ptr() as *const c_char,
options_raw.as_ptr(),
options_raw.len(),
include_directories_raw.as_ptr(),
include_directories_raw.len(),
auto_type_source.as_ref().as_ptr() as *const c_char,
import_dependencies,
&mut raw_result,
&mut errors,
&mut error_count,
)
};
if success {
let result = TypeParserResult::from_raw(&raw_result);
TypeParserResult::free_raw(raw_result);
Ok(result)
} else {
assert!(!errors.is_null());
Err(unsafe { Array::new(errors, error_count, ()) })
}
}
}
impl Debug for TypeContainer {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TypeContainer")
.field("id", &self.id())
.field("name", &self.name())
.field("container_type", &self.container_type())
.field("is_mutable", &self.is_mutable())
.field("type_names", &self.type_names().unwrap().to_vec())
.finish()
}
}
impl Drop for TypeContainer {
fn drop(&mut self) {
unsafe { BNFreeTypeContainer(self.handle.as_ptr()) }
}
}
impl Clone for TypeContainer {
fn clone(&self) -> Self {
unsafe {
let cloned_ptr = NonNull::new(BNDuplicateTypeContainer(self.handle.as_ptr()));
Self {
handle: cloned_ptr.unwrap(),
}
}
}
}