Skip to content

Commit 9131cb5

Browse files
rbranemesare
authored andcommitted
Implement rust TypeContainer
1 parent 70d0429 commit 9131cb5

File tree

1 file changed

+363
-0
lines changed

1 file changed

+363
-0
lines changed

rust/src/typeparser.rs

Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub type TypeParserErrorSeverity = BNTypeParserErrorSeverity;
1313
pub type TypeParserOption = BNTypeParserOption;
1414
pub type TokenEscapingType = BNTokenEscapingType;
1515
pub type TypeDefinitionLineType = BNTypeDefinitionLineType;
16+
pub type TypeContainerType = BNTypeContainerType;
1617

1718
#[repr(transparent)]
1819
pub struct CoreTypeParser {
@@ -279,6 +280,17 @@ unsafe impl CoreArrayProviderInner for CoreTypeParser {
279280
}
280281
}
281282

283+
/// A `TypeContainer` is a generic interface to access various Binary Ninja models
284+
/// that contain types. Types are stored with both a unique id and a unique name.
285+
// ///
286+
// /// The `TypeContainer` class should not generally be instantiated directly. Instances
287+
// /// can be retrieved from the following properties and methods in the API:
288+
// /// * [BinaryView::type_container]
289+
// /// * [BinaryView::auto_type_container]
290+
// /// * [BinaryView::user_type_container]
291+
// /// * [Platform::type_container]
292+
// /// * [TypeLibrary::type_container]
293+
// /// * [DebugInfo::get_type_container]
282294
#[repr(transparent)]
283295
pub struct TypeContainer {
284296
handle: ptr::NonNull<BNTypeContainer>,
@@ -298,6 +310,340 @@ impl TypeContainer {
298310
pub(crate) unsafe fn as_raw(&self) -> &mut BNTypeContainer {
299311
&mut *self.handle.as_ptr()
300312
}
313+
314+
/// Get an id string for the Type Container. This will be unique within a given
315+
/// analysis session, but may not be globally unique.
316+
pub fn id(&self) -> BnString {
317+
let result = unsafe { BNTypeContainerGetId(self.as_raw()) };
318+
assert!(!result.is_null());
319+
unsafe { BnString::from_raw(result) }
320+
}
321+
322+
/// Get a user-friendly name for the Type Container.
323+
pub fn name(&self) -> BnString {
324+
let result = unsafe { BNTypeContainerGetName(self.as_raw()) };
325+
assert!(!result.is_null());
326+
unsafe { BnString::from_raw(result) }
327+
}
328+
329+
/// Get the type of underlying model the Type Container is accessing.
330+
pub fn container_type(&self) -> TypeContainerType {
331+
unsafe { BNTypeContainerGetType(self.as_raw()) }
332+
}
333+
334+
/// Test if the Type Container supports mutable operations (add, rename, delete)
335+
pub fn is_mutable(&self) -> bool {
336+
unsafe { BNTypeContainerIsMutable(self.as_raw()) }
337+
}
338+
339+
/// Get the Platform object associated with this Type Container. All Type Containers
340+
/// have exactly one associated Platform (as opposed to, e.g. Type Libraries).
341+
pub fn platform(&self) -> Ref<Platform> {
342+
let result = unsafe { BNTypeContainerGetPlatform(self.as_raw()) };
343+
assert!(!result.is_null());
344+
unsafe { Platform::ref_from_raw(result) }
345+
}
346+
347+
/// Add or update types to a Type Container. If the Type Container already contains
348+
/// a type with the same name as a type being added, the existing type will be
349+
/// replaced with the definition given to this function, and references will be
350+
/// updated in the source model.
351+
pub fn add_types<I>(&self, types: I) -> bool
352+
where
353+
I: IntoIterator<Item = (QualifiedName, Ref<Type>)>,
354+
{
355+
let (names, types): (Vec<QualifiedName>, Vec<Ref<Type>>) = types.into_iter().unzip();
356+
357+
// SAFETY QualifiedName is transparent with BNQualifiedName
358+
let names_raw: &[BNQualifiedName] = unsafe { mem::transmute(names.as_slice()) };
359+
360+
let mut types_raw: Vec<*mut BNType> = types.iter().map(|t| t.handle).collect();
361+
362+
let mut result_names = ptr::null_mut();
363+
let mut result_ids = ptr::null_mut();
364+
let mut result_count = 0;
365+
unsafe {
366+
BNTypeContainerAddTypes(
367+
self.as_raw(),
368+
names_raw.as_ptr(),
369+
types_raw.as_mut_ptr(),
370+
types.len(),
371+
Some(cb_progress_nop),
372+
ptr::null_mut(),
373+
&mut result_names,
374+
&mut result_ids,
375+
&mut result_count,
376+
)
377+
}
378+
}
379+
380+
pub fn add_types_with_progress<I, F>(&self, types: I, mut progress: F) -> bool
381+
where
382+
I: IntoIterator<Item = (QualifiedName, Ref<Type>)>,
383+
F: FnMut(usize, usize) -> bool,
384+
{
385+
let (names, types): (Vec<QualifiedName>, Vec<Ref<Type>>) = types.into_iter().unzip();
386+
387+
// SAFETY QualifiedName is transparent with BNQualifiedName
388+
let names_raw: &[BNQualifiedName] = unsafe { mem::transmute(names.as_slice()) };
389+
390+
let mut types_raw: Vec<*mut BNType> = types.iter().map(|t| t.handle).collect();
391+
392+
let mut result_names = ptr::null_mut();
393+
let mut result_ids = ptr::null_mut();
394+
let mut result_count = 0;
395+
unsafe {
396+
BNTypeContainerAddTypes(
397+
self.as_raw(),
398+
names_raw.as_ptr(),
399+
types_raw.as_mut_ptr(),
400+
types.len(),
401+
Some(cb_progress::<F>),
402+
&mut progress as *mut F as *mut ffi::c_void,
403+
&mut result_names,
404+
&mut result_ids,
405+
&mut result_count,
406+
)
407+
}
408+
}
409+
410+
/// Rename a type in the Type Container. All references to this type will be updated
411+
/// (by id) to use the new name.
412+
pub fn rename_type<S: BnStrCompatible>(&self, name: &QualifiedName, type_id: S) -> bool {
413+
let type_id = type_id.into_bytes_with_nul();
414+
unsafe {
415+
BNTypeContainerRenameType(
416+
self.as_raw(),
417+
type_id.as_ref().as_ptr() as *const ffi::c_char,
418+
&name.0,
419+
)
420+
}
421+
}
422+
423+
/// Delete a type in the Type Container. Behavior of references to this type is
424+
/// not specified and you may end up with broken references if any still exist.
425+
pub fn delete_type<S: BnStrCompatible>(&self, type_id: S) -> bool {
426+
let type_id = type_id.into_bytes_with_nul();
427+
unsafe {
428+
BNTypeContainerDeleteType(
429+
self.as_raw(),
430+
type_id.as_ref().as_ptr() as *const ffi::c_char,
431+
)
432+
}
433+
}
434+
435+
/// Get the unique id of the type in the Type Container with the given name.
436+
/// If no type with that name exists, returns None.
437+
pub fn type_id(&self, type_name: &QualifiedName) -> Option<BnString> {
438+
let mut result = ptr::null_mut();
439+
let success = unsafe { BNTypeContainerGetTypeId(self.as_raw(), &type_name.0, &mut result) };
440+
success.then(|| unsafe { BnString::from_raw(result) })
441+
}
442+
443+
/// Get the unique name of the type in the Type Container with the given id.
444+
/// If no type with that id exists, returns None.
445+
pub fn type_name<S: BnStrCompatible>(&self, type_id: S) -> Option<QualifiedName> {
446+
let type_id = type_id.into_bytes_with_nul();
447+
let mut result = BNQualifiedName::default();
448+
let success = unsafe {
449+
BNTypeContainerGetTypeName(
450+
self.as_raw(),
451+
type_id.as_ref().as_ptr() as *const ffi::c_char,
452+
&mut result,
453+
)
454+
};
455+
success.then(|| QualifiedName(result))
456+
}
457+
458+
/// Get the definition of the type in the Type Container with the given id.
459+
/// If no type with that id exists, returns None.
460+
pub fn type_by_id<S: BnStrCompatible>(&self, type_id: S) -> Option<Ref<Type>> {
461+
let type_id = type_id.into_bytes_with_nul();
462+
let mut result = ptr::null_mut();
463+
let success = unsafe {
464+
BNTypeContainerGetTypeById(
465+
self.as_raw(),
466+
type_id.as_ref().as_ptr() as *const ffi::c_char,
467+
&mut result,
468+
)
469+
};
470+
success.then(|| unsafe { Type::ref_from_raw(result) })
471+
}
472+
473+
/// Get the definition of the type in the Type Container with the given name.
474+
/// If no type with that name exists, returns None.
475+
pub fn type_by_name(&self, type_name: &QualifiedName) -> Option<Ref<Type>> {
476+
let mut result = ptr::null_mut();
477+
let success =
478+
unsafe { BNTypeContainerGetTypeByName(self.as_raw(), &type_name.0, &mut result) };
479+
success.then(|| unsafe { Type::ref_from_raw(result) })
480+
}
481+
482+
/// Get a mapping of all types in a Type Container.
483+
pub fn types(&self) -> Option<(Array<Type>, Array<QualifiedName>, Array<BnString>)> {
484+
let mut type_ids = ptr::null_mut();
485+
let mut type_names = ptr::null_mut();
486+
let mut type_types = ptr::null_mut();
487+
let mut type_count = 0;
488+
let success = unsafe {
489+
BNTypeContainerGetTypes(
490+
self.as_raw(),
491+
&mut type_ids,
492+
&mut type_names,
493+
&mut type_types,
494+
&mut type_count,
495+
)
496+
};
497+
success.then(|| unsafe {
498+
let ids = Array::new(type_ids, type_count, ());
499+
let names = Array::new(type_names, type_count, ());
500+
let types = Array::new(type_types, type_count, ());
501+
(types, names, ids)
502+
})
503+
}
504+
505+
/// Get all type ids in a Type Container.
506+
pub fn type_ids(&self) -> Option<Array<BnString>> {
507+
let mut type_ids = ptr::null_mut();
508+
let mut type_count = 0;
509+
let success =
510+
unsafe { BNTypeContainerGetTypeIds(self.as_raw(), &mut type_ids, &mut type_count) };
511+
success.then(|| unsafe { Array::new(type_ids, type_count, ()) })
512+
}
513+
514+
/// Get all type names in a Type Container.
515+
pub fn type_names(&self) -> Option<Array<QualifiedName>> {
516+
let mut type_ids = ptr::null_mut();
517+
let mut type_count = 0;
518+
let success =
519+
unsafe { BNTypeContainerGetTypeNames(self.as_raw(), &mut type_ids, &mut type_count) };
520+
success.then(|| unsafe { Array::new(type_ids, type_count, ()) })
521+
}
522+
523+
/// Get a mapping of all type ids and type names in a Type Container.
524+
pub fn type_names_and_ids(&self) -> Option<(Array<BnString>, Array<QualifiedName>)> {
525+
let mut type_ids = ptr::null_mut();
526+
let mut type_names = ptr::null_mut();
527+
let mut type_count = 0;
528+
let success = unsafe {
529+
BNTypeContainerGetTypeNamesAndIds(
530+
self.as_raw(),
531+
&mut type_ids,
532+
&mut type_names,
533+
&mut type_count,
534+
)
535+
};
536+
success.then(|| unsafe {
537+
let ids = Array::new(type_ids, type_count, ());
538+
let names = Array::new(type_names, type_count, ());
539+
(ids, names)
540+
})
541+
}
542+
543+
/// Parse a single type and name from a string containing their definition, with
544+
/// knowledge of the types in the Type Container.
545+
///
546+
/// * `source` - Source code to parse
547+
/// * `import_dependencies` - If Type Library / Type Archive types should be imported during parsing
548+
pub fn parse_type_string<S: BnStrCompatible>(
549+
&self,
550+
source: S,
551+
import_dependencies: bool,
552+
) -> Result<QualifiedNameAndType, Array<TypeParserError>> {
553+
let source = source.into_bytes_with_nul();
554+
let mut result = BNQualifiedNameAndType::default();
555+
let mut errors = ptr::null_mut();
556+
let mut error_count = 0;
557+
let success = unsafe {
558+
BNTypeContainerParseTypeString(
559+
self.as_raw(),
560+
source.as_ref().as_ptr() as *const ffi::c_char,
561+
import_dependencies,
562+
&mut result,
563+
&mut errors,
564+
&mut error_count,
565+
)
566+
};
567+
if success {
568+
Ok(QualifiedNameAndType(result))
569+
} else {
570+
assert!(!errors.is_null());
571+
Err(unsafe { Array::new(errors, error_count, ()) })
572+
}
573+
}
574+
575+
/// Parse an entire block of source into types, variables, and functions, with
576+
/// knowledge of the types in the Type Container.
577+
578+
/// * `source` - Source code to parse
579+
/// * `file_name` - Name of the file containing the source (optional: exists on disk)
580+
/// * `options` - String arguments to pass as options, e.g. command line arguments
581+
/// * `include_dirs` - List of directories to include in the header search path
582+
/// * `auto_type_source` - Source of types if used for automatically generated types
583+
/// * `import_dependencies` - If Type Library / Type Archive types should be imported during parsing
584+
pub fn parse_types_from_source<S, F, O, D, A>(
585+
&self,
586+
source: S,
587+
filename: F,
588+
options: O,
589+
include_directories: D,
590+
auto_type_source: A,
591+
import_dependencies: bool,
592+
) -> Result<TypeParserResult, Array<TypeParserError>>
593+
where
594+
S: BnStrCompatible,
595+
F: BnStrCompatible,
596+
O: IntoIterator,
597+
O::Item: BnStrCompatible,
598+
D: IntoIterator,
599+
D::Item: BnStrCompatible,
600+
A: BnStrCompatible,
601+
{
602+
let source = source.into_bytes_with_nul();
603+
let filename = filename.into_bytes_with_nul();
604+
let options: Vec<_> = options
605+
.into_iter()
606+
.map(|o| o.into_bytes_with_nul())
607+
.collect();
608+
let options_raw: Vec<*const ffi::c_char> = options
609+
.iter()
610+
.map(|o| o.as_ref().as_ptr() as *const ffi::c_char)
611+
.collect();
612+
let include_directories: Vec<_> = include_directories
613+
.into_iter()
614+
.map(|d| d.into_bytes_with_nul())
615+
.collect();
616+
let include_directories_raw: Vec<*const ffi::c_char> = include_directories
617+
.iter()
618+
.map(|d| d.as_ref().as_ptr() as *const ffi::c_char)
619+
.collect();
620+
let auto_type_source = auto_type_source.into_bytes_with_nul();
621+
let mut result = BNTypeParserResult::default();
622+
let mut errors = ptr::null_mut();
623+
let mut error_count = 0;
624+
let success = unsafe {
625+
BNTypeContainerParseTypesFromSource(
626+
self.as_raw(),
627+
source.as_ref().as_ptr() as *const ffi::c_char,
628+
filename.as_ref().as_ptr() as *const ffi::c_char,
629+
options_raw.as_ptr(),
630+
options_raw.len(),
631+
include_directories_raw.as_ptr(),
632+
include_directories_raw.len(),
633+
auto_type_source.as_ref().as_ptr() as *const ffi::c_char,
634+
import_dependencies,
635+
&mut result,
636+
&mut errors,
637+
&mut error_count,
638+
)
639+
};
640+
if success {
641+
Ok(unsafe { TypeParserResult::from_raw(result) })
642+
} else {
643+
assert!(!errors.is_null());
644+
Err(unsafe { Array::new(errors, error_count, ()) })
645+
}
646+
}
301647
}
302648

303649
impl Drop for TypeContainer {
@@ -1583,6 +1929,23 @@ unsafe extern "C" fn cb_print_all_types<T: TypePrinter>(
15831929
}
15841930
}
15851931

1932+
unsafe extern "C" fn cb_progress_nop(
1933+
_ctxt: *mut ::std::os::raw::c_void,
1934+
_progress: usize,
1935+
_total: usize,
1936+
) -> bool {
1937+
true
1938+
}
1939+
1940+
unsafe extern "C" fn cb_progress<F: FnMut(usize, usize) -> bool>(
1941+
ctxt: *mut ::std::os::raw::c_void,
1942+
progress: usize,
1943+
total: usize,
1944+
) -> bool {
1945+
let ctxt = &mut *(ctxt as *mut F);
1946+
ctxt(progress, total)
1947+
}
1948+
15861949
unsafe extern "C" fn cb_free_tokens(
15871950
_ctxt: *mut ::std::os::raw::c_void,
15881951
tokens: *mut BNInstructionTextToken,

0 commit comments

Comments
 (0)