binaryninja/
type_printer.rs

1#![allow(unused)]
2
3use crate::binary_view::BinaryView;
4use crate::disassembly::InstructionTextToken;
5use crate::platform::Platform;
6use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
7use crate::string::{raw_to_string, BnString, IntoCStr};
8use crate::type_container::TypeContainer;
9use crate::types::{NamedTypeReference, QualifiedName, QualifiedNameAndType, Type};
10use binaryninjacore_sys::*;
11use std::ffi::{c_char, c_int, c_void};
12use std::ptr::NonNull;
13
14pub type TokenEscapingType = BNTokenEscapingType;
15pub type TypeDefinitionLineType = BNTypeDefinitionLineType;
16
17/// Register a custom parser with the API
18pub fn register_type_printer<T: TypePrinter>(
19    name: &str,
20    parser: T,
21) -> (&'static mut T, CoreTypePrinter) {
22    let parser = Box::leak(Box::new(parser));
23    let mut callback = BNTypePrinterCallbacks {
24        context: parser as *mut _ as *mut c_void,
25        getTypeTokens: Some(cb_get_type_tokens::<T>),
26        getTypeTokensBeforeName: Some(cb_get_type_tokens_before_name::<T>),
27        getTypeTokensAfterName: Some(cb_get_type_tokens_after_name::<T>),
28        getTypeString: Some(cb_get_type_string::<T>),
29        getTypeStringBeforeName: Some(cb_get_type_string_before_name::<T>),
30        getTypeStringAfterName: Some(cb_get_type_string_after_name::<T>),
31        getTypeLines: Some(cb_get_type_lines::<T>),
32        printAllTypes: Some(cb_print_all_types::<T>),
33        freeTokens: Some(cb_free_tokens),
34        freeString: Some(cb_free_string),
35        freeLines: Some(cb_free_lines),
36    };
37    let raw_name = name.to_cstr();
38    let result = unsafe { BNRegisterTypePrinter(raw_name.as_ptr(), &mut callback) };
39    let core = unsafe { CoreTypePrinter::from_raw(NonNull::new(result).unwrap()) };
40    (parser, core)
41}
42
43#[repr(transparent)]
44pub struct CoreTypePrinter {
45    pub(crate) handle: NonNull<BNTypePrinter>,
46}
47
48impl CoreTypePrinter {
49    pub(crate) unsafe fn from_raw(handle: NonNull<BNTypePrinter>) -> CoreTypePrinter {
50        Self { handle }
51    }
52
53    pub fn printers() -> Array<CoreTypePrinter> {
54        let mut count = 0;
55        let result = unsafe { BNGetTypePrinterList(&mut count) };
56        assert!(!result.is_null());
57        unsafe { Array::new(result, count, ()) }
58    }
59
60    pub fn printer_by_name(name: &str) -> Option<CoreTypePrinter> {
61        let name_raw = name.to_cstr();
62        let result = unsafe { BNGetTypePrinterByName(name_raw.as_ptr()) };
63        NonNull::new(result).map(|x| unsafe { Self::from_raw(x) })
64    }
65
66    pub fn name(&self) -> String {
67        let result = unsafe { BNGetTypePrinterName(self.handle.as_ptr()) };
68        assert!(!result.is_null());
69        unsafe { BnString::into_string(result) }
70    }
71
72    pub fn get_type_tokens<T: Into<QualifiedName>>(
73        &self,
74        type_: &Type,
75        platform: &Platform,
76        name: T,
77        base_confidence: u8,
78        escaping: TokenEscapingType,
79    ) -> Option<Array<InstructionTextToken>> {
80        let mut result_count = 0;
81        let mut result = std::ptr::null_mut();
82        let mut raw_name = QualifiedName::into_raw(name.into());
83        let success = unsafe {
84            BNGetTypePrinterTypeTokens(
85                self.handle.as_ptr(),
86                type_.handle,
87                platform.handle,
88                &mut raw_name,
89                base_confidence,
90                escaping,
91                &mut result,
92                &mut result_count,
93            )
94        };
95        QualifiedName::free_raw(raw_name);
96        success.then(|| {
97            assert!(!result.is_null());
98            unsafe { Array::new(result, result_count, ()) }
99        })
100    }
101
102    pub fn get_type_tokens_before_name(
103        &self,
104        type_: &Type,
105        platform: &Platform,
106        base_confidence: u8,
107        parent_type: &Type,
108        escaping: TokenEscapingType,
109    ) -> Option<Array<InstructionTextToken>> {
110        let mut result_count = 0;
111        let mut result = std::ptr::null_mut();
112        let success = unsafe {
113            BNGetTypePrinterTypeTokensBeforeName(
114                self.handle.as_ptr(),
115                type_.handle,
116                platform.handle,
117                base_confidence,
118                parent_type.handle,
119                escaping,
120                &mut result,
121                &mut result_count,
122            )
123        };
124        success.then(|| {
125            assert!(!result.is_null());
126            unsafe { Array::new(result, result_count, ()) }
127        })
128    }
129
130    pub fn get_type_tokens_after_name(
131        &self,
132        type_: &Type,
133        platform: &Platform,
134        base_confidence: u8,
135        parent_type: &Type,
136        escaping: TokenEscapingType,
137    ) -> Option<Array<InstructionTextToken>> {
138        let mut result_count = 0;
139        let mut result = std::ptr::null_mut();
140        let success = unsafe {
141            BNGetTypePrinterTypeTokensAfterName(
142                self.handle.as_ptr(),
143                type_.handle,
144                platform.handle,
145                base_confidence,
146                parent_type.handle,
147                escaping,
148                &mut result,
149                &mut result_count,
150            )
151        };
152        success.then(|| {
153            assert!(!result.is_null());
154            unsafe { Array::new(result, result_count, ()) }
155        })
156    }
157
158    pub fn get_type_string<T: Into<QualifiedName>>(
159        &self,
160        type_: &Type,
161        platform: &Platform,
162        name: T,
163        escaping: TokenEscapingType,
164    ) -> Option<BnString> {
165        let mut result = std::ptr::null_mut();
166        let mut raw_name = QualifiedName::into_raw(name.into());
167        let success = unsafe {
168            BNGetTypePrinterTypeString(
169                self.handle.as_ptr(),
170                type_.handle,
171                platform.handle,
172                &mut raw_name,
173                escaping,
174                &mut result,
175            )
176        };
177        QualifiedName::free_raw(raw_name);
178        success.then(|| unsafe {
179            assert!(!result.is_null());
180            BnString::from_raw(result)
181        })
182    }
183
184    pub fn get_type_string_before_name(
185        &self,
186        type_: &Type,
187        platform: &Platform,
188        escaping: BNTokenEscapingType,
189    ) -> Option<BnString> {
190        let mut result = std::ptr::null_mut();
191        let success = unsafe {
192            BNGetTypePrinterTypeStringAfterName(
193                self.handle.as_ptr(),
194                type_.handle,
195                platform.handle,
196                escaping,
197                &mut result,
198            )
199        };
200        success.then(|| unsafe {
201            assert!(!result.is_null());
202            BnString::from_raw(result)
203        })
204    }
205
206    pub fn get_type_string_after_name(
207        &self,
208        type_: &Type,
209        platform: &Platform,
210        escaping: TokenEscapingType,
211    ) -> Option<BnString> {
212        let mut result = std::ptr::null_mut();
213        let success = unsafe {
214            BNGetTypePrinterTypeStringBeforeName(
215                self.handle.as_ptr(),
216                type_.handle,
217                platform.handle,
218                escaping,
219                &mut result,
220            )
221        };
222        success.then(|| unsafe {
223            assert!(!result.is_null());
224            BnString::from_raw(result)
225        })
226    }
227
228    pub fn get_type_lines<T: Into<QualifiedName>>(
229        &self,
230        type_: &Type,
231        types: &TypeContainer,
232        name: T,
233        padding_cols: isize,
234        collapsed: bool,
235        escaping: TokenEscapingType,
236    ) -> Option<Array<TypeDefinitionLine>> {
237        let mut result_count = 0;
238        let mut result = std::ptr::null_mut();
239        let mut raw_name = QualifiedName::into_raw(name.into());
240        let success = unsafe {
241            BNGetTypePrinterTypeLines(
242                self.handle.as_ptr(),
243                type_.handle,
244                types.handle.as_ptr(),
245                &mut raw_name,
246                padding_cols as c_int,
247                collapsed,
248                escaping,
249                &mut result,
250                &mut result_count,
251            )
252        };
253        QualifiedName::free_raw(raw_name);
254        success.then(|| {
255            assert!(!result.is_null());
256            unsafe { Array::<TypeDefinitionLine>::new(result, result_count, ()) }
257        })
258    }
259
260    /// Print all types to a single big string, including headers, sections, etc
261    ///
262    /// * `types` - All types to print
263    /// * `data` - Binary View in which all the types are defined
264    /// * `padding_cols` - Maximum number of bytes represented by each padding line
265    /// * `escaping` - Style of escaping literals which may not be parsable
266    pub fn default_print_all_types<T, I>(
267        &self,
268        types: T,
269        data: &BinaryView,
270        padding_cols: isize,
271        escaping: TokenEscapingType,
272    ) -> Option<BnString>
273    where
274        T: Iterator<Item = I>,
275        I: Into<QualifiedNameAndType>,
276    {
277        let mut result = std::ptr::null_mut();
278        let (mut raw_names, mut raw_types): (Vec<BNQualifiedName>, Vec<_>) = types
279            .map(|t| {
280                let t = t.into();
281                // Leak both to the core and then free afterwards.
282                (
283                    QualifiedName::into_raw(t.name),
284                    unsafe { Ref::into_raw(t.ty) }.handle,
285                )
286            })
287            .unzip();
288        let success = unsafe {
289            BNTypePrinterDefaultPrintAllTypes(
290                self.handle.as_ptr(),
291                raw_names.as_mut_ptr(),
292                raw_types.as_mut_ptr(),
293                raw_types.len(),
294                data.handle,
295                padding_cols as c_int,
296                escaping,
297                &mut result,
298            )
299        };
300        for raw_name in raw_names {
301            QualifiedName::free_raw(raw_name);
302        }
303        for raw_type in raw_types {
304            let _ = unsafe { Type::ref_from_raw(raw_type) };
305        }
306        success.then(|| unsafe {
307            assert!(!result.is_null());
308            BnString::from_raw(result)
309        })
310    }
311
312    pub fn print_all_types<T, I>(
313        &self,
314        types: T,
315        data: &BinaryView,
316        padding_cols: isize,
317        escaping: TokenEscapingType,
318    ) -> Option<BnString>
319    where
320        T: IntoIterator<Item = I>,
321        I: Into<QualifiedNameAndType>,
322    {
323        let mut result = std::ptr::null_mut();
324        // TODO: I dislike how this iter unzip looks like... but its how to avoid allocating again...
325        let (mut raw_names, mut raw_types): (Vec<BNQualifiedName>, Vec<_>) = types
326            .into_iter()
327            .map(|t| {
328                let t = t.into();
329                // Leak both to the core and then free afterwards.
330                (
331                    QualifiedName::into_raw(t.name),
332                    unsafe { Ref::into_raw(t.ty) }.handle,
333                )
334            })
335            .unzip();
336        let success = unsafe {
337            BNTypePrinterPrintAllTypes(
338                self.handle.as_ptr(),
339                raw_names.as_mut_ptr(),
340                raw_types.as_mut_ptr(),
341                raw_types.len(),
342                data.handle,
343                padding_cols as c_int,
344                escaping,
345                &mut result,
346            )
347        };
348        for raw_name in raw_names {
349            QualifiedName::free_raw(raw_name);
350        }
351        for raw_type in raw_types {
352            let _ = unsafe { Type::ref_from_raw(raw_type) };
353        }
354        success.then(|| unsafe {
355            assert!(!result.is_null());
356            BnString::from_raw(result)
357        })
358    }
359}
360
361impl Default for CoreTypePrinter {
362    fn default() -> Self {
363        // TODO: Remove this entirely, there is no "default", its view specific lets not make this some defined behavior.
364        let default_settings = crate::settings::Settings::new();
365        let name = default_settings.get_string("analysis.types.printerName");
366        Self::printer_by_name(&name).unwrap()
367    }
368}
369
370impl CoreArrayProvider for CoreTypePrinter {
371    type Raw = *mut BNTypePrinter;
372    type Context = ();
373    type Wrapped<'a> = Self;
374}
375
376unsafe impl CoreArrayProviderInner for CoreTypePrinter {
377    unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
378        BNFreeTypePrinterList(raw)
379    }
380
381    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
382        // TODO: Because handle is a NonNull we should prob make Self::Raw that as well...
383        let handle = NonNull::new(*raw).unwrap();
384        CoreTypePrinter::from_raw(handle)
385    }
386}
387
388pub trait TypePrinter {
389    /// Generate a single-line text representation of a type, Returns a List
390    /// of text tokens representing the type.
391    ///
392    /// * `type_` - Type to print
393    /// * `platform` - Platform responsible for this type
394    /// * `name` - Name of the type
395    /// * `base_confidence` - Confidence to use for tokens created for this type
396    /// * `escaping` - Style of escaping literals which may not be parsable
397    fn get_type_tokens<T: Into<QualifiedName>>(
398        &self,
399        type_: Ref<Type>,
400        platform: Option<Ref<Platform>>,
401        name: T,
402        base_confidence: u8,
403        escaping: TokenEscapingType,
404    ) -> Option<Vec<InstructionTextToken>>;
405
406    /// In a single-line text representation of a type, generate the tokens that
407    /// should be printed before the type's name. Returns a list of text tokens
408    /// representing the type
409    ///
410    /// * `type_` - Type to print
411    /// * `platform` - Platform responsible for this type
412    /// * `base_confidence` - Confidence to use for tokens created for this type
413    /// * `parent_type` - Type of the parent of this type, or None
414    /// * `escaping` - Style of escaping literals which may not be parsable
415    fn get_type_tokens_before_name(
416        &self,
417        type_: Ref<Type>,
418        platform: Option<Ref<Platform>>,
419        base_confidence: u8,
420        parent_type: Option<Ref<Type>>,
421        escaping: TokenEscapingType,
422    ) -> Option<Vec<InstructionTextToken>>;
423
424    /// In a single-line text representation of a type, generate the tokens
425    /// that should be printed after the type's name. Returns a list of text
426    /// tokens representing the type
427    ///
428    /// * `type_` - Type to print
429    /// * `platform` - Platform responsible for this type
430    /// * `base_confidence` - Confidence to use for tokens created for this type
431    /// * `parent_type` - Type of the parent of this type, or None
432    /// * `escaping` - Style of escaping literals which may not be parsable
433    fn get_type_tokens_after_name(
434        &self,
435        type_: Ref<Type>,
436        platform: Option<Ref<Platform>>,
437        base_confidence: u8,
438        parent_type: Option<Ref<Type>>,
439        escaping: TokenEscapingType,
440    ) -> Option<Vec<InstructionTextToken>>;
441
442    /// Generate a single-line text representation of a type. Returns a string
443    /// representing the type
444    ///
445    /// * `type_` - Type to print
446    /// * `platform` - Platform responsible for this type
447    /// * `name` - Name of the type
448    /// * `escaping` - Style of escaping literals which may not be parsable
449    fn get_type_string<T: Into<QualifiedName>>(
450        &self,
451        type_: Ref<Type>,
452        platform: Option<Ref<Platform>>,
453        name: T,
454        escaping: TokenEscapingType,
455    ) -> Option<String>;
456
457    /// In a single-line text representation of a type, generate the string that
458    /// should be printed before the type's name. Returns a string representing
459    /// the type
460    ///
461    /// * `type_` - Type to print
462    /// * `platform` - Platform responsible for this type
463    /// * `escaping` - Style of escaping literals which may not be parsable
464    fn get_type_string_before_name(
465        &self,
466        type_: Ref<Type>,
467        platform: Option<Ref<Platform>>,
468        escaping: TokenEscapingType,
469    ) -> Option<String>;
470
471    /// In a single-line text representation of a type, generate the string that
472    /// should be printed after the type's name. Returns a string representing
473    /// the type
474    ///
475    /// * `type_` - Type to print
476    /// * `platform` - Platform responsible for this type
477    /// * `escaping` - Style of escaping literals which may not be parsable
478    fn get_type_string_after_name(
479        &self,
480        type_: Ref<Type>,
481        platform: Option<Ref<Platform>>,
482        escaping: TokenEscapingType,
483    ) -> Option<String>;
484
485    /// Generate a multi-line representation of a type. Returns a list of type
486    /// definition lines
487    ///
488    /// * `type_` - Type to print
489    /// * `types` - Type Container containing the type and dependencies
490    /// * `name` - Name of the type
491    /// * `padding_cols` - Maximum number of bytes represented by each padding line
492    /// * `collapsed` - Whether to collapse structure/enum blocks
493    /// * `escaping` - Style of escaping literals which may not be parsable
494    fn get_type_lines<T: Into<QualifiedName>>(
495        &self,
496        type_: Ref<Type>,
497        types: &TypeContainer,
498        name: T,
499        padding_cols: isize,
500        collapsed: bool,
501        escaping: TokenEscapingType,
502    ) -> Option<Vec<TypeDefinitionLine>>;
503
504    /// Print all types to a single big string, including headers, sections,
505    /// etc.
506    ///
507    /// * `types` - All types to print
508    /// * `data` - Binary View in which all the types are defined
509    /// * `padding_cols` - Maximum number of bytes represented by each padding line
510    /// * `escaping` - Style of escaping literals which may not be parsable
511    fn print_all_types(
512        &self,
513        names: Vec<QualifiedName>,
514        types: Vec<Ref<Type>>,
515        data: Ref<BinaryView>,
516        padding_cols: isize,
517        escaping: TokenEscapingType,
518    ) -> Option<String>;
519}
520
521// TODO: This needs an extreme amount of documentation...
522#[derive(Clone)]
523pub struct TypeDefinitionLine {
524    pub line_type: TypeDefinitionLineType,
525    pub tokens: Vec<InstructionTextToken>,
526    pub ty: Ref<Type>,
527    pub parent_type: Option<Ref<Type>>,
528    // TODO: Document what the root type is.
529    pub root_type: Option<Ref<Type>>,
530    pub root_type_name: Option<String>,
531    // TODO: Document the base type, and why its a ntr instead of type + name like root type
532    pub base_type: Option<Ref<NamedTypeReference>>,
533    // TODO: These can also be optional?
534    pub base_offset: u64,
535    pub offset: u64,
536    pub field_index: usize,
537}
538
539impl TypeDefinitionLine {
540    pub(crate) fn from_raw(value: &BNTypeDefinitionLine) -> Self {
541        Self {
542            line_type: value.lineType,
543            tokens: {
544                let raw_tokens = unsafe { std::slice::from_raw_parts(value.tokens, value.count) };
545                raw_tokens
546                    .iter()
547                    .map(InstructionTextToken::from_raw)
548                    .collect()
549            },
550            ty: unsafe { Type::from_raw(value.type_).to_owned() },
551            parent_type: match value.parentType.is_null() {
552                false => Some(unsafe { Type::from_raw(value.parentType).to_owned() }),
553                true => None,
554            },
555            root_type: match value.rootType.is_null() {
556                false => Some(unsafe { Type::from_raw(value.rootType).to_owned() }),
557                true => None,
558            },
559            root_type_name: match value.rootTypeName.is_null() {
560                false => Some(raw_to_string(value.rootTypeName).unwrap()),
561                true => None,
562            },
563            base_type: match value.baseType.is_null() {
564                false => Some(unsafe { NamedTypeReference::from_raw(value.baseType).to_owned() }),
565                true => None,
566            },
567            base_offset: value.baseOffset,
568            offset: value.offset,
569            field_index: value.fieldIndex,
570        }
571    }
572
573    /// The raw value must have been allocated by rust. See [`Self::free_owned_raw`] for details.
574    pub(crate) fn from_owned_raw(value: BNTypeDefinitionLine) -> Self {
575        let owned = Self::from_raw(&value);
576        Self::free_owned_raw(value);
577        owned
578    }
579
580    pub(crate) fn into_raw(value: Self) -> BNTypeDefinitionLine {
581        // NOTE: This is leaking [BNInstructionTextToken::text], [BNInstructionTextToken::typeNames].
582        let tokens: Box<[BNInstructionTextToken]> = value
583            .tokens
584            .into_iter()
585            .map(InstructionTextToken::into_raw)
586            .collect();
587        BNTypeDefinitionLine {
588            lineType: value.line_type,
589            count: tokens.len(),
590            // NOTE: This is leaking tokens. Must free with `cb_free_lines`.
591            tokens: Box::leak(tokens).as_mut_ptr(),
592            // NOTE: This is leaking a ref to ty. Must free with `cb_free_lines`.
593            type_: unsafe { Ref::into_raw(value.ty) }.handle,
594            // NOTE: This is leaking a ref to parent_type. Must free with `cb_free_lines`.
595            parentType: value
596                .parent_type
597                .map(|t| unsafe { Ref::into_raw(t) }.handle)
598                .unwrap_or(std::ptr::null_mut()),
599            // NOTE: This is leaking a ref to root_type. Must free with `cb_free_lines`.
600            rootType: value
601                .root_type
602                .map(|t| unsafe { Ref::into_raw(t) }.handle)
603                .unwrap_or(std::ptr::null_mut()),
604            // NOTE: This is leaking root_type_name. Must free with `cb_free_lines`.
605            rootTypeName: value
606                .root_type_name
607                .map(|s| BnString::into_raw(BnString::new(s)))
608                .unwrap_or(std::ptr::null_mut()),
609            // NOTE: This is leaking a ref to base_type. Must free with `cb_free_lines`.
610            baseType: value
611                .base_type
612                .map(|t| unsafe { Ref::into_raw(t) }.handle)
613                .unwrap_or(std::ptr::null_mut()),
614            baseOffset: value.base_offset,
615            offset: value.offset,
616            fieldIndex: value.field_index,
617        }
618    }
619
620    /// This is unique from the typical `from_raw` as the allocation of InstructionTextToken requires it be from rust, hence the "owned" free.
621    pub(crate) fn free_owned_raw(raw: BNTypeDefinitionLine) {
622        if !raw.tokens.is_null() {
623            let tokens = std::ptr::slice_from_raw_parts_mut(raw.tokens, raw.count);
624            // SAFETY: raw.tokens must have been allocated by rust.
625            let boxed_tokens = unsafe { Box::from_raw(tokens) };
626            for token in boxed_tokens {
627                InstructionTextToken::free_raw(token);
628            }
629        }
630        if !raw.type_.is_null() {
631            // SAFETY: raw.type_ must have been ref incremented in conjunction with this free
632            let _ = unsafe { Type::ref_from_raw(raw.type_) };
633        }
634        if !raw.parentType.is_null() {
635            // SAFETY: raw.parentType must have been ref incremented in conjunction with this free
636            let _ = unsafe { Type::ref_from_raw(raw.parentType) };
637        }
638        if !raw.rootType.is_null() {
639            // SAFETY: raw.rootType must have been ref incremented in conjunction with this free
640            let _ = unsafe { Type::ref_from_raw(raw.rootType) };
641        }
642        if !raw.rootTypeName.is_null() {
643            // SAFETY: raw.rootTypeName must have been ref incremented in conjunction with this free
644            let _ = unsafe { BnString::from_raw(raw.rootTypeName) };
645        }
646        if !raw.baseType.is_null() {
647            // SAFETY: raw.baseType must have been ref incremented in conjunction with this free
648            let _ = unsafe { NamedTypeReference::ref_from_raw(raw.baseType) };
649        }
650    }
651}
652
653impl CoreArrayProvider for TypeDefinitionLine {
654    type Raw = BNTypeDefinitionLine;
655    type Context = ();
656    type Wrapped<'a> = Self;
657}
658
659unsafe impl CoreArrayProviderInner for TypeDefinitionLine {
660    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
661        unsafe { BNFreeTypeDefinitionLineList(raw, count) };
662    }
663
664    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
665        Self::from_raw(raw)
666    }
667}
668
669unsafe extern "C" fn cb_get_type_tokens<T: TypePrinter>(
670    ctxt: *mut ::std::os::raw::c_void,
671    type_: *mut BNType,
672    platform: *mut BNPlatform,
673    name: *mut BNQualifiedName,
674    base_confidence: u8,
675    escaping: BNTokenEscapingType,
676    result: *mut *mut BNInstructionTextToken,
677    result_count: *mut usize,
678) -> bool {
679    let ctxt: &mut T = &mut *(ctxt as *mut T);
680    // NOTE: The caller is responsible for freeing name.
681    let qualified_name = QualifiedName::from_raw(&*name);
682    let inner_result = ctxt.get_type_tokens(
683        unsafe { Type::ref_from_raw(type_) },
684        match platform.is_null() {
685            false => Some(Platform::ref_from_raw(platform)),
686            true => None,
687        },
688        qualified_name,
689        base_confidence,
690        escaping,
691    );
692    if let Some(inner_result) = inner_result {
693        let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result
694            .into_iter()
695            .map(InstructionTextToken::into_raw)
696            .collect();
697        *result_count = raw_text_tokens.len();
698        // NOTE: Dropped by the cb_free_tokens
699        *result = Box::leak(raw_text_tokens).as_mut_ptr();
700        true
701    } else {
702        *result = std::ptr::null_mut();
703        *result_count = 0;
704        false
705    }
706}
707
708unsafe extern "C" fn cb_get_type_tokens_before_name<T: TypePrinter>(
709    ctxt: *mut ::std::os::raw::c_void,
710    type_: *mut BNType,
711    platform: *mut BNPlatform,
712    base_confidence: u8,
713    parent_type: *mut BNType,
714    escaping: BNTokenEscapingType,
715    result: *mut *mut BNInstructionTextToken,
716    result_count: *mut usize,
717) -> bool {
718    let ctxt: &mut T = &mut *(ctxt as *mut T);
719    let inner_result = ctxt.get_type_tokens_before_name(
720        Type::ref_from_raw(type_),
721        match platform.is_null() {
722            false => Some(Platform::ref_from_raw(platform)),
723            true => None,
724        },
725        base_confidence,
726        match parent_type.is_null() {
727            false => Some(Type::ref_from_raw(parent_type)),
728            true => None,
729        },
730        escaping,
731    );
732    if let Some(inner_result) = inner_result {
733        let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result
734            .into_iter()
735            .map(InstructionTextToken::into_raw)
736            .collect();
737        *result_count = raw_text_tokens.len();
738        // NOTE: Dropped by the cb_free_tokens
739        *result = Box::leak(raw_text_tokens).as_mut_ptr();
740        true
741    } else {
742        *result = std::ptr::null_mut();
743        *result_count = 0;
744        false
745    }
746}
747
748unsafe extern "C" fn cb_get_type_tokens_after_name<T: TypePrinter>(
749    ctxt: *mut ::std::os::raw::c_void,
750    type_: *mut BNType,
751    platform: *mut BNPlatform,
752    base_confidence: u8,
753    parent_type: *mut BNType,
754    escaping: BNTokenEscapingType,
755    result: *mut *mut BNInstructionTextToken,
756    result_count: *mut usize,
757) -> bool {
758    let ctxt: &mut T = &mut *(ctxt as *mut T);
759    let inner_result = ctxt.get_type_tokens_after_name(
760        Type::ref_from_raw(type_),
761        match platform.is_null() {
762            false => Some(Platform::ref_from_raw(platform)),
763            true => None,
764        },
765        base_confidence,
766        match parent_type.is_null() {
767            false => Some(Type::ref_from_raw(parent_type)),
768            true => None,
769        },
770        escaping,
771    );
772    if let Some(inner_result) = inner_result {
773        let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result
774            .into_iter()
775            .map(InstructionTextToken::into_raw)
776            .collect();
777        *result_count = raw_text_tokens.len();
778        // NOTE: Dropped by the cb_free_tokens
779        *result = Box::leak(raw_text_tokens).as_mut_ptr();
780        true
781    } else {
782        *result = std::ptr::null_mut();
783        *result_count = 0;
784        false
785    }
786}
787
788unsafe extern "C" fn cb_get_type_string<T: TypePrinter>(
789    ctxt: *mut ::std::os::raw::c_void,
790    type_: *mut BNType,
791    platform: *mut BNPlatform,
792    name: *mut BNQualifiedName,
793    escaping: BNTokenEscapingType,
794    result: *mut *mut ::std::os::raw::c_char,
795) -> bool {
796    let ctxt: &mut T = &mut *(ctxt as *mut T);
797    // NOTE: The caller is responsible for freeing name.
798    let qualified_name = QualifiedName::from_raw(&*name);
799    let inner_result = ctxt.get_type_string(
800        Type::ref_from_raw(type_),
801        match platform.is_null() {
802            false => Some(Platform::ref_from_raw(platform)),
803            true => None,
804        },
805        qualified_name,
806        escaping,
807    );
808    if let Some(inner_result) = inner_result {
809        let raw_string = BnString::new(inner_result);
810        // NOTE: Dropped by `cb_free_string`
811        *result = BnString::into_raw(raw_string);
812        true
813    } else {
814        *result = std::ptr::null_mut();
815        false
816    }
817}
818
819unsafe extern "C" fn cb_get_type_string_before_name<T: TypePrinter>(
820    ctxt: *mut ::std::os::raw::c_void,
821    type_: *mut BNType,
822    platform: *mut BNPlatform,
823    escaping: BNTokenEscapingType,
824    result: *mut *mut ::std::os::raw::c_char,
825) -> bool {
826    let ctxt: &mut T = &mut *(ctxt as *mut T);
827    let inner_result = ctxt.get_type_string_before_name(
828        Type::ref_from_raw(type_),
829        match platform.is_null() {
830            false => Some(Platform::ref_from_raw(platform)),
831            true => None,
832        },
833        escaping,
834    );
835    if let Some(inner_result) = inner_result {
836        // NOTE: Dropped by `cb_free_string`
837        let raw_string = BnString::new(inner_result);
838        *result = BnString::into_raw(raw_string);
839        true
840    } else {
841        *result = std::ptr::null_mut();
842        false
843    }
844}
845
846unsafe extern "C" fn cb_get_type_string_after_name<T: TypePrinter>(
847    ctxt: *mut ::std::os::raw::c_void,
848    type_: *mut BNType,
849    platform: *mut BNPlatform,
850    escaping: BNTokenEscapingType,
851    result: *mut *mut ::std::os::raw::c_char,
852) -> bool {
853    let ctxt: &mut T = &mut *(ctxt as *mut T);
854    let inner_result = ctxt.get_type_string_after_name(
855        Type::ref_from_raw(type_),
856        match platform.is_null() {
857            false => Some(Platform::ref_from_raw(platform)),
858            true => None,
859        },
860        escaping,
861    );
862    if let Some(inner_result) = inner_result {
863        let raw_string = BnString::new(inner_result);
864        // NOTE: Dropped by `cb_free_string`
865        *result = BnString::into_raw(raw_string);
866        true
867    } else {
868        *result = std::ptr::null_mut();
869        false
870    }
871}
872
873unsafe extern "C" fn cb_get_type_lines<T: TypePrinter>(
874    ctxt: *mut ::std::os::raw::c_void,
875    type_: *mut BNType,
876    types: *mut BNTypeContainer,
877    name: *mut BNQualifiedName,
878    padding_cols: ::std::os::raw::c_int,
879    collapsed: bool,
880    escaping: BNTokenEscapingType,
881    result: *mut *mut BNTypeDefinitionLine,
882    result_count: *mut usize,
883) -> bool {
884    let ctxt: &mut T = &mut *(ctxt as *mut T);
885    // NOTE: The caller is responsible for freeing name.
886    let qualified_name = QualifiedName::from_raw(&*name);
887    let types_ptr = NonNull::new(types).unwrap();
888    let types = TypeContainer::from_raw(types_ptr);
889    let inner_result = ctxt.get_type_lines(
890        Type::ref_from_raw(type_),
891        &types,
892        qualified_name,
893        padding_cols as isize,
894        collapsed,
895        escaping,
896    );
897    if let Some(inner_result) = inner_result {
898        let boxed_raw_lines: Box<[_]> = inner_result
899            .into_iter()
900            .map(TypeDefinitionLine::into_raw)
901            .collect();
902        *result_count = boxed_raw_lines.len();
903        // NOTE: Dropped by `cb_free_lines`
904        *result = Box::leak(boxed_raw_lines).as_mut_ptr();
905        true
906    } else {
907        *result = std::ptr::null_mut();
908        *result_count = 0;
909        false
910    }
911}
912
913unsafe extern "C" fn cb_print_all_types<T: TypePrinter>(
914    ctxt: *mut ::std::os::raw::c_void,
915    names: *mut BNQualifiedName,
916    types: *mut *mut BNType,
917    type_count: usize,
918    data: *mut BNBinaryView,
919    padding_cols: ::std::os::raw::c_int,
920    escaping: BNTokenEscapingType,
921    result: *mut *mut ::std::os::raw::c_char,
922) -> bool {
923    let ctxt: &mut T = &mut *(ctxt as *mut T);
924    let raw_names = std::slice::from_raw_parts(names, type_count);
925    // NOTE: The caller is responsible for freeing raw_names.
926    let names: Vec<_> = raw_names.iter().map(QualifiedName::from_raw).collect();
927    let raw_types = std::slice::from_raw_parts(types, type_count);
928    // NOTE: The caller is responsible for freeing raw_types.
929    let types: Vec<_> = raw_types.iter().map(|&t| Type::ref_from_raw(t)).collect();
930    let inner_result = ctxt.print_all_types(
931        names,
932        types,
933        BinaryView::ref_from_raw(data),
934        padding_cols as isize,
935        escaping,
936    );
937    if let Some(inner_result) = inner_result {
938        let raw_string = BnString::new(inner_result);
939        // NOTE: Dropped by `cb_free_string`
940        *result = BnString::into_raw(raw_string);
941        true
942    } else {
943        *result = std::ptr::null_mut();
944        false
945    }
946}
947
948unsafe extern "C" fn cb_free_string(_ctxt: *mut c_void, string: *mut c_char) {
949    // SAFETY: The returned string is just BnString
950    BnString::free_raw(string);
951}
952
953unsafe extern "C" fn cb_free_tokens(
954    _ctxt: *mut ::std::os::raw::c_void,
955    tokens: *mut BNInstructionTextToken,
956    count: usize,
957) {
958    let tokens = std::ptr::slice_from_raw_parts_mut(tokens, count);
959    // SAFETY: tokens must have been allocated by rust.
960    let boxed_tokens = Box::from_raw(tokens);
961    for token in boxed_tokens {
962        InstructionTextToken::free_raw(token);
963    }
964}
965
966unsafe extern "C" fn cb_free_lines(
967    _ctxt: *mut ::std::os::raw::c_void,
968    lines: *mut BNTypeDefinitionLine,
969    count: usize,
970) {
971    let lines = std::ptr::slice_from_raw_parts_mut(lines, count);
972    // SAFETY: lines must have been allocated by rust.
973    let boxes_lines = Box::from_raw(lines);
974    for line in boxes_lines {
975        TypeDefinitionLine::free_owned_raw(line);
976    }
977}