Skip to main content

binaryninja/types/
parser.rs

1#![allow(unused)]
2use binaryninjacore_sys::*;
3use std::ffi::{c_char, c_void};
4use std::fmt::Debug;
5use std::path::PathBuf;
6use std::ptr::NonNull;
7
8use crate::platform::Platform;
9use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
10use crate::string::{raw_to_string, BnString, IntoCStr};
11use crate::types::{QualifiedName, QualifiedNameAndType, Type, TypeContainer};
12
13pub type TypeParserErrorSeverity = BNTypeParserErrorSeverity;
14pub type TypeParserOption = BNTypeParserOption;
15
16/// Register a custom parser with the API
17pub fn register_type_parser<T: TypeParser>(
18    name: &str,
19    parser: T,
20) -> (&'static mut T, CoreTypeParser) {
21    let parser = Box::leak(Box::new(parser));
22    let mut callback = BNTypeParserCallbacks {
23        context: parser as *mut _ as *mut c_void,
24        getOptionText: Some(cb_get_option_text::<T>),
25        preprocessSource: Some(cb_preprocess_source::<T>),
26        parseTypesFromSource: Some(cb_parse_types_from_source::<T>),
27        parseTypeString: Some(cb_parse_type_string::<T>),
28        freeString: Some(cb_free_string),
29        freeResult: Some(cb_free_result),
30        freeErrorList: Some(cb_free_error_list),
31    };
32    let name = name.to_cstr();
33    let result = unsafe { BNRegisterTypeParser(name.as_ptr(), &mut callback) };
34    let core = unsafe { CoreTypeParser::from_raw(NonNull::new(result).unwrap()) };
35    (parser, core)
36}
37
38#[repr(transparent)]
39pub struct CoreTypeParser {
40    pub(crate) handle: NonNull<BNTypeParser>,
41}
42
43impl CoreTypeParser {
44    pub(crate) unsafe fn from_raw(handle: NonNull<BNTypeParser>) -> Self {
45        Self { handle }
46    }
47
48    pub fn parsers() -> Array<CoreTypeParser> {
49        let mut count = 0;
50        let result = unsafe { BNGetTypeParserList(&mut count) };
51        unsafe { Array::new(result, count, ()) }
52    }
53
54    pub fn parser_by_name(name: &str) -> Option<CoreTypeParser> {
55        let name_raw = name.to_cstr();
56        let result = unsafe { BNGetTypeParserByName(name_raw.as_ptr()) };
57        NonNull::new(result).map(|x| unsafe { Self::from_raw(x) })
58    }
59
60    pub fn name(&self) -> String {
61        let result = unsafe { BNGetTypeParserName(self.handle.as_ptr()) };
62        assert!(!result.is_null());
63        unsafe { BnString::into_string(result) }
64    }
65}
66
67impl TypeParser for CoreTypeParser {
68    fn get_option_text(&self, option: TypeParserOption, value: &str) -> Option<String> {
69        let mut output = std::ptr::null_mut();
70        let value_ptr = std::ptr::null_mut();
71        let result = unsafe {
72            BNGetTypeParserOptionText(self.handle.as_ptr(), option, value_ptr, &mut output)
73        };
74        result.then(|| {
75            assert!(!output.is_null());
76            unsafe { BnString::into_string(value_ptr) }
77        })
78    }
79
80    fn preprocess_source(
81        &self,
82        source: &str,
83        file_name: &str,
84        platform: &Platform,
85        existing_types: &TypeContainer,
86        options: &[String],
87        include_directories: &[PathBuf],
88    ) -> Result<String, Vec<TypeParserError>> {
89        let source_cstr = BnString::new(source);
90        let file_name_cstr = BnString::new(file_name);
91        let options: Vec<_> = options.iter().map(|o| o.to_cstr()).collect();
92        let options_raw: Vec<*const c_char> = options.iter().map(|o| o.as_ptr()).collect();
93        let include_directories: Vec<_> = include_directories
94            .iter()
95            .map(|d| d.clone().to_cstr())
96            .collect();
97        let include_directories_raw: Vec<*const c_char> =
98            include_directories.iter().map(|d| d.as_ptr()).collect();
99        let mut result = std::ptr::null_mut();
100        let mut errors = std::ptr::null_mut();
101        let mut error_count = 0;
102        let success = unsafe {
103            BNTypeParserPreprocessSource(
104                self.handle.as_ptr(),
105                source_cstr.as_ptr(),
106                file_name_cstr.as_ptr(),
107                platform.handle,
108                existing_types.handle.as_ptr(),
109                options_raw.as_ptr(),
110                options_raw.len(),
111                include_directories_raw.as_ptr(),
112                include_directories_raw.len(),
113                &mut result,
114                &mut errors,
115                &mut error_count,
116            )
117        };
118        if success {
119            assert!(!result.is_null());
120            let bn_result = unsafe { BnString::into_string(result) };
121            Ok(bn_result)
122        } else {
123            let errors: Array<TypeParserError> = unsafe { Array::new(errors, error_count, ()) };
124            Err(errors.to_vec())
125        }
126    }
127
128    fn parse_types_from_source(
129        &self,
130        source: &str,
131        file_name: &str,
132        platform: &Platform,
133        existing_types: &TypeContainer,
134        options: &[String],
135        include_directories: &[PathBuf],
136        auto_type_source: &str,
137    ) -> Result<TypeParserResult, Vec<TypeParserError>> {
138        let source_cstr = BnString::new(source);
139        let file_name_cstr = BnString::new(file_name);
140        let options: Vec<_> = options.iter().map(|o| o.to_cstr()).collect();
141        let options_raw: Vec<*const c_char> = options.iter().map(|o| o.as_ptr()).collect();
142        let include_directories: Vec<_> = include_directories
143            .iter()
144            .map(|d| d.clone().to_cstr())
145            .collect();
146        let include_directories_raw: Vec<*const c_char> =
147            include_directories.iter().map(|d| d.as_ptr()).collect();
148        let auto_type_source = BnString::new(auto_type_source);
149        let mut raw_result = BNTypeParserResult::default();
150        let mut errors = std::ptr::null_mut();
151        let mut error_count = 0;
152        let success = unsafe {
153            BNTypeParserParseTypesFromSource(
154                self.handle.as_ptr(),
155                source_cstr.as_ptr(),
156                file_name_cstr.as_ptr(),
157                platform.handle,
158                existing_types.handle.as_ptr(),
159                options_raw.as_ptr(),
160                options_raw.len(),
161                include_directories_raw.as_ptr(),
162                include_directories_raw.len(),
163                auto_type_source.as_ptr(),
164                &mut raw_result,
165                &mut errors,
166                &mut error_count,
167            )
168        };
169        if success {
170            let result = TypeParserResult::from_raw(&raw_result);
171            // NOTE: This is safe because the core allocated the TypeParserResult
172            TypeParserResult::free_raw(raw_result);
173            Ok(result)
174        } else {
175            let errors: Array<TypeParserError> = unsafe { Array::new(errors, error_count, ()) };
176            Err(errors.to_vec())
177        }
178    }
179
180    fn parse_type_string(
181        &self,
182        source: &str,
183        platform: &Platform,
184        existing_types: &TypeContainer,
185    ) -> Result<QualifiedNameAndType, Vec<TypeParserError>> {
186        let source_cstr = BnString::new(source);
187        let mut output = BNQualifiedNameAndType::default();
188        let mut errors = std::ptr::null_mut();
189        let mut error_count = 0;
190        let result = unsafe {
191            BNTypeParserParseTypeString(
192                self.handle.as_ptr(),
193                source_cstr.as_ptr(),
194                platform.handle,
195                existing_types.handle.as_ptr(),
196                &mut output,
197                &mut errors,
198                &mut error_count,
199            )
200        };
201        if result {
202            Ok(QualifiedNameAndType::from_owned_raw(output))
203        } else {
204            unsafe { BNFreeQualifiedNameAndType(&mut output) };
205            let errors: Array<TypeParserError> = unsafe { Array::new(errors, error_count, ()) };
206            Err(errors.to_vec())
207        }
208    }
209}
210
211impl Default for CoreTypeParser {
212    fn default() -> Self {
213        // TODO: This should return a ref
214        unsafe { Self::from_raw(NonNull::new(BNGetDefaultTypeParser()).unwrap()) }
215    }
216}
217
218// TODO: Impl this on platform.
219pub trait TypeParser {
220    /// Get the string representation of an option for passing to parse_type_*.
221    /// Returns a string representing the option if the parser supports it,
222    /// otherwise None
223    ///
224    /// * `option` - Option type
225    /// * `value` - Option value
226    fn get_option_text(&self, option: TypeParserOption, value: &str) -> Option<String>;
227
228    /// Preprocess a block of source, returning the source that would be parsed
229    ///
230    /// * `source` - Source code to process
231    /// * `file_name` - Name of the file containing the source (does not need to exist on disk)
232    /// * `platform` - Platform to assume the source is relevant to
233    /// * `existing_types` - Optional collection of all existing types to use for parsing context
234    /// * `options` - Optional string arguments to pass as options, e.g. command line arguments
235    /// * `include_dirs` - Optional list of directories to include in the header search path
236    fn preprocess_source(
237        &self,
238        source: &str,
239        file_name: &str,
240        platform: &Platform,
241        existing_types: &TypeContainer,
242        options: &[String],
243        include_dirs: &[PathBuf],
244    ) -> Result<String, Vec<TypeParserError>>;
245
246    /// Parse an entire block of source into types, variables, and functions
247    ///
248    /// * `source` - Source code to parse
249    /// * `file_name` - Name of the file containing the source (optional: exists on disk)
250    /// * `platform` - Platform to assume the types are relevant to
251    /// * `existing_types` - Optional container of all existing types to use for parsing context
252    /// * `options` - Optional string arguments to pass as options, e.g. command line arguments
253    /// * `include_dirs` - Optional list of directories to include in the header search path
254    /// * `auto_type_source` - Optional source of types if used for automatically generated types
255    fn parse_types_from_source(
256        &self,
257        source: &str,
258        file_name: &str,
259        platform: &Platform,
260        existing_types: &TypeContainer,
261        options: &[String],
262        include_dirs: &[PathBuf],
263        auto_type_source: &str,
264    ) -> Result<TypeParserResult, Vec<TypeParserError>>;
265
266    /// Parse a single type and name from a string containing their definition.
267    ///
268    /// * `source` - Source code to parse
269    /// * `platform` - Platform to assume the types are relevant to
270    /// * `existing_types` - Optional container of all existing types to use for parsing context
271    fn parse_type_string(
272        &self,
273        source: &str,
274        platform: &Platform,
275        existing_types: &TypeContainer,
276    ) -> Result<QualifiedNameAndType, Vec<TypeParserError>>;
277}
278
279impl CoreArrayProvider for CoreTypeParser {
280    type Raw = *mut BNTypeParser;
281    type Context = ();
282    type Wrapped<'a> = Self;
283}
284
285unsafe impl CoreArrayProviderInner for CoreTypeParser {
286    unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
287        BNFreeTypeParserList(raw)
288    }
289
290    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
291        // TODO: Because handle is a NonNull we should prob make Self::Raw that as well...
292        let handle = NonNull::new(*raw).unwrap();
293        CoreTypeParser::from_raw(handle)
294    }
295}
296
297#[derive(Clone, Debug, Eq, PartialEq)]
298pub struct TypeParserError {
299    pub severity: TypeParserErrorSeverity,
300    pub message: String,
301    pub file_name: String,
302    pub line: u64,
303    pub column: u64,
304}
305
306impl TypeParserError {
307    pub(crate) fn from_raw(value: &BNTypeParserError) -> Self {
308        Self {
309            severity: value.severity,
310            message: raw_to_string(value.message).unwrap(),
311            file_name: raw_to_string(value.fileName).unwrap(),
312            line: value.line,
313            column: value.column,
314        }
315    }
316
317    pub(crate) fn from_owned_raw(value: BNTypeParserError) -> Self {
318        let owned = Self::from_raw(&value);
319        Self::free_raw(value);
320        owned
321    }
322
323    pub(crate) fn into_raw(value: Self) -> BNTypeParserError {
324        BNTypeParserError {
325            severity: value.severity,
326            message: BnString::into_raw(BnString::new(value.message)),
327            fileName: BnString::into_raw(BnString::new(value.file_name)),
328            line: value.line,
329            column: value.column,
330        }
331    }
332
333    pub(crate) fn free_raw(value: BNTypeParserError) {
334        unsafe { BnString::free_raw(value.message) };
335        unsafe { BnString::free_raw(value.fileName) };
336    }
337
338    pub fn new(
339        severity: TypeParserErrorSeverity,
340        message: String,
341        file_name: String,
342        line: u64,
343        column: u64,
344    ) -> Self {
345        Self {
346            severity,
347            message,
348            file_name,
349            line,
350            column,
351        }
352    }
353}
354
355impl CoreArrayProvider for TypeParserError {
356    type Raw = BNTypeParserError;
357    type Context = ();
358    type Wrapped<'a> = Self;
359}
360
361unsafe impl CoreArrayProviderInner for TypeParserError {
362    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
363        unsafe { BNFreeTypeParserErrors(raw, count) }
364    }
365
366    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
367        Self::from_raw(raw)
368    }
369}
370
371#[derive(Debug, Eq, PartialEq, Default)]
372pub struct TypeParserResult {
373    pub types: Vec<ParsedType>,
374    pub variables: Vec<ParsedType>,
375    pub functions: Vec<ParsedType>,
376}
377
378impl TypeParserResult {
379    pub(crate) fn from_raw(value: &BNTypeParserResult) -> Self {
380        let raw_types = unsafe { std::slice::from_raw_parts(value.types, value.typeCount) };
381        let types = raw_types.iter().map(ParsedType::from_raw).collect();
382        let raw_variables =
383            unsafe { std::slice::from_raw_parts(value.variables, value.variableCount) };
384        let variables = raw_variables.iter().map(ParsedType::from_raw).collect();
385        let raw_functions =
386            unsafe { std::slice::from_raw_parts(value.functions, value.functionCount) };
387        let functions = raw_functions.iter().map(ParsedType::from_raw).collect();
388        TypeParserResult {
389            types,
390            variables,
391            functions,
392        }
393    }
394
395    /// Return a rust allocated type parser result, free using [`Self::free_owned_raw`].
396    ///
397    /// Under no circumstance should you call [`Self::free_raw`] on the returned result.
398    pub(crate) fn into_raw(value: Self) -> BNTypeParserResult {
399        let boxed_raw_types: Box<[BNParsedType]> = value
400            .types
401            .into_iter()
402            // NOTE: Freed with [`Self::free_owned_raw`].
403            .map(ParsedType::into_raw)
404            .collect();
405        let boxed_raw_variables: Box<[BNParsedType]> = value
406            .variables
407            .into_iter()
408            // NOTE: Freed with [`Self::free_owned_raw`].
409            .map(ParsedType::into_raw)
410            .collect();
411        let boxed_raw_functions: Box<[BNParsedType]> = value
412            .functions
413            .into_iter()
414            // NOTE: Freed with [`Self::free_owned_raw`].
415            .map(ParsedType::into_raw)
416            .collect();
417        BNTypeParserResult {
418            typeCount: boxed_raw_types.len(),
419            // NOTE: Freed with [`Self::free_owned_raw`].
420            types: Box::leak(boxed_raw_types).as_mut_ptr(),
421            variableCount: boxed_raw_variables.len(),
422            // NOTE: Freed with [`Self::free_owned_raw`].
423            variables: Box::leak(boxed_raw_variables).as_mut_ptr(),
424            functionCount: boxed_raw_functions.len(),
425            // NOTE: Freed with [`Self::free_owned_raw`].
426            functions: Box::leak(boxed_raw_functions).as_mut_ptr(),
427        }
428    }
429
430    pub(crate) fn free_raw(mut value: BNTypeParserResult) {
431        // SAFETY: `value` must be a properly initialized BNTypeParserResult.
432        // SAFETY: `value` must be core allocated.
433        unsafe { BNFreeTypeParserResult(&mut value) };
434    }
435
436    pub(crate) fn free_owned_raw(value: BNTypeParserResult) {
437        let raw_types = std::ptr::slice_from_raw_parts_mut(value.types, value.typeCount);
438        // Free the rust allocated types list
439        let boxed_types = unsafe { Box::from_raw(raw_types) };
440        for parsed_type in boxed_types {
441            ParsedType::free_raw(parsed_type);
442        }
443        let raw_variables =
444            std::ptr::slice_from_raw_parts_mut(value.variables, value.variableCount);
445        // Free the rust allocated variables list
446        let boxed_variables = unsafe { Box::from_raw(raw_variables) };
447        for parsed_type in boxed_variables {
448            ParsedType::free_raw(parsed_type);
449        }
450        let raw_functions =
451            std::ptr::slice_from_raw_parts_mut(value.functions, value.functionCount);
452        // Free the rust allocated functions list
453        let boxed_functions = unsafe { Box::from_raw(raw_functions) };
454        for parsed_type in boxed_functions {
455            ParsedType::free_raw(parsed_type);
456        }
457    }
458}
459
460#[derive(Debug, Clone, Eq, PartialEq)]
461pub struct ParsedType {
462    pub name: QualifiedName,
463    pub ty: Ref<Type>,
464    pub user: bool,
465}
466
467impl ParsedType {
468    pub(crate) fn from_raw(value: &BNParsedType) -> Self {
469        Self {
470            name: QualifiedName::from_raw(&value.name),
471            ty: unsafe { Type::from_raw(value.type_).to_owned() },
472            user: value.isUser,
473        }
474    }
475
476    pub(crate) fn from_owned_raw(value: BNParsedType) -> Self {
477        let owned = Self::from_raw(&value);
478        Self::free_raw(value);
479        owned
480    }
481
482    pub(crate) fn into_raw(value: Self) -> BNParsedType {
483        BNParsedType {
484            name: QualifiedName::into_raw(value.name),
485            type_: unsafe { Ref::into_raw(value.ty) }.handle,
486            isUser: value.user,
487        }
488    }
489
490    pub(crate) fn free_raw(value: BNParsedType) {
491        QualifiedName::free_raw(value.name);
492        let _ = unsafe { Type::ref_from_raw(value.type_) };
493    }
494
495    pub fn new(name: QualifiedName, ty: Ref<Type>, user: bool) -> Self {
496        Self { name, ty, user }
497    }
498}
499
500impl CoreArrayProvider for ParsedType {
501    type Raw = BNParsedType;
502    type Context = ();
503    type Wrapped<'b> = Self;
504}
505
506unsafe impl CoreArrayProviderInner for ParsedType {
507    unsafe fn free(_raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
508        // Expected to be freed with BNFreeTypeParserResult
509        // TODO ^ because of the above, we should not provide an array provider for this
510    }
511
512    unsafe fn wrap_raw<'b>(raw: &'b Self::Raw, _context: &'b Self::Context) -> Self::Wrapped<'b> {
513        ParsedType::from_raw(raw)
514    }
515}
516
517unsafe extern "C" fn cb_get_option_text<T: TypeParser>(
518    ctxt: *mut ::std::os::raw::c_void,
519    option: BNTypeParserOption,
520    value: *const c_char,
521    result: *mut *mut c_char,
522) -> bool {
523    let ctxt: &mut T = &mut *(ctxt as *mut T);
524    if let Some(inner_result) = ctxt.get_option_text(option, &raw_to_string(value).unwrap()) {
525        let bn_inner_result = BnString::new(inner_result);
526        // NOTE: Dropped by `cb_free_string`
527        *result = BnString::into_raw(bn_inner_result);
528        true
529    } else {
530        *result = std::ptr::null_mut();
531        false
532    }
533}
534
535unsafe extern "C" fn cb_preprocess_source<T: TypeParser>(
536    ctxt: *mut c_void,
537    source: *const c_char,
538    file_name: *const c_char,
539    platform: *mut BNPlatform,
540    existing_types: *mut BNTypeContainer,
541    options: *const *const c_char,
542    option_count: usize,
543    include_dirs: *const *const c_char,
544    include_dir_count: usize,
545    result: *mut *mut c_char,
546    errors: *mut *mut BNTypeParserError,
547    error_count: *mut usize,
548) -> bool {
549    let ctxt: &mut T = &mut *(ctxt as *mut T);
550    let platform = Platform { handle: platform };
551    let existing_types_ptr = NonNull::new(existing_types).unwrap();
552    let existing_types = TypeContainer::from_raw(existing_types_ptr);
553    let options_raw = unsafe { std::slice::from_raw_parts(options, option_count) };
554    let options: Vec<_> = options_raw
555        .iter()
556        .filter_map(|&r| raw_to_string(r))
557        .collect();
558    let includes_raw = unsafe { std::slice::from_raw_parts(include_dirs, include_dir_count) };
559    let includes: Vec<_> = includes_raw
560        .iter()
561        .filter_map(|&r| Some(PathBuf::from(raw_to_string(r)?)))
562        .collect();
563    match ctxt.preprocess_source(
564        &raw_to_string(source).unwrap(),
565        &raw_to_string(file_name).unwrap(),
566        &platform,
567        &existing_types,
568        &options,
569        &includes,
570    ) {
571        Ok(inner_result) => {
572            let bn_inner_result = BnString::new(inner_result);
573            // NOTE: Dropped by `cb_free_string`
574            *result = BnString::into_raw(bn_inner_result);
575            *errors = std::ptr::null_mut();
576            *error_count = 0;
577            true
578        }
579        Err(inner_errors) => {
580            *result = std::ptr::null_mut();
581            *error_count = inner_errors.len();
582            // NOTE: Leaking errors here, dropped by `cb_free_error_list`.
583            let inner_errors: Box<[_]> = inner_errors
584                .into_iter()
585                .map(TypeParserError::into_raw)
586                .collect();
587            // NOTE: Dropped by `cb_free_error_list`
588            *errors = Box::leak(inner_errors).as_mut_ptr();
589            false
590        }
591    }
592}
593
594unsafe extern "C" fn cb_parse_types_from_source<T: TypeParser>(
595    ctxt: *mut c_void,
596    source: *const c_char,
597    file_name: *const c_char,
598    platform: *mut BNPlatform,
599    existing_types: *mut BNTypeContainer,
600    options: *const *const c_char,
601    option_count: usize,
602    include_dirs: *const *const c_char,
603    include_dir_count: usize,
604    auto_type_source: *const c_char,
605    result: *mut BNTypeParserResult,
606    errors: *mut *mut BNTypeParserError,
607    error_count: *mut usize,
608) -> bool {
609    let ctxt: &mut T = &mut *(ctxt as *mut T);
610    let platform = Platform { handle: platform };
611    let existing_types_ptr = NonNull::new(existing_types).unwrap();
612    let existing_types = TypeContainer::from_raw(existing_types_ptr);
613    let options_raw = unsafe { std::slice::from_raw_parts(options, option_count) };
614    let options: Vec<_> = options_raw
615        .iter()
616        .filter_map(|&r| raw_to_string(r))
617        .collect();
618    let includes_raw = unsafe { std::slice::from_raw_parts(include_dirs, include_dir_count) };
619    let includes: Vec<_> = includes_raw
620        .iter()
621        .filter_map(|&r| Some(PathBuf::from(raw_to_string(r)?)))
622        .collect();
623    match ctxt.parse_types_from_source(
624        &raw_to_string(source).unwrap(),
625        &raw_to_string(file_name).unwrap(),
626        &platform,
627        &existing_types,
628        &options,
629        &includes,
630        &raw_to_string(auto_type_source).unwrap(),
631    ) {
632        Ok(type_parser_result) => {
633            *result = TypeParserResult::into_raw(type_parser_result);
634            *errors = std::ptr::null_mut();
635            *error_count = 0;
636            true
637        }
638        Err(inner_errors) => {
639            *error_count = inner_errors.len();
640            let inner_errors: Box<[_]> = inner_errors
641                .into_iter()
642                .map(TypeParserError::into_raw)
643                .collect();
644            *result = Default::default();
645            // NOTE: Dropped by cb_free_error_list
646            *errors = Box::leak(inner_errors).as_mut_ptr();
647            false
648        }
649    }
650}
651
652unsafe extern "C" fn cb_parse_type_string<T: TypeParser>(
653    ctxt: *mut c_void,
654    source: *const c_char,
655    platform: *mut BNPlatform,
656    existing_types: *mut BNTypeContainer,
657    result: *mut BNQualifiedNameAndType,
658    errors: *mut *mut BNTypeParserError,
659    error_count: *mut usize,
660) -> bool {
661    let ctxt: &mut T = &mut *(ctxt as *mut T);
662    let platform = Platform { handle: platform };
663    let existing_types_ptr = NonNull::new(existing_types).unwrap();
664    let existing_types = TypeContainer::from_raw(existing_types_ptr);
665    match ctxt.parse_type_string(&raw_to_string(source).unwrap(), &platform, &existing_types) {
666        Ok(inner_result) => {
667            *result = QualifiedNameAndType::into_raw(inner_result);
668            *errors = std::ptr::null_mut();
669            *error_count = 0;
670            true
671        }
672        Err(inner_errors) => {
673            *error_count = inner_errors.len();
674            let inner_errors: Box<[_]> = inner_errors
675                .into_iter()
676                .map(TypeParserError::into_raw)
677                .collect();
678            *result = Default::default();
679            // NOTE: Dropped by cb_free_error_list
680            *errors = Box::leak(inner_errors).as_mut_ptr();
681            false
682        }
683    }
684}
685
686unsafe extern "C" fn cb_free_string(_ctxt: *mut c_void, string: *mut c_char) {
687    // SAFETY: The returned string is just BnString
688    BnString::free_raw(string);
689}
690
691unsafe extern "C" fn cb_free_result(_ctxt: *mut c_void, result: *mut BNTypeParserResult) {
692    TypeParserResult::free_owned_raw(*result);
693}
694
695unsafe extern "C" fn cb_free_error_list(
696    _ctxt: *mut c_void,
697    errors: *mut BNTypeParserError,
698    error_count: usize,
699) {
700    let errors = std::ptr::slice_from_raw_parts_mut(errors, error_count);
701    let boxed_errors = Box::from_raw(errors);
702    for error in boxed_errors {
703        TypeParserError::free_raw(error);
704    }
705}