binaryninja/types/
container.rs

1// TODO: Add these!
2// The `TypeContainer` class should not generally be instantiated directly. Instances
3// can be retrieved from the following properties and methods in the API:
4// * [BinaryView::type_container]
5// * [BinaryView::auto_type_container]
6// * [BinaryView::user_type_container]
7// * [Platform::type_container]
8// * [TypeLibrary::type_container]
9// * [DebugInfo::get_type_container]
10
11use crate::platform::Platform;
12use crate::progress::{NoProgressCallback, ProgressCallback};
13use crate::rc::{Array, Ref};
14use crate::string::{raw_to_string, BnString, IntoCStr};
15use crate::types::{QualifiedName, QualifiedNameAndType, Type, TypeParserError, TypeParserResult};
16use binaryninjacore_sys::*;
17use std::collections::HashMap;
18use std::ffi::{c_char, c_void};
19use std::fmt::{Debug, Formatter};
20use std::ptr::NonNull;
21
22pub type TypeContainerType = BNTypeContainerType;
23
24/// A `TypeContainer` is a generic interface to access various Binary Ninja models
25/// that contain types. Types are stored with both a unique id and a unique name.
26#[repr(transparent)]
27pub struct TypeContainer {
28    pub handle: NonNull<BNTypeContainer>,
29}
30
31impl TypeContainer {
32    pub(crate) unsafe fn from_raw(handle: NonNull<BNTypeContainer>) -> Self {
33        // NOTE: There does not seem to be any shared ref counting for type containers, it seems if the
34        // NOTE: binary view is freed the type container will be freed and cause this to become invalid
35        // NOTE: but this is how the C++ and Python bindings operate so i guess its fine?
36        // TODO: I really dont get how some of the usage of the TypeContainer doesnt free the underlying container.
37        // TODO: So for now we always duplicate the type container
38        let cloned_ptr = NonNull::new(BNDuplicateTypeContainer(handle.as_ptr()));
39        Self {
40            handle: cloned_ptr.unwrap(),
41        }
42    }
43
44    /// Get an empty type container that contains no types (immutable)
45    pub fn empty() -> TypeContainer {
46        let result = unsafe { BNGetEmptyTypeContainer() };
47        unsafe { Self::from_raw(NonNull::new(result).unwrap()) }
48    }
49
50    /// Get an id string for the Type Container. This will be unique within a given
51    /// analysis session, but may not be globally unique.
52    pub fn id(&self) -> String {
53        let result = unsafe { BNTypeContainerGetId(self.handle.as_ptr()) };
54        assert!(!result.is_null());
55        unsafe { BnString::into_string(result) }
56    }
57
58    /// Get a user-friendly name for the Type Container.
59    pub fn name(&self) -> String {
60        let result = unsafe { BNTypeContainerGetName(self.handle.as_ptr()) };
61        assert!(!result.is_null());
62        unsafe { BnString::into_string(result) }
63    }
64
65    /// Get the type of underlying model the Type Container is accessing.
66    pub fn container_type(&self) -> TypeContainerType {
67        unsafe { BNTypeContainerGetType(self.handle.as_ptr()) }
68    }
69
70    /// If the Type Container supports mutable operations (add, rename, delete)
71    pub fn is_mutable(&self) -> bool {
72        unsafe { BNTypeContainerIsMutable(self.handle.as_ptr()) }
73    }
74
75    /// Get the Platform object associated with this Type Container. All Type Containers
76    /// have exactly one associated Platform (as opposed to, e.g. Type Libraries).
77    pub fn platform(&self) -> Ref<Platform> {
78        let result = unsafe { BNTypeContainerGetPlatform(self.handle.as_ptr()) };
79        assert!(!result.is_null());
80        unsafe { Platform::ref_from_raw(result) }
81    }
82
83    /// Add or update types to a Type Container. If the Type Container already contains
84    /// a type with the same name as a type being added, the existing type will be
85    /// replaced with the definition given to this function, and references will be
86    /// updated in the source model.
87    pub fn add_types<I, T>(&self, types: I) -> bool
88    where
89        I: IntoIterator<Item = T>,
90        T: Into<QualifiedNameAndType>,
91    {
92        self.add_types_with_progress(types, NoProgressCallback)
93    }
94
95    pub fn add_types_with_progress<I, T, P>(&self, types: I, mut progress: P) -> bool
96    where
97        I: IntoIterator<Item = T>,
98        T: Into<QualifiedNameAndType>,
99        P: ProgressCallback,
100    {
101        // TODO: I dislike how this iter unzip looks like... but its how to avoid allocating again...
102        let (raw_names, mut raw_types): (Vec<BNQualifiedName>, Vec<_>) = types
103            .into_iter()
104            .map(|t| {
105                let t = t.into();
106                // Leaked to be freed after the call to core.
107                (
108                    QualifiedName::into_raw(t.name),
109                    unsafe { Ref::into_raw(t.ty) }.handle,
110                )
111            })
112            .unzip();
113
114        let mut result_names = std::ptr::null_mut();
115        let mut result_ids = std::ptr::null_mut();
116        let mut result_count = 0;
117
118        let success = unsafe {
119            BNTypeContainerAddTypes(
120                self.handle.as_ptr(),
121                raw_names.as_ptr(),
122                raw_types.as_mut_ptr(),
123                raw_types.len(),
124                Some(P::cb_progress_callback),
125                &mut progress as *mut P as *mut c_void,
126                &mut result_names,
127                &mut result_ids,
128                &mut result_count,
129            )
130        };
131
132        for name in raw_names {
133            QualifiedName::free_raw(name);
134        }
135        for ty in raw_types {
136            let _ = unsafe { Type::ref_from_raw(ty) };
137        }
138        success
139    }
140
141    /// Rename a type in the Type Container. All references to this type will be updated
142    /// (by id) to use the new name.
143    ///
144    /// Returns true if the type was renamed.
145    pub fn rename_type<T: Into<QualifiedName>>(&self, name: T, type_id: &str) -> bool {
146        let type_id = type_id.to_cstr();
147        let raw_name = QualifiedName::into_raw(name.into());
148        let success =
149            unsafe { BNTypeContainerRenameType(self.handle.as_ptr(), type_id.as_ptr(), &raw_name) };
150        QualifiedName::free_raw(raw_name);
151        success
152    }
153
154    /// Delete a type in the Type Container. Behavior of references to this type is
155    /// not specified and you may end up with broken references if any still exist.
156    ///
157    /// Returns true if the type was deleted.
158    pub fn delete_type(&self, type_id: &str) -> bool {
159        let type_id = type_id.to_cstr();
160        unsafe { BNTypeContainerDeleteType(self.handle.as_ptr(), type_id.as_ptr()) }
161    }
162
163    /// Get the unique id of the type in the Type Container with the given name.
164    ///
165    /// If no type with that name exists, returns None.
166    pub fn type_id<T: Into<QualifiedName>>(&self, name: T) -> Option<String> {
167        let mut result = std::ptr::null_mut();
168        let raw_name = QualifiedName::into_raw(name.into());
169        let success =
170            unsafe { BNTypeContainerGetTypeId(self.handle.as_ptr(), &raw_name, &mut result) };
171        QualifiedName::free_raw(raw_name);
172        success.then(|| unsafe { BnString::into_string(result) })
173    }
174
175    /// Get the unique name of the type in the Type Container with the given id.
176    ///
177    /// If no type with that id exists, returns None.
178    pub fn type_name(&self, type_id: &str) -> Option<QualifiedName> {
179        let type_id = type_id.to_cstr();
180        let mut result = BNQualifiedName::default();
181        let success = unsafe {
182            BNTypeContainerGetTypeName(self.handle.as_ptr(), type_id.as_ptr(), &mut result)
183        };
184        success.then(|| QualifiedName::from_owned_raw(result))
185    }
186
187    /// Get the definition of the type in the Type Container with the given id.
188    ///
189    /// If no type with that id exists, returns None.
190    pub fn type_by_id(&self, type_id: &str) -> Option<Ref<Type>> {
191        let type_id = type_id.to_cstr();
192        let mut result = std::ptr::null_mut();
193        let success = unsafe {
194            BNTypeContainerGetTypeById(self.handle.as_ptr(), type_id.as_ptr(), &mut result)
195        };
196        success.then(|| unsafe { Type::ref_from_raw(result) })
197    }
198
199    /// Get the definition of the type in the Type Container with the given name.
200    ///
201    /// If no type with that name exists, returns None.
202    pub fn type_by_name<T: Into<QualifiedName>>(&self, name: T) -> Option<Ref<Type>> {
203        let mut result = std::ptr::null_mut();
204        let raw_name = QualifiedName::into_raw(name.into());
205        let success =
206            unsafe { BNTypeContainerGetTypeByName(self.handle.as_ptr(), &raw_name, &mut result) };
207        QualifiedName::free_raw(raw_name);
208        success.then(|| unsafe { Type::ref_from_raw(result) })
209    }
210
211    /// Get a mapping of all types in a Type Container.
212    pub fn types(&self) -> Option<HashMap<String, (QualifiedName, Ref<Type>)>> {
213        let mut type_ids = std::ptr::null_mut();
214        let mut type_names = std::ptr::null_mut();
215        let mut type_types = std::ptr::null_mut();
216        let mut type_count = 0;
217        let success = unsafe {
218            BNTypeContainerGetTypes(
219                self.handle.as_ptr(),
220                &mut type_ids,
221                &mut type_names,
222                &mut type_types,
223                &mut type_count,
224            )
225        };
226        success.then(|| unsafe {
227            let raw_ids = std::slice::from_raw_parts(type_ids, type_count);
228            let raw_names = std::slice::from_raw_parts(type_names, type_count);
229            let raw_types = std::slice::from_raw_parts(type_types, type_count);
230            let mut map = HashMap::new();
231            for (idx, raw_id) in raw_ids.iter().enumerate() {
232                let id = raw_to_string(*raw_id).expect("Valid string");
233                // Take the qualified name as a ref as the name should not be freed.
234                let name = QualifiedName::from_raw(&raw_names[idx]);
235                // Take the type as an owned ref, as the returned type was not already incremented.
236                let ty = Type::from_raw(raw_types[idx]).to_owned();
237                map.insert(id, (name, ty));
238            }
239            BNFreeStringList(type_ids, type_count);
240            BNFreeTypeNameList(type_names, type_count);
241            BNFreeTypeList(type_types, type_count);
242            map
243        })
244    }
245
246    /// Get all type ids in a Type Container.
247    pub fn type_ids(&self) -> Option<Array<BnString>> {
248        let mut type_ids = std::ptr::null_mut();
249        let mut type_count = 0;
250        let success = unsafe {
251            BNTypeContainerGetTypeIds(self.handle.as_ptr(), &mut type_ids, &mut type_count)
252        };
253        success.then(|| unsafe { Array::new(type_ids, type_count, ()) })
254    }
255
256    /// Get all type names in a Type Container.
257    pub fn type_names(&self) -> Option<Array<QualifiedName>> {
258        let mut type_ids = std::ptr::null_mut();
259        let mut type_count = 0;
260        let success = unsafe {
261            BNTypeContainerGetTypeNames(self.handle.as_ptr(), &mut type_ids, &mut type_count)
262        };
263        success.then(|| unsafe { Array::new(type_ids, type_count, ()) })
264    }
265
266    /// Get a mapping of all type ids and type names in a Type Container.
267    pub fn type_names_and_ids(&self) -> Option<(Array<BnString>, Array<QualifiedName>)> {
268        let mut type_ids = std::ptr::null_mut();
269        let mut type_names = std::ptr::null_mut();
270        let mut type_count = 0;
271        let success = unsafe {
272            BNTypeContainerGetTypeNamesAndIds(
273                self.handle.as_ptr(),
274                &mut type_ids,
275                &mut type_names,
276                &mut type_count,
277            )
278        };
279        success.then(|| unsafe {
280            let ids = Array::new(type_ids, type_count, ());
281            let names = Array::new(type_names, type_count, ());
282            (ids, names)
283        })
284    }
285
286    /// Parse a single type and name from a string containing their definition, with
287    /// knowledge of the types in the Type Container.
288    ///
289    /// * `source` - Source code to parse
290    /// * `import_dependencies` - If Type Library / Type Archive types should be imported during parsing
291    pub fn parse_type_string(
292        &self,
293        source: &str,
294        import_dependencies: bool,
295    ) -> Result<QualifiedNameAndType, Array<TypeParserError>> {
296        let source = source.to_cstr();
297        let mut result = BNQualifiedNameAndType::default();
298        let mut errors = std::ptr::null_mut();
299        let mut error_count = 0;
300        let success = unsafe {
301            BNTypeContainerParseTypeString(
302                self.handle.as_ptr(),
303                source.as_ptr(),
304                import_dependencies,
305                &mut result,
306                &mut errors,
307                &mut error_count,
308            )
309        };
310        if success {
311            Ok(QualifiedNameAndType::from_owned_raw(result))
312        } else {
313            assert!(!errors.is_null());
314            Err(unsafe { Array::new(errors, error_count, ()) })
315        }
316    }
317
318    /// Parse an entire block of source into types, variables, and functions, with
319    /// knowledge of the types in the Type Container.
320    ///
321    /// * `source` - Source code to parse
322    /// * `file_name` - Name of the file containing the source (optional: exists on disk)
323    /// * `options` - String arguments to pass as options, e.g. command line arguments
324    /// * `include_dirs` - List of directories to include in the header search path
325    /// * `auto_type_source` - Source of types if used for automatically generated types
326    /// * `import_dependencies` - If Type Library / Type Archive types should be imported during parsing
327    pub fn parse_types_from_source<O, I>(
328        &self,
329        source: &str,
330        filename: &str,
331        options: O,
332        include_directories: I,
333        auto_type_source: &str,
334        import_dependencies: bool,
335    ) -> Result<TypeParserResult, Array<TypeParserError>>
336    where
337        O: IntoIterator<Item = String>,
338        I: IntoIterator<Item = String>,
339    {
340        let source = source.to_cstr();
341        let filename = filename.to_cstr();
342        let options: Vec<_> = options.into_iter().map(|o| o.to_cstr()).collect();
343        let options_raw: Vec<*const c_char> = options.iter().map(|o| o.as_ptr()).collect();
344        let include_directories: Vec<_> = include_directories
345            .into_iter()
346            .map(|d| d.to_cstr())
347            .collect();
348        let include_directories_raw: Vec<*const c_char> =
349            include_directories.iter().map(|d| d.as_ptr()).collect();
350        let auto_type_source = auto_type_source.to_cstr();
351        let mut raw_result = BNTypeParserResult::default();
352        let mut errors = std::ptr::null_mut();
353        let mut error_count = 0;
354        let success = unsafe {
355            BNTypeContainerParseTypesFromSource(
356                self.handle.as_ptr(),
357                source.as_ptr(),
358                filename.as_ptr(),
359                options_raw.as_ptr(),
360                options_raw.len(),
361                include_directories_raw.as_ptr(),
362                include_directories_raw.len(),
363                auto_type_source.as_ptr(),
364                import_dependencies,
365                &mut raw_result,
366                &mut errors,
367                &mut error_count,
368            )
369        };
370        if success {
371            let result = TypeParserResult::from_raw(&raw_result);
372            // NOTE: This is safe because the core allocated the TypeParserResult
373            TypeParserResult::free_raw(raw_result);
374            Ok(result)
375        } else {
376            assert!(!errors.is_null());
377            Err(unsafe { Array::new(errors, error_count, ()) })
378        }
379    }
380}
381
382impl Debug for TypeContainer {
383    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
384        f.debug_struct("TypeContainer")
385            .field("id", &self.id())
386            .field("name", &self.name())
387            .field("container_type", &self.container_type())
388            .field("is_mutable", &self.is_mutable())
389            .field("type_names", &self.type_names().unwrap().to_vec())
390            .finish()
391    }
392}
393
394impl Drop for TypeContainer {
395    fn drop(&mut self) {
396        unsafe { BNFreeTypeContainer(self.handle.as_ptr()) }
397    }
398}
399
400impl Clone for TypeContainer {
401    fn clone(&self) -> Self {
402        unsafe {
403            let cloned_ptr = NonNull::new(BNDuplicateTypeContainer(self.handle.as_ptr()));
404            Self {
405                handle: cloned_ptr.unwrap(),
406            }
407        }
408    }
409}