binaryninja/
line_formatter.rs1use std::ffi::c_void;
2use std::ptr::NonNull;
3
4use binaryninjacore_sys::*;
5
6use crate::disassembly::DisassemblyTextLine;
7use crate::high_level_il::HighLevelILFunction;
8use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
9use crate::string::{raw_to_string, BnString, IntoCStr};
10
11pub fn register_line_formatter<C: LineFormatter>(name: &str, formatter: C) -> CoreLineFormatter {
13 let custom = Box::leak(Box::new(formatter));
14 let mut callbacks = BNCustomLineFormatter {
15 context: custom as *mut C as *mut c_void,
16 formatLines: Some(cb_format_lines::<C>),
17 freeLines: Some(cb_free_lines),
18 };
19 let name = name.to_cstr();
20 let handle = unsafe { BNRegisterLineFormatter(name.as_ptr(), &mut callbacks) };
21 CoreLineFormatter::from_raw(NonNull::new(handle).unwrap())
22}
23
24pub trait LineFormatter: Sized {
25 fn format_lines(
26 &self,
27 lines: &[DisassemblyTextLine],
28 settings: &LineFormatterSettings,
29 ) -> Vec<DisassemblyTextLine>;
30}
31
32#[repr(transparent)]
33pub struct CoreLineFormatter {
34 pub(crate) handle: NonNull<BNLineFormatter>,
35}
36
37impl CoreLineFormatter {
38 pub fn from_raw(handle: NonNull<BNLineFormatter>) -> Self {
39 Self { handle }
40 }
41
42 pub fn default_if_available() -> Option<Self> {
44 Some(unsafe { Self::from_raw(NonNull::new(BNGetDefaultLineFormatter())?) })
45 }
46
47 pub fn all() -> Array<CoreLineFormatter> {
48 let mut count = 0;
49 let result = unsafe { BNGetLineFormatterList(&mut count) };
50 unsafe { Array::new(result, count, ()) }
51 }
52
53 pub fn from_name(name: &str) -> Option<CoreLineFormatter> {
54 let name_raw = name.to_cstr();
55 let result = unsafe { BNGetLineFormatterByName(name_raw.as_ptr()) };
56 NonNull::new(result).map(Self::from_raw)
57 }
58
59 pub fn name(&self) -> BnString {
60 unsafe { BnString::from_raw(BNGetLineFormatterName(self.handle.as_ptr())) }
61 }
62}
63
64impl CoreArrayProvider for CoreLineFormatter {
65 type Raw = *mut BNLineFormatter;
66 type Context = ();
67 type Wrapped<'a> = Self;
68}
69
70unsafe impl CoreArrayProviderInner for CoreLineFormatter {
71 unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
72 BNFreeLineFormatterList(raw)
73 }
74
75 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
76 let handle = NonNull::new(*raw).unwrap();
78 CoreLineFormatter::from_raw(handle)
79 }
80}
81
82#[derive(Clone, Debug)]
83pub struct LineFormatterSettings {
84 pub high_level_il: Option<Ref<HighLevelILFunction>>,
85 pub desired_line_len: usize,
86 pub min_content_len: usize,
87 pub tab_width: usize,
88 pub lang_name: String,
89 pub comment_start: String,
90 pub comment_end: String,
91 pub annotation_start: String,
92 pub annotation_end: String,
93}
94
95impl LineFormatterSettings {
96 pub(crate) fn from_raw(value: &BNLineFormatterSettings) -> Self {
97 Self {
98 high_level_il: match value.highLevelIL.is_null() {
99 false => Some(
100 unsafe { HighLevelILFunction::from_raw(value.highLevelIL, false) }.to_owned(),
101 ),
102 true => None,
103 },
104 desired_line_len: value.desiredLineLength,
105 min_content_len: value.minimumContentLength,
106 tab_width: value.tabWidth,
107 lang_name: raw_to_string(value.languageName as *mut _).unwrap(),
108 comment_start: raw_to_string(value.commentStartString as *mut _).unwrap(),
109 comment_end: raw_to_string(value.commentEndString as *mut _).unwrap(),
110 annotation_start: raw_to_string(value.annotationStartString as *mut _).unwrap(),
111 annotation_end: raw_to_string(value.annotationEndString as *mut _).unwrap(),
112 }
113 }
114
115 #[allow(unused)]
116 pub(crate) fn from_owned_raw(value: BNLineFormatterSettings) -> Self {
117 let owned = Self::from_raw(&value);
118 Self::free_raw(value);
119 owned
120 }
121
122 #[allow(unused)]
123 pub(crate) fn free_raw(value: BNLineFormatterSettings) {
124 let _ = unsafe { HighLevelILFunction::ref_from_raw(value.highLevelIL, false) };
125 let _ = unsafe { BnString::from_raw(value.languageName as *mut _) };
126 let _ = unsafe { BnString::from_raw(value.commentStartString as *mut _) };
127 let _ = unsafe { BnString::from_raw(value.commentEndString as *mut _) };
128 let _ = unsafe { BnString::from_raw(value.annotationStartString as *mut _) };
129 let _ = unsafe { BnString::from_raw(value.annotationEndString as *mut _) };
130 }
131}
132
133unsafe extern "C" fn cb_format_lines<C: LineFormatter>(
134 ctxt: *mut c_void,
135 in_lines: *mut BNDisassemblyTextLine,
136 in_count: usize,
137 raw_settings: *const BNLineFormatterSettings,
138 out_count: *mut usize,
139) -> *mut BNDisassemblyTextLine {
140 let ctxt = ctxt as *mut C;
142 let lines_slice = core::slice::from_raw_parts(in_lines, in_count);
143 let lines: Vec<_> = lines_slice
144 .iter()
145 .map(DisassemblyTextLine::from_raw)
146 .collect();
147 let settings = LineFormatterSettings::from_raw(&*raw_settings);
148 let result = (*ctxt).format_lines(&lines, &settings);
149 *out_count = result.len();
150 let result: Box<[BNDisassemblyTextLine]> = result
151 .into_iter()
152 .map(DisassemblyTextLine::into_raw)
153 .collect();
154 Box::leak(result).as_mut_ptr()
155}
156
157unsafe extern "C" fn cb_free_lines(
158 _ctxt: *mut c_void,
159 raw_lines: *mut BNDisassemblyTextLine,
160 count: usize,
161) {
162 let lines: Box<[BNDisassemblyTextLine]> =
163 Box::from_raw(core::slice::from_raw_parts_mut(raw_lines, count));
164 for line in lines {
165 DisassemblyTextLine::free_raw(line);
166 }
167}