binaryninja/high_level_il/
token_emitter.rs

1use std::ptr::NonNull;
2
3use binaryninjacore_sys::*;
4
5use crate::disassembly::{
6    DisassemblySettings, DisassemblyTextLine, InstructionTextToken, InstructionTextTokenContext,
7    InstructionTextTokenType,
8};
9use crate::high_level_il::HighLevelILFunction;
10use crate::language_representation::{OperatorPrecedence, SymbolDisplayResult, SymbolDisplayType};
11use crate::rc::{Array, Ref, RefCountable};
12use crate::variable::Variable;
13
14pub type ScopeType = BNScopeType;
15pub type TokenEmitterExpr = BNTokenEmitterExpr;
16pub type BraceRequirement = BNBraceRequirement;
17
18#[derive(PartialEq, Eq, Hash)]
19pub struct HighLevelILTokenEmitter {
20    handle: NonNull<BNHighLevelILTokenEmitter>,
21}
22
23impl HighLevelILTokenEmitter {
24    pub(crate) unsafe fn from_raw(handle: NonNull<BNHighLevelILTokenEmitter>) -> Self {
25        Self { handle }
26    }
27
28    /// Returns the list of [`InstructionTextToken`] on the current line.
29    pub fn current_tokens(&self) -> Array<InstructionTextToken> {
30        let mut count = 0;
31        let array =
32            unsafe { BNHighLevelILTokenEmitterGetCurrentTokens(self.handle.as_ptr(), &mut count) };
33        unsafe { Array::new(array, count, ()) }
34    }
35
36    /// Returns the list of [`DisassemblyTextLine`] in the output.
37    pub fn lines(&self) -> Array<DisassemblyTextLine> {
38        let mut count = 0;
39        let array = unsafe { BNHighLevelILTokenEmitterGetLines(self.handle.as_ptr(), &mut count) };
40        unsafe { Array::new(array, count, ()) }
41    }
42
43    pub fn prepend_collapse_blank_indicator(&self) {
44        unsafe { BNHighLevelILTokenPrependCollapseBlankIndicator(self.handle.as_ptr()) };
45    }
46
47    pub fn prepend_collapse_indicator(&self, context: InstructionTextTokenContext, hash: u64) {
48        unsafe {
49            BNHighLevelILTokenPrependCollapseIndicator(self.handle.as_ptr(), context.into(), hash)
50        };
51    }
52
53    pub fn has_collapsible_regions(&self) -> bool {
54        unsafe { BNHighLevelILTokenEmitterHasCollapsableRegions(self.handle.as_ptr()) }
55    }
56
57    pub fn set_has_collapsible_regions(&self, state: bool) {
58        unsafe { BNHighLevelILTokenEmitterSetHasCollapsableRegions(self.handle.as_ptr(), state) };
59    }
60
61    pub fn append(&self, token: InstructionTextToken) {
62        let mut raw_token = InstructionTextToken::into_raw(token);
63        unsafe { BNHighLevelILTokenEmitterAppend(self.handle.as_ptr(), &mut raw_token) };
64        InstructionTextToken::free_raw(raw_token);
65    }
66
67    /// Starts a new line in the output.
68    pub fn init_line(&self) {
69        unsafe { BNHighLevelILTokenEmitterInitLine(self.handle.as_ptr()) };
70    }
71
72    // TODO: Difference from `init_line`?
73    /// Starts a new line in the output.
74    pub fn new_line(&self) {
75        unsafe { BNHighLevelILTokenEmitterNewLine(self.handle.as_ptr()) };
76    }
77
78    /// Increases the indentation level by one.
79    pub fn increase_indent(&self) {
80        unsafe { BNHighLevelILTokenEmitterIncreaseIndent(self.handle.as_ptr()) };
81    }
82
83    /// Decreases the indentation level by one.
84    pub fn decrease_indent(&self) {
85        unsafe { BNHighLevelILTokenEmitterDecreaseIndent(self.handle.as_ptr()) };
86    }
87
88    /// Indicates that visual separation of scopes is desirable at the current position.
89    ///
90    /// By default, this will insert a blank line, but this can be configured by the user.
91    pub fn scope_separator(&self) {
92        unsafe { BNHighLevelILTokenEmitterScopeSeparator(self.handle.as_ptr()) };
93    }
94
95    /// Begins a new scope. Insertion of newlines and braces will be handled using the current settings.
96    pub fn begin_scope(&self, ty: ScopeType) {
97        unsafe { BNHighLevelILTokenEmitterBeginScope(self.handle.as_ptr(), ty) };
98    }
99
100    /// Ends the current scope.
101    ///
102    /// The type `ty` should be equal to what was passed to [`HighLevelILTokenEmitter::begin_scope`].
103    pub fn end_scope(&self, ty: ScopeType) {
104        unsafe { BNHighLevelILTokenEmitterEndScope(self.handle.as_ptr(), ty) };
105    }
106
107    /// Continues the previous scope with a new associated scope. This is most commonly used for else statements.
108    ///
109    /// If `force_same_line` is true, the continuation will always be placed on the same line as the previous scope.
110    pub fn scope_continuation(&self, force_same_line: bool) {
111        unsafe {
112            BNHighLevelILTokenEmitterScopeContinuation(self.handle.as_ptr(), force_same_line)
113        };
114    }
115
116    /// Finalizes the previous scope, indicating that there are no more associated scopes.
117    pub fn finalize_scope(&self) {
118        unsafe { BNHighLevelILTokenEmitterFinalizeScope(self.handle.as_ptr()) };
119    }
120
121    /// Forces there to be no indentation for the next line.
122    pub fn no_indent_for_this_line(&self) {
123        unsafe { BNHighLevelILTokenEmitterNoIndentForThisLine(self.handle.as_ptr()) };
124    }
125
126    /// Begins a region of tokens that always have zero confidence.
127    pub fn begin_force_zero_confidence(&self) {
128        unsafe { BNHighLevelILTokenEmitterBeginForceZeroConfidence(self.handle.as_ptr()) };
129    }
130
131    /// Ends a region of tokens that always have zero confidence.
132    pub fn end_force_zero_confidence(&self) {
133        unsafe { BNHighLevelILTokenEmitterEndForceZeroConfidence(self.handle.as_ptr()) };
134    }
135
136    /// Sets the current expression. Returning the [`CurrentTokenEmitterExpr`] which when dropped
137    /// will restore the previously active [`TokenEmitterExpr`].
138    pub fn set_current_expr(&self, expr: TokenEmitterExpr) -> CurrentTokenEmitterExpr {
139        let previous_expr =
140            unsafe { BNHighLevelILTokenEmitterSetCurrentExpr(self.handle.as_ptr(), expr) };
141        CurrentTokenEmitterExpr::new(self.to_owned(), expr, previous_expr)
142    }
143
144    fn restore_current_expr(&self, expr: TokenEmitterExpr) {
145        unsafe { BNHighLevelILTokenEmitterRestoreCurrentExpr(self.handle.as_ptr(), expr) };
146    }
147
148    /// Finalizes the outputted lines.
149    pub fn finalize(&self) {
150        unsafe { BNHighLevelILTokenEmitterFinalize(self.handle.as_ptr()) };
151    }
152
153    /// Appends `(`.
154    pub fn append_open_paren(&self) {
155        unsafe { BNHighLevelILTokenEmitterAppendOpenParen(self.handle.as_ptr()) };
156    }
157
158    /// Appends `)`.
159    pub fn append_close_paren(&self) {
160        unsafe { BNHighLevelILTokenEmitterAppendCloseParen(self.handle.as_ptr()) };
161    }
162
163    /// Appends `[`.
164    pub fn append_open_bracket(&self) {
165        unsafe { BNHighLevelILTokenEmitterAppendOpenBracket(self.handle.as_ptr()) };
166    }
167
168    /// Appends `]`.
169    pub fn append_close_bracket(&self) {
170        unsafe { BNHighLevelILTokenEmitterAppendCloseBracket(self.handle.as_ptr()) };
171    }
172
173    /// Appends `{`.
174    pub fn append_open_brace(&self) {
175        unsafe { BNHighLevelILTokenEmitterAppendOpenBrace(self.handle.as_ptr()) };
176    }
177
178    /// Appends `}`.
179    pub fn append_close_brace(&self) {
180        unsafe { BNHighLevelILTokenEmitterAppendCloseBrace(self.handle.as_ptr()) };
181    }
182
183    /// Appends `;`.
184    pub fn append_semicolon(&self) {
185        unsafe { BNHighLevelILTokenEmitterAppendSemicolon(self.handle.as_ptr()) };
186    }
187
188    /// Sets the requirement for insertion of braces around scopes in the output.
189    pub fn set_brace_requirement(&self, required: BraceRequirement) {
190        unsafe { BNHighLevelILTokenEmitterSetBraceRequirement(self.handle.as_ptr(), required) };
191    }
192
193    /// Sets whether cases within switch statements should always have braces around them.
194    pub fn set_braces_around_switch_cases(&self, braces: bool) {
195        unsafe {
196            BNHighLevelILTokenEmitterSetBracesAroundSwitchCases(self.handle.as_ptr(), braces)
197        };
198    }
199
200    /// Sets whether braces should default to being on the same line as the statement that begins the scope.
201    ///
202    /// If the user has explicitly set a preference, this setting will be ignored and the user's preference will be used instead.
203    pub fn set_default_braces_on_same_line(&self, same_line: bool) {
204        unsafe {
205            BNHighLevelILTokenEmitterSetDefaultBracesOnSameLine(self.handle.as_ptr(), same_line)
206        };
207    }
208
209    /// Sets whether omitting braces around single-line scopes is allowed.
210    pub fn set_simple_scope_allowed(&self, allowed: bool) {
211        unsafe { BNHighLevelILTokenEmitterSetSimpleScopeAllowed(self.handle.as_ptr(), allowed) };
212    }
213
214    pub fn brace_requirement(&self) -> BraceRequirement {
215        unsafe { BNHighLevelILTokenEmitterGetBraceRequirement(self.handle.as_ptr()) }
216    }
217
218    pub fn has_braces_around_switch_cases(&self) -> bool {
219        unsafe { BNHighLevelILTokenEmitterHasBracesAroundSwitchCases(self.handle.as_ptr()) }
220    }
221
222    pub fn default_braces_on_same_line(&self) -> bool {
223        unsafe { BNHighLevelILTokenEmitterGetDefaultBracesOnSameLine(self.handle.as_ptr()) }
224    }
225
226    pub fn is_simple_scope_allowed(&self) -> bool {
227        unsafe { BNHighLevelILTokenEmitterIsSimpleScopeAllowed(self.handle.as_ptr()) }
228    }
229
230    /// Appends a size token for the given size in the High Level IL syntax.
231    pub fn append_size_token(&self, size: usize, ty: InstructionTextTokenType) {
232        unsafe { BNAddHighLevelILSizeToken(size, ty, self.handle.as_ptr()) }
233    }
234
235    /// Appends a floating point size token for the given size in the High Level IL syntax.
236    pub fn append_float_size_token(&self, size: usize, ty: InstructionTextTokenType) {
237        unsafe { BNAddHighLevelILFloatSizeToken(size, ty, self.handle.as_ptr()) }
238    }
239
240    /// Appends tokens for access to a variable.
241    pub fn append_var_text_token(
242        &self,
243        func: &HighLevelILFunction,
244        var: Variable,
245        expr_index: usize,
246        size: usize,
247    ) {
248        unsafe {
249            BNAddHighLevelILVarTextToken(
250                func.handle,
251                &BNVariable::from(var),
252                self.handle.as_ptr(),
253                expr_index,
254                size,
255            )
256        }
257    }
258
259    /// Appends tokens for a constant integer value.
260    pub fn append_integer_text_token(
261        &self,
262        func: &HighLevelILFunction,
263        expr_index: usize,
264        val: i64,
265        size: usize,
266    ) {
267        unsafe {
268            BNAddHighLevelILIntegerTextToken(
269                func.handle,
270                expr_index,
271                val,
272                size,
273                self.handle.as_ptr(),
274            )
275        }
276    }
277
278    /// Appends tokens for accessing an array by constant index.
279    pub fn append_array_index_token(
280        &self,
281        func: &HighLevelILFunction,
282        expr_index: usize,
283        val: i64,
284        size: usize,
285        address: Option<u64>,
286    ) {
287        unsafe {
288            BNAddHighLevelILArrayIndexToken(
289                func.handle,
290                expr_index,
291                val,
292                size,
293                self.handle.as_ptr(),
294                address.unwrap_or(0),
295            )
296        }
297    }
298
299    /// Appends tokens for displaying a constant pointer value.
300    ///
301    /// If `allow_short_string` is true, then a string will be shown even if it is "short".
302    pub fn append_pointer_text_token(
303        &self,
304        func: &HighLevelILFunction,
305        expr_index: usize,
306        val: i64,
307        settings: &DisassemblySettings,
308        symbol_display: SymbolDisplayType,
309        precedence: OperatorPrecedence,
310        allow_short_string: bool,
311    ) -> SymbolDisplayResult {
312        unsafe {
313            BNAddHighLevelILPointerTextToken(
314                func.handle,
315                expr_index,
316                val,
317                self.handle.as_ptr(),
318                settings.handle,
319                symbol_display,
320                precedence,
321                allow_short_string,
322            )
323        }
324    }
325
326    /// Appends tokens for a constant value.
327    pub fn append_constant_text_token(
328        &self,
329        func: &HighLevelILFunction,
330        expr_index: usize,
331        val: i64,
332        size: usize,
333        settings: &DisassemblySettings,
334        precedence: OperatorPrecedence,
335    ) {
336        unsafe {
337            BNAddHighLevelILConstantTextToken(
338                func.handle,
339                expr_index,
340                val,
341                size,
342                self.handle.as_ptr(),
343                settings.handle,
344                precedence,
345            )
346        }
347    }
348}
349
350unsafe impl Send for HighLevelILTokenEmitter {}
351unsafe impl Sync for HighLevelILTokenEmitter {}
352
353unsafe impl RefCountable for HighLevelILTokenEmitter {
354    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
355        let handle = BNNewHighLevelILTokenEmitterReference(handle.handle.as_ptr());
356        let handle = NonNull::new(handle).unwrap();
357        Ref::new(HighLevelILTokenEmitter { handle })
358    }
359
360    unsafe fn dec_ref(handle: &Self) {
361        BNFreeHighLevelILTokenEmitter(handle.handle.as_ptr())
362    }
363}
364
365impl ToOwned for HighLevelILTokenEmitter {
366    type Owned = Ref<Self>;
367
368    fn to_owned(&self) -> Self::Owned {
369        unsafe { RefCountable::inc_ref(self) }
370    }
371}
372
373/// Manages the currently active [`TokenEmitterExpr`] for the given [`HighLevelILTokenEmitter`].
374///
375/// When this object is destroyed, the previously active [`TokenEmitterExpr`] will become active again.
376pub struct CurrentTokenEmitterExpr {
377    pub emitter: Ref<HighLevelILTokenEmitter>,
378    pub expr: TokenEmitterExpr,
379    pub previous_expr: TokenEmitterExpr,
380}
381
382impl CurrentTokenEmitterExpr {
383    pub fn new(
384        emitter: Ref<HighLevelILTokenEmitter>,
385        expr: TokenEmitterExpr,
386        previous_expr: TokenEmitterExpr,
387    ) -> Self {
388        Self {
389            emitter,
390            expr,
391            previous_expr,
392        }
393    }
394}
395
396impl Drop for CurrentTokenEmitterExpr {
397    fn drop(&mut self) {
398        self.emitter.restore_current_expr(self.previous_expr);
399    }
400}