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