Skip to main content

binaryninja/
disassembly.rs

1// Copyright 2021-2026 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14#![allow(unused)]
15
16use binaryninjacore_sys::*;
17
18use crate::architecture::Architecture;
19use crate::architecture::CoreArchitecture;
20use crate::basic_block::BasicBlock;
21use crate::function::{Location, NativeBlock};
22use crate::low_level_il as llil;
23use crate::medium_level_il as mlil;
24use crate::string::IntoCStr;
25use crate::string::{raw_to_string, strings_to_string_list, BnString};
26use crate::{high_level_il as hlil, BN_INVALID_EXPR, BN_INVALID_OPERAND};
27
28use crate::rc::*;
29
30use crate::confidence::MAX_CONFIDENCE;
31use crate::function::{Function, HighlightColor};
32use crate::tags::Tag;
33use crate::types::Type;
34use crate::variable::StackVariableReference;
35
36use crate::binary_view::StringType;
37use crate::high_level_il::HighLevelILFunction;
38use crate::low_level_il::function::{FunctionForm, FunctionMutability, LowLevelILFunction};
39use crate::medium_level_il::MediumLevelILFunction;
40use crate::project::Project;
41use std::convert::From;
42use std::ffi;
43use std::fmt::{Display, Formatter};
44use std::ptr;
45use std::ptr::NonNull;
46
47pub type DisassemblyOption = BNDisassemblyOption;
48pub type InstructionTextTokenType = BNInstructionTextTokenType;
49
50#[derive(Clone, PartialEq, Debug, Default, Eq)]
51pub struct DisassemblyTextLine {
52    pub address: u64,
53    // TODO: This is not always available.
54    pub instruction_index: usize,
55    pub tokens: Vec<InstructionTextToken>,
56    pub highlight: HighlightColor,
57    pub tags: Vec<Ref<Tag>>,
58    pub type_info: DisassemblyTextLineTypeInfo,
59}
60
61impl DisassemblyTextLine {
62    pub(crate) fn from_raw(value: &BNDisassemblyTextLine) -> Self {
63        let raw_tokens = unsafe { std::slice::from_raw_parts(value.tokens, value.count) };
64        let tokens: Vec<_> = raw_tokens
65            .iter()
66            .map(InstructionTextToken::from_raw)
67            .collect();
68        // SAFETY: Increment the tag ref as we are going from ref to owned.
69        let raw_tags = unsafe { std::slice::from_raw_parts(value.tags, value.tagCount) };
70        let tags: Vec<_> = raw_tags
71            .iter()
72            .map(|&t| unsafe { Tag::from_raw(t) }.to_owned())
73            .collect();
74        Self {
75            address: value.addr,
76            instruction_index: value.instrIndex,
77            tokens,
78            highlight: value.highlight.into(),
79            tags,
80            type_info: DisassemblyTextLineTypeInfo::from_raw(&value.typeInfo),
81        }
82    }
83
84    /// Convert into a raw [BNDisassemblyTextLine], use with caution.
85    ///
86    /// NOTE: The allocations here for tokens and tags MUST be freed by rust using [Self::free_raw].
87    pub(crate) fn into_raw(value: Self) -> BNDisassemblyTextLine {
88        // NOTE: The instruction text and type names fields are being leaked here. To be freed with [Self::free_raw].
89        let tokens: Box<[BNInstructionTextToken]> = value
90            .tokens
91            .into_iter()
92            .map(InstructionTextToken::into_raw)
93            .collect();
94        let tags: Box<[*mut BNTag]> = value
95            .tags
96            .into_iter()
97            .map(|t| {
98                // SAFETY: The tags ref will be temporarily incremented here, until [Self::free_raw] is called.
99                // SAFETY: This is so that tags lifetime is long enough, as we might be the last holders of the ref.
100                unsafe { Ref::into_raw(t) }.handle
101            })
102            .collect();
103        BNDisassemblyTextLine {
104            addr: value.address,
105            instrIndex: value.instruction_index,
106            count: tokens.len(),
107            // NOTE: Leaking tokens here to be freed with [Self::free_raw].
108            tokens: Box::leak(tokens).as_mut_ptr(),
109            highlight: value.highlight.into(),
110            tagCount: tags.len(),
111            // NOTE: Leaking tags here to be freed with [Self::free_raw].
112            tags: Box::leak(tags).as_mut_ptr(),
113            typeInfo: DisassemblyTextLineTypeInfo::into_raw(value.type_info),
114        }
115    }
116
117    /// Frees raw object created with [Self::into_raw], use with caution.
118    ///
119    /// NOTE: The allocations freed MUST have been created in rust using [Self::into_raw].
120    pub(crate) fn free_raw(value: BNDisassemblyTextLine) {
121        // Free the token list
122        let raw_tokens = unsafe { std::slice::from_raw_parts_mut(value.tokens, value.count) };
123        let boxed_tokens = unsafe { Box::from_raw(raw_tokens) };
124        for token in boxed_tokens {
125            // SAFETY: As we have leaked the token contents we need to now free them (text and typeNames).
126            InstructionTextToken::free_raw(token);
127        }
128        // Free the tag list
129        let raw_tags = unsafe { std::slice::from_raw_parts_mut(value.tags, value.tagCount) };
130        let boxed_tags = unsafe { Box::from_raw(raw_tags) };
131        for tag in boxed_tags {
132            // SAFETY: As we have incremented the tags ref in [Self::into_raw] we must now decrement.
133            let _ = unsafe { Tag::ref_from_raw(tag) };
134        }
135        // Free the type info
136        DisassemblyTextLineTypeInfo::free_raw(value.typeInfo);
137    }
138
139    pub fn new(tokens: Vec<InstructionTextToken>) -> Self {
140        Self {
141            tokens,
142            ..Default::default()
143        }
144    }
145
146    pub fn new_with_addr(tokens: Vec<InstructionTextToken>, addr: u64) -> Self {
147        Self {
148            address: addr,
149            tokens,
150            ..Default::default()
151        }
152    }
153}
154
155impl From<&str> for DisassemblyTextLine {
156    fn from(value: &str) -> Self {
157        Self::new(vec![InstructionTextToken::new(
158            value,
159            InstructionTextTokenKind::Text,
160        )])
161    }
162}
163
164impl From<String> for DisassemblyTextLine {
165    fn from(value: String) -> Self {
166        Self::new(vec![InstructionTextToken::new(
167            value,
168            InstructionTextTokenKind::Text,
169        )])
170    }
171}
172
173impl Display for DisassemblyTextLine {
174    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
175        for token in &self.tokens {
176            write!(f, "{}", token)?;
177        }
178        Ok(())
179    }
180}
181
182impl CoreArrayProvider for DisassemblyTextLine {
183    type Raw = BNDisassemblyTextLine;
184    type Context = ();
185    type Wrapped<'a> = Self;
186}
187
188unsafe impl CoreArrayProviderInner for DisassemblyTextLine {
189    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
190        BNFreeDisassemblyTextLines(raw, count)
191    }
192
193    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
194        Self::from_raw(raw)
195    }
196}
197
198#[derive(Default, Clone, PartialEq, Eq, Debug, Hash)]
199pub struct DisassemblyTextLineTypeInfo {
200    pub has_type_info: bool,
201    pub parent_type: Option<Ref<Type>>,
202    pub field_index: usize,
203    pub offset: u64,
204}
205
206impl DisassemblyTextLineTypeInfo {
207    pub(crate) fn from_raw(value: &BNDisassemblyTextLineTypeInfo) -> Self {
208        Self {
209            has_type_info: value.hasTypeInfo,
210            parent_type: match value.parentType.is_null() {
211                false => Some(unsafe { Type::from_raw(value.parentType).to_owned() }),
212                true => None,
213            },
214            field_index: value.fieldIndex,
215            offset: value.offset,
216        }
217    }
218
219    pub(crate) fn from_owned_raw(value: BNDisassemblyTextLineTypeInfo) -> Self {
220        Self {
221            has_type_info: value.hasTypeInfo,
222            parent_type: match value.parentType.is_null() {
223                false => Some(unsafe { Type::ref_from_raw(value.parentType) }),
224                true => None,
225            },
226            field_index: value.fieldIndex,
227            offset: value.offset,
228        }
229    }
230
231    pub(crate) fn into_raw(value: Self) -> BNDisassemblyTextLineTypeInfo {
232        BNDisassemblyTextLineTypeInfo {
233            hasTypeInfo: value.has_type_info,
234            parentType: value
235                .parent_type
236                .map(|t| unsafe { Ref::into_raw(t) }.handle)
237                .unwrap_or(std::ptr::null_mut()),
238            fieldIndex: value.field_index,
239            offset: value.offset,
240        }
241    }
242
243    pub(crate) fn into_owned_raw(value: &Self) -> BNDisassemblyTextLineTypeInfo {
244        BNDisassemblyTextLineTypeInfo {
245            hasTypeInfo: value.has_type_info,
246            parentType: value
247                .parent_type
248                .as_ref()
249                .map(|t| t.handle)
250                .unwrap_or(std::ptr::null_mut()),
251            fieldIndex: value.field_index,
252            offset: value.offset,
253        }
254    }
255
256    pub(crate) fn free_raw(value: BNDisassemblyTextLineTypeInfo) {
257        if !value.parentType.is_null() {
258            let _ = unsafe { Type::ref_from_raw(value.parentType) };
259        }
260    }
261}
262
263#[derive(Debug, Clone, PartialEq, Eq)]
264pub struct InstructionTextToken {
265    pub address: u64,
266    pub text: String,
267    pub confidence: u8,
268    pub context: InstructionTextTokenContext,
269    pub expr_index: Option<usize>,
270    /// Native token type. Use [`Self::kind`] for the typed interpretation.
271    pub type_: InstructionTextTokenType,
272    /// Native value, including the unmodified payload bits of floating-point tokens.
273    pub value: u64,
274    /// Display width in columns. Zero selects the UTF-8 byte length of `text`.
275    pub width: u64,
276    pub size: usize,
277    pub operand: usize,
278    pub type_names: Vec<String>,
279}
280
281impl InstructionTextToken {
282    pub(crate) fn from_raw(value: &BNInstructionTextToken) -> Self {
283        let text = raw_to_string(value.text).unwrap();
284        let type_names = if value.namesCount == 0 {
285            Vec::new()
286        } else {
287            let names = unsafe { std::slice::from_raw_parts(value.typeNames, value.namesCount) };
288            names
289                .iter()
290                .filter_map(|&name| raw_to_string(name))
291                .collect()
292        };
293        Self {
294            address: value.address,
295            width: if value.width == 0 {
296                text.len() as u64
297            } else {
298                value.width
299            },
300            text,
301            confidence: value.confidence,
302            context: value.context.into(),
303            expr_index: match value.exprIndex {
304                BN_INVALID_EXPR => None,
305                index => Some(index),
306            },
307            type_: value.type_,
308            value: value.value,
309            size: value.size,
310            operand: value.operand,
311            type_names,
312        }
313    }
314
315    pub(crate) fn into_raw(value: Self) -> BNInstructionTextToken {
316        let bn_text = BnString::new(value.text);
317        BNInstructionTextToken {
318            type_: value.type_,
319            // NOTE: Expected to be freed with `InstructionTextToken::free_raw`.
320            text: BnString::into_raw(bn_text),
321            value: value.value,
322            width: value.width,
323            size: value.size,
324            operand: value.operand,
325            context: value.context.into(),
326            confidence: value.confidence,
327            address: value.address,
328            // NOTE: Expected to be freed with `InstructionTextToken::free_raw`.
329            typeNames: strings_to_string_list(&value.type_names),
330            namesCount: value.type_names.len(),
331            exprIndex: value.expr_index.unwrap_or(BN_INVALID_EXPR),
332        }
333    }
334
335    pub(crate) fn free_raw(value: BNInstructionTextToken) {
336        unsafe { BnString::free_raw(value.text) };
337        if !value.typeNames.is_null() {
338            unsafe { BNFreeStringList(value.typeNames, value.namesCount) };
339        }
340    }
341
342    /// Construct a new token **without** an associated address.
343    ///
344    /// You most likely want to call [`InstructionTextToken::new_with_address`], while also adjusting
345    /// the [`InstructionTextToken::expr_index`] field where applicable.
346    pub fn new(text: impl Into<String>, kind: InstructionTextTokenKind) -> Self {
347        Self::new_with_address(0, text, kind)
348    }
349
350    pub fn new_with_address(
351        address: u64,
352        text: impl Into<String>,
353        kind: InstructionTextTokenKind,
354    ) -> Self {
355        let text = text.into();
356        Self {
357            address,
358            width: text.len() as u64,
359            text,
360            confidence: MAX_CONFIDENCE,
361            context: InstructionTextTokenContext::Normal,
362            expr_index: None,
363            value: kind.try_value().unwrap_or(0),
364            size: kind.try_size().unwrap_or(0),
365            operand: kind.try_operand().unwrap_or(BN_INVALID_OPERAND),
366            type_names: kind.try_type_names().unwrap_or_default(),
367            type_: kind.into(),
368        }
369    }
370
371    /// Interprets the native fields as a typed token kind without discarding ancillary metadata.
372    /// Mutate the token's fields directly to change its metadata.
373    pub fn kind(&self) -> InstructionTextTokenKind {
374        InstructionTextTokenKind::from_token(self)
375    }
376}
377
378impl Display for InstructionTextToken {
379    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
380        self.text.fmt(f)
381    }
382}
383
384impl CoreArrayProvider for InstructionTextToken {
385    type Raw = BNInstructionTextToken;
386    type Context = ();
387    type Wrapped<'a> = Self;
388}
389
390unsafe impl CoreArrayProviderInner for InstructionTextToken {
391    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
392        // SAFETY: The Array MUST have been allocated on the core side. This will `delete[] raw`.
393        BNFreeInstructionText(raw, count)
394    }
395
396    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
397        Self::from_raw(raw)
398    }
399}
400
401impl CoreArrayProvider for Array<InstructionTextToken> {
402    type Raw = BNInstructionTextLine;
403    type Context = ();
404    type Wrapped<'a> = std::mem::ManuallyDrop<Self>;
405}
406
407unsafe impl CoreArrayProviderInner for Array<InstructionTextToken> {
408    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
409        // SAFETY: The Array MUST have been allocated on the core side. This will `delete[] raw`.
410        BNFreeInstructionTextLines(raw, count)
411    }
412
413    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
414        // TODO: This is insane.
415        std::mem::ManuallyDrop::new(Self::new(raw.tokens, raw.count, ()))
416    }
417}
418
419#[derive(Clone, PartialEq, Debug)]
420pub enum InstructionTextTokenKind {
421    Text,
422    Instruction,
423    /// Separator between operands, such as `,` or `+`.
424    ///
425    /// This is primarily used to identify the tokens associated with a given operand.
426    OperandSeparator,
427    Register,
428    Integer {
429        value: u64,
430        /// Size of the integer
431        size: Option<usize>,
432        /// The operand this integer is associated with.
433        ///
434        /// This is primarily used to change the display type of the integer.
435        ///
436        /// NOTE: This will be populated by a post-processing step when rendering, so you can leave this
437        /// as `None` when emitting in [`Architecture::instruction_text`] and other similar methods.
438        operand: Option<usize>,
439    },
440    PossibleAddress {
441        value: u64,
442        /// Size of the address
443        size: Option<usize>,
444        /// The operand this integer is associated with.
445        ///
446        /// This is primarily used to change the display type of the integer.
447        ///
448        /// NOTE: This will be populated by a post-processing step when rendering, so you can leave this
449        /// as `None` when emitting in [`Architecture::instruction_text`] and other similar methods.
450        operand: Option<usize>,
451    },
452    BeginMemoryOperand,
453    EndMemoryOperand,
454    FloatingPoint {
455        value: f64,
456        /// Size of the floating point
457        size: Option<usize>,
458    },
459    Annotation,
460    CodeRelativeAddress {
461        value: u64,
462        /// Size of the address
463        size: Option<usize>,
464        /// The operand this integer is associated with.
465        ///
466        /// This is primarily used to change the display type of the integer.
467        ///
468        /// NOTE: This will be populated by a post-processing step when rendering, so you can leave this
469        /// as `None` when emitting in [`Architecture::instruction_text`] and other similar methods.
470        operand: Option<usize>,
471    },
472    ArgumentName {
473        // TODO: The argument index?
474        value: u64,
475    },
476    HexDumpByteValue {
477        value: u8,
478    },
479    HexDumpSkippedByte,
480    HexDumpInvalidByte,
481    HexDumpText {
482        // TODO: Explain what this does
483        width: u64,
484    },
485    Opcode,
486    String {
487        // TODO: What is this?
488        // TODO: It seems like people just throw things in here...
489        value: u64,
490    },
491    /// String content is only present for:
492    /// - [`InstructionTextTokenContext::StringReference`]
493    /// - [`InstructionTextTokenContext::StringDisplay`]
494    StringContent {
495        ty: StringType,
496    },
497    CharacterConstant {
498        /// The operand this character is associated with.
499        ///
500        /// This is primarily used to change the display type of the character.
501        ///
502        /// NOTE: This will be populated by a post-processing step when rendering, so you can leave this
503        /// as `None` when emitting in [`Architecture::instruction_text`] and other similar methods.
504        operand: Option<usize>,
505    },
506    Keyword {
507        // Example usage can be found for `BNAnalysisWarningActionType`.
508        value: u64,
509    },
510    TypeName,
511    FieldName {
512        /// Offset to this field in the respective structure
513        offset: u64,
514        /// Stores the type names for the referenced field name.
515        ///
516        /// This is typically just the members name.
517        /// For example MyStructure.my_field will have type_names be \["my_field"\].
518        type_names: Vec<String>,
519    },
520    NameSpace,
521    NameSpaceSeparator,
522    Tag,
523    StructOffset {
524        /// Offset to this field in the respective structure
525        offset: u64,
526        // TODO: This makes no sense for struct offset, they dont have types?
527        /// Stores the type names for the referenced field name.
528        type_names: Vec<String>,
529    },
530    // TODO: Unused?
531    StructOffsetByteValue,
532    // TODO: Unused?
533    StructureHexDumpText {
534        // TODO: Explain what this does
535        width: u64,
536    },
537    GotoLabel {
538        target: u64,
539    },
540    Comment {
541        target: u64,
542    },
543    PossibleValue {
544        value: u64,
545    },
546    // TODO: This is weird, you pass the value type as the text, we should restrict this behavior and type it
547    PossibleValueType,
548    ArrayIndex {
549        index: u64,
550    },
551    Indentation,
552    UnknownMemory,
553    EnumerationMember {
554        value: u64,
555        // TODO: Document where this type id comes from
556        // TODO: Can we type this to something other than a string?
557        /// The enumerations type id
558        type_id: Option<String>,
559    },
560    /// Operations like +, -, %
561    Operation,
562    BaseStructureName,
563    BaseStructureSeparator,
564    Brace {
565        // TODO: Explain what this is
566        hash: Option<u64>,
567    },
568    ValueLocation,
569    CodeSymbol {
570        // Target address of the symbol
571        value: u64,
572        // TODO: Size of what?
573        size: usize, // TODO: Operand?
574    },
575    DataSymbol {
576        // Target address of the symbol
577        value: u64,
578        // TODO: Size of what?
579        size: usize, // TODO: Operand?
580    },
581    LocalVariable {
582        // This comes from the token.value
583        // TODO: Do we have a variable id type we can attach to this?
584        // TODO: Probably not considering this is used at multiple IL levels.
585        variable_id: u64,
586        /// NOTE: This is only valid in SSA form
587        ssa_version: usize,
588    },
589    Import {
590        // TODO: Looks to be the target address from the import.
591        target: u64,
592    },
593    AddressDisplay {
594        address: u64,
595    },
596    // TODO: BAD
597    IndirectImport {
598        /// The address of the import
599        ///
600        /// If you want the address of the import token use [`InstructionTextToken::address`] instead.
601        target: u64,
602        /// Size of the instruction this token is apart of
603        size: usize,
604        // TODO: Type this
605        source_operand: usize,
606    },
607    ExternalSymbol {
608        // TODO: Value of what?
609        value: u64,
610    },
611    StackVariable {
612        // TODO: Do we have a variable id type we can attach to this?
613        // TODO: Probably not considering this is used at multiple IL levels.
614        variable_id: u64,
615    },
616    AddressSeparator,
617    CollapsedInformation,
618    CollapseStateIndicator {
619        // TODO: Explain what this is
620        hash: Option<u64>,
621    },
622    NewLine {
623        // Offset into instruction that this new line is associated with
624        value: u64,
625    },
626}
627
628impl InstructionTextTokenKind {
629    fn from_token(value: &InstructionTextToken) -> Self {
630        match value.type_ {
631            BNInstructionTextTokenType::TextToken => Self::Text,
632            BNInstructionTextTokenType::InstructionToken => Self::Instruction,
633            BNInstructionTextTokenType::OperandSeparatorToken => Self::OperandSeparator,
634            BNInstructionTextTokenType::RegisterToken => Self::Register,
635            BNInstructionTextTokenType::IntegerToken => Self::Integer {
636                value: value.value,
637                size: match value.size {
638                    0 => None,
639                    size => Some(size),
640                },
641                operand: Some(value.operand),
642            },
643            BNInstructionTextTokenType::PossibleAddressToken => Self::PossibleAddress {
644                value: value.value,
645                size: match value.size {
646                    0 => None,
647                    size => Some(size),
648                },
649                operand: Some(value.operand),
650            },
651            BNInstructionTextTokenType::BeginMemoryOperandToken => Self::BeginMemoryOperand,
652            BNInstructionTextTokenType::EndMemoryOperandToken => Self::EndMemoryOperand,
653            BNInstructionTextTokenType::FloatingPointToken => Self::FloatingPoint {
654                value: f64::from_bits(value.value),
655                size: match value.size {
656                    0 => None,
657                    size => Some(size),
658                },
659            },
660            BNInstructionTextTokenType::AnnotationToken => Self::Annotation,
661            BNInstructionTextTokenType::CodeRelativeAddressToken => Self::CodeRelativeAddress {
662                value: value.value,
663                size: match value.size {
664                    0 => None,
665                    size => Some(size),
666                },
667                operand: Some(value.operand),
668            },
669            BNInstructionTextTokenType::ArgumentNameToken => {
670                Self::ArgumentName { value: value.value }
671            }
672            BNInstructionTextTokenType::HexDumpByteValueToken => Self::HexDumpByteValue {
673                value: value.value as u8,
674            },
675            BNInstructionTextTokenType::HexDumpSkippedByteToken => Self::HexDumpSkippedByte,
676            BNInstructionTextTokenType::HexDumpInvalidByteToken => Self::HexDumpInvalidByte,
677            BNInstructionTextTokenType::HexDumpTextToken => {
678                Self::HexDumpText { width: value.value }
679            }
680            BNInstructionTextTokenType::OpcodeToken => Self::Opcode,
681            BNInstructionTextTokenType::StringToken => match value.context.into() {
682                BNInstructionTextTokenContext::StringReferenceTokenContext
683                | BNInstructionTextTokenContext::StringDisplayTokenContext => {
684                    match value.value {
685                        0 => Self::StringContent {
686                            ty: StringType::AsciiString,
687                        },
688                        1 => Self::StringContent {
689                            ty: StringType::Utf8String,
690                        },
691                        2 => Self::StringContent {
692                            ty: StringType::Utf16String,
693                        },
694                        3 => Self::StringContent {
695                            ty: StringType::Utf32String,
696                        },
697                        // If we reach here all hope is lost.
698                        // Reaching here means someone made a ref or display context token with no
699                        // StringType and instead some other random value...
700                        value => Self::String { value },
701                    }
702                }
703                _ => Self::String { value: value.value },
704            },
705            BNInstructionTextTokenType::CharacterConstantToken => Self::CharacterConstant {
706                operand: Some(value.operand),
707            },
708            BNInstructionTextTokenType::KeywordToken => Self::Keyword { value: value.value },
709            BNInstructionTextTokenType::TypeNameToken => Self::TypeName,
710            BNInstructionTextTokenType::FieldNameToken => Self::FieldName {
711                offset: value.value,
712                type_names: value.type_names.clone(),
713            },
714            BNInstructionTextTokenType::NameSpaceToken => Self::NameSpace,
715            BNInstructionTextTokenType::NameSpaceSeparatorToken => Self::NameSpaceSeparator,
716            BNInstructionTextTokenType::TagToken => Self::Tag,
717            BNInstructionTextTokenType::StructOffsetToken => Self::StructOffset {
718                offset: value.value,
719                type_names: value.type_names.clone(),
720            },
721            BNInstructionTextTokenType::StructOffsetByteValueToken => Self::StructOffsetByteValue,
722            BNInstructionTextTokenType::StructureHexDumpTextToken => {
723                Self::StructureHexDumpText { width: value.value }
724            }
725            BNInstructionTextTokenType::GotoLabelToken => Self::GotoLabel {
726                target: value.value,
727            },
728            BNInstructionTextTokenType::CommentToken => Self::Comment {
729                target: value.value,
730            },
731            BNInstructionTextTokenType::PossibleValueToken => {
732                Self::PossibleValue { value: value.value }
733            }
734            // NOTE: See my comment about this type in [`Self::PossibleValueType`]
735            BNInstructionTextTokenType::PossibleValueTypeToken => Self::PossibleValueType,
736            BNInstructionTextTokenType::ArrayIndexToken => Self::ArrayIndex { index: value.value },
737            BNInstructionTextTokenType::IndentationToken => Self::Indentation,
738            BNInstructionTextTokenType::UnknownMemoryToken => Self::UnknownMemory,
739            BNInstructionTextTokenType::EnumerationMemberToken => Self::EnumerationMember {
740                value: value.value,
741                type_id: value.type_names.first().cloned(),
742            },
743            BNInstructionTextTokenType::OperationToken => Self::Operation,
744            BNInstructionTextTokenType::BaseStructureNameToken => Self::BaseStructureName,
745            BNInstructionTextTokenType::BaseStructureSeparatorToken => Self::BaseStructureSeparator,
746            BNInstructionTextTokenType::BraceToken => Self::Brace {
747                hash: match value.value {
748                    0 => None,
749                    hash => Some(hash),
750                },
751            },
752            BNInstructionTextTokenType::ValueLocationToken => Self::ValueLocation,
753            BNInstructionTextTokenType::CodeSymbolToken => Self::CodeSymbol {
754                value: value.value,
755                size: value.size,
756            },
757            BNInstructionTextTokenType::DataSymbolToken => Self::DataSymbol {
758                value: value.value,
759                size: value.size,
760            },
761            BNInstructionTextTokenType::LocalVariableToken => Self::LocalVariable {
762                variable_id: value.value,
763                ssa_version: value.operand,
764            },
765            BNInstructionTextTokenType::ImportToken => Self::Import {
766                target: value.value,
767            },
768            BNInstructionTextTokenType::AddressDisplayToken => Self::AddressDisplay {
769                address: value.value,
770            },
771            BNInstructionTextTokenType::IndirectImportToken => Self::IndirectImport {
772                target: value.value,
773                size: value.size,
774                source_operand: value.operand,
775            },
776            BNInstructionTextTokenType::ExternalSymbolToken => {
777                Self::ExternalSymbol { value: value.value }
778            }
779            BNInstructionTextTokenType::StackVariableToken => Self::StackVariable {
780                variable_id: value.value,
781            },
782            BNInstructionTextTokenType::AddressSeparatorToken => Self::AddressSeparator,
783            BNInstructionTextTokenType::CollapsedInformationToken => Self::CollapsedInformation,
784            BNInstructionTextTokenType::CollapseStateIndicatorToken => {
785                Self::CollapseStateIndicator {
786                    hash: match value.value {
787                        0 => None,
788                        hash => Some(hash),
789                    },
790                }
791            }
792            BNInstructionTextTokenType::NewLineToken => Self::NewLine { value: value.value },
793        }
794    }
795
796    /// Mapping to the [`BNInstructionTextTokenType::value`] field.
797    fn try_value(&self) -> Option<u64> {
798        // TODO: Double check to make sure these are correct.
799        match self {
800            InstructionTextTokenKind::Integer { value, .. } => Some(*value),
801            InstructionTextTokenKind::PossibleAddress { value, .. } => Some(*value),
802            InstructionTextTokenKind::PossibleValue { value, .. } => Some(*value),
803            InstructionTextTokenKind::FloatingPoint { value, .. } => Some(value.to_bits()),
804            InstructionTextTokenKind::CodeRelativeAddress { value, .. } => Some(*value),
805            InstructionTextTokenKind::ArgumentName { value, .. } => Some(*value),
806            InstructionTextTokenKind::HexDumpByteValue { value, .. } => Some(*value as u64),
807            InstructionTextTokenKind::HexDumpText { width, .. } => Some(*width),
808            InstructionTextTokenKind::String { value, .. } => Some(*value),
809            InstructionTextTokenKind::StringContent { ty, .. } => Some(*ty as u64),
810            InstructionTextTokenKind::Keyword { value, .. } => Some(*value),
811            InstructionTextTokenKind::FieldName { offset, .. } => Some(*offset),
812            InstructionTextTokenKind::StructOffset { offset, .. } => Some(*offset),
813            InstructionTextTokenKind::StructureHexDumpText { width, .. } => Some(*width),
814            InstructionTextTokenKind::GotoLabel { target, .. } => Some(*target),
815            InstructionTextTokenKind::Comment { target, .. } => Some(*target),
816            InstructionTextTokenKind::ArrayIndex { index, .. } => Some(*index),
817            InstructionTextTokenKind::EnumerationMember { value, .. } => Some(*value),
818            InstructionTextTokenKind::LocalVariable { variable_id, .. } => Some(*variable_id),
819            InstructionTextTokenKind::Import { target, .. } => Some(*target),
820            InstructionTextTokenKind::AddressDisplay { address, .. } => Some(*address),
821            InstructionTextTokenKind::IndirectImport { target, .. } => Some(*target),
822            InstructionTextTokenKind::Brace { hash, .. } => *hash,
823            InstructionTextTokenKind::CodeSymbol { value, .. } => Some(*value),
824            InstructionTextTokenKind::DataSymbol { value, .. } => Some(*value),
825            InstructionTextTokenKind::ExternalSymbol { value, .. } => Some(*value),
826            InstructionTextTokenKind::StackVariable { variable_id, .. } => Some(*variable_id),
827            InstructionTextTokenKind::CollapseStateIndicator { hash, .. } => *hash,
828            InstructionTextTokenKind::NewLine { value, .. } => Some(*value),
829            _ => None,
830        }
831    }
832
833    /// Mapping to the [`BNInstructionTextTokenType::size`] field.
834    fn try_size(&self) -> Option<usize> {
835        match self {
836            InstructionTextTokenKind::Integer { size, .. } => *size,
837            InstructionTextTokenKind::FloatingPoint { size, .. } => *size,
838            InstructionTextTokenKind::PossibleAddress { size, .. } => *size,
839            InstructionTextTokenKind::CodeRelativeAddress { size, .. } => *size,
840            InstructionTextTokenKind::CodeSymbol { size, .. } => Some(*size),
841            InstructionTextTokenKind::DataSymbol { size, .. } => Some(*size),
842            InstructionTextTokenKind::IndirectImport { size, .. } => Some(*size),
843            _ => None,
844        }
845    }
846
847    /// Mapping to the [`BNInstructionTextTokenType::operand`] field.
848    fn try_operand(&self) -> Option<usize> {
849        match self {
850            InstructionTextTokenKind::Integer { operand, .. } => *operand,
851            InstructionTextTokenKind::PossibleAddress { operand, .. } => *operand,
852            InstructionTextTokenKind::CodeRelativeAddress { operand, .. } => *operand,
853            InstructionTextTokenKind::CharacterConstant { operand, .. } => *operand,
854            InstructionTextTokenKind::LocalVariable { ssa_version, .. } => Some(*ssa_version),
855            InstructionTextTokenKind::IndirectImport { source_operand, .. } => {
856                Some(*source_operand)
857            }
858            _ => None,
859        }
860    }
861
862    /// Mapping to the [`BNInstructionTextTokenType::typeNames`] field.
863    fn try_type_names(&self) -> Option<Vec<String>> {
864        match self {
865            InstructionTextTokenKind::FieldName { type_names, .. } => Some(type_names.clone()),
866            InstructionTextTokenKind::StructOffset { type_names, .. } => Some(type_names.clone()),
867            InstructionTextTokenKind::EnumerationMember { type_id, .. } => {
868                Some(vec![type_id.clone()?])
869            }
870            _ => None,
871        }
872    }
873}
874
875impl From<InstructionTextTokenKind> for BNInstructionTextTokenType {
876    fn from(value: InstructionTextTokenKind) -> Self {
877        match value {
878            InstructionTextTokenKind::Text => BNInstructionTextTokenType::TextToken,
879            InstructionTextTokenKind::Instruction => BNInstructionTextTokenType::InstructionToken,
880            InstructionTextTokenKind::OperandSeparator => {
881                BNInstructionTextTokenType::OperandSeparatorToken
882            }
883            InstructionTextTokenKind::Register => BNInstructionTextTokenType::RegisterToken,
884            InstructionTextTokenKind::Integer { .. } => BNInstructionTextTokenType::IntegerToken,
885            InstructionTextTokenKind::PossibleAddress { .. } => {
886                BNInstructionTextTokenType::PossibleAddressToken
887            }
888            InstructionTextTokenKind::BeginMemoryOperand => {
889                BNInstructionTextTokenType::BeginMemoryOperandToken
890            }
891            InstructionTextTokenKind::EndMemoryOperand => {
892                BNInstructionTextTokenType::EndMemoryOperandToken
893            }
894            InstructionTextTokenKind::FloatingPoint { .. } => {
895                BNInstructionTextTokenType::FloatingPointToken
896            }
897            InstructionTextTokenKind::Annotation => BNInstructionTextTokenType::AnnotationToken,
898            InstructionTextTokenKind::CodeRelativeAddress { .. } => {
899                BNInstructionTextTokenType::CodeRelativeAddressToken
900            }
901            InstructionTextTokenKind::ArgumentName { .. } => {
902                BNInstructionTextTokenType::ArgumentNameToken
903            }
904            InstructionTextTokenKind::HexDumpByteValue { .. } => {
905                BNInstructionTextTokenType::HexDumpByteValueToken
906            }
907            InstructionTextTokenKind::HexDumpSkippedByte => {
908                BNInstructionTextTokenType::HexDumpSkippedByteToken
909            }
910            InstructionTextTokenKind::HexDumpInvalidByte => {
911                BNInstructionTextTokenType::HexDumpInvalidByteToken
912            }
913            InstructionTextTokenKind::HexDumpText { .. } => {
914                BNInstructionTextTokenType::HexDumpTextToken
915            }
916            InstructionTextTokenKind::Opcode => BNInstructionTextTokenType::OpcodeToken,
917            InstructionTextTokenKind::String { .. } => BNInstructionTextTokenType::StringToken,
918            InstructionTextTokenKind::StringContent { .. } => {
919                BNInstructionTextTokenType::StringToken
920            }
921            InstructionTextTokenKind::CharacterConstant { .. } => {
922                BNInstructionTextTokenType::CharacterConstantToken
923            }
924            InstructionTextTokenKind::Keyword { .. } => BNInstructionTextTokenType::KeywordToken,
925            InstructionTextTokenKind::TypeName => BNInstructionTextTokenType::TypeNameToken,
926            InstructionTextTokenKind::FieldName { .. } => {
927                BNInstructionTextTokenType::FieldNameToken
928            }
929            InstructionTextTokenKind::NameSpace => BNInstructionTextTokenType::NameSpaceToken,
930            InstructionTextTokenKind::NameSpaceSeparator => {
931                BNInstructionTextTokenType::NameSpaceSeparatorToken
932            }
933            InstructionTextTokenKind::Tag => BNInstructionTextTokenType::TagToken,
934            InstructionTextTokenKind::StructOffset { .. } => {
935                BNInstructionTextTokenType::StructOffsetToken
936            }
937            InstructionTextTokenKind::StructOffsetByteValue => {
938                BNInstructionTextTokenType::StructOffsetByteValueToken
939            }
940            InstructionTextTokenKind::StructureHexDumpText { .. } => {
941                BNInstructionTextTokenType::StructureHexDumpTextToken
942            }
943            InstructionTextTokenKind::GotoLabel { .. } => {
944                BNInstructionTextTokenType::GotoLabelToken
945            }
946            InstructionTextTokenKind::Comment { .. } => BNInstructionTextTokenType::CommentToken,
947            InstructionTextTokenKind::PossibleValue { .. } => {
948                BNInstructionTextTokenType::PossibleValueToken
949            }
950            InstructionTextTokenKind::PossibleValueType => {
951                BNInstructionTextTokenType::PossibleValueTypeToken
952            }
953            InstructionTextTokenKind::ArrayIndex { .. } => {
954                BNInstructionTextTokenType::ArrayIndexToken
955            }
956            InstructionTextTokenKind::Indentation => BNInstructionTextTokenType::IndentationToken,
957            InstructionTextTokenKind::UnknownMemory => {
958                BNInstructionTextTokenType::UnknownMemoryToken
959            }
960            InstructionTextTokenKind::EnumerationMember { .. } => {
961                BNInstructionTextTokenType::EnumerationMemberToken
962            }
963            InstructionTextTokenKind::Operation => BNInstructionTextTokenType::OperationToken,
964            InstructionTextTokenKind::BaseStructureName => {
965                BNInstructionTextTokenType::BaseStructureNameToken
966            }
967            InstructionTextTokenKind::BaseStructureSeparator => {
968                BNInstructionTextTokenType::BaseStructureSeparatorToken
969            }
970            InstructionTextTokenKind::Brace { .. } => BNInstructionTextTokenType::BraceToken,
971            InstructionTextTokenKind::ValueLocation => {
972                BNInstructionTextTokenType::ValueLocationToken
973            }
974            InstructionTextTokenKind::CodeSymbol { .. } => {
975                BNInstructionTextTokenType::CodeSymbolToken
976            }
977            InstructionTextTokenKind::DataSymbol { .. } => {
978                BNInstructionTextTokenType::DataSymbolToken
979            }
980            InstructionTextTokenKind::LocalVariable { .. } => {
981                BNInstructionTextTokenType::LocalVariableToken
982            }
983            InstructionTextTokenKind::Import { .. } => BNInstructionTextTokenType::ImportToken,
984            InstructionTextTokenKind::AddressDisplay { .. } => {
985                BNInstructionTextTokenType::AddressDisplayToken
986            }
987            InstructionTextTokenKind::IndirectImport { .. } => {
988                BNInstructionTextTokenType::IndirectImportToken
989            }
990            InstructionTextTokenKind::ExternalSymbol { .. } => {
991                BNInstructionTextTokenType::ExternalSymbolToken
992            }
993            InstructionTextTokenKind::StackVariable { .. } => {
994                BNInstructionTextTokenType::StackVariableToken
995            }
996            InstructionTextTokenKind::AddressSeparator => {
997                BNInstructionTextTokenType::AddressSeparatorToken
998            }
999            InstructionTextTokenKind::CollapsedInformation => {
1000                BNInstructionTextTokenType::CollapsedInformationToken
1001            }
1002            InstructionTextTokenKind::CollapseStateIndicator { .. } => {
1003                BNInstructionTextTokenType::CollapseStateIndicatorToken
1004            }
1005            InstructionTextTokenKind::NewLine { .. } => BNInstructionTextTokenType::NewLineToken,
1006        }
1007    }
1008}
1009
1010impl Eq for InstructionTextTokenKind {}
1011
1012#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1013pub enum InstructionTextTokenContext {
1014    Normal,
1015    LocalVariable,
1016    DataVariable,
1017    FunctionReturn,
1018    InstructionAddress,
1019    ILInstructionIndex,
1020    ConstData,
1021    /// Use only with [`InstructionTextTokenKind::String`]
1022    ConstStringData,
1023    /// Use only with [`InstructionTextTokenKind::String`]
1024    StringReference,
1025    /// Use only with [`InstructionTextTokenKind::String`]
1026    StringDataVariable,
1027    /// For displaying strings which aren't associated with an address
1028    ///
1029    /// Use only with [`InstructionTextTokenKind::String`]
1030    StringDisplay,
1031    /// Use only with [`InstructionTextTokenKind::CollapseStateIndicator`]
1032    Collapsed,
1033    /// Use only with [`InstructionTextTokenKind::CollapseStateIndicator`]
1034    Expanded,
1035    /// Use only with [`InstructionTextTokenKind::CollapseStateIndicator`]
1036    CollapsiblePadding,
1037    /// Use only with [`InstructionTextTokenKind::String`]
1038    DerivedStringReference,
1039}
1040
1041impl From<BNInstructionTextTokenContext> for InstructionTextTokenContext {
1042    fn from(value: BNInstructionTextTokenContext) -> Self {
1043        match value {
1044            BNInstructionTextTokenContext::NoTokenContext => Self::Normal,
1045            BNInstructionTextTokenContext::LocalVariableTokenContext => Self::LocalVariable,
1046            BNInstructionTextTokenContext::DataVariableTokenContext => Self::DataVariable,
1047            BNInstructionTextTokenContext::FunctionReturnTokenContext => Self::FunctionReturn,
1048            BNInstructionTextTokenContext::InstructionAddressTokenContext => {
1049                Self::InstructionAddress
1050            }
1051            BNInstructionTextTokenContext::ILInstructionIndexTokenContext => {
1052                Self::ILInstructionIndex
1053            }
1054            BNInstructionTextTokenContext::ConstDataTokenContext => Self::ConstData,
1055            // For use with [`InstructionTextTokenKind::String`]
1056            BNInstructionTextTokenContext::ConstStringDataTokenContext => Self::ConstStringData,
1057            BNInstructionTextTokenContext::StringReferenceTokenContext => Self::StringReference,
1058            BNInstructionTextTokenContext::StringDataVariableTokenContext => {
1059                Self::StringDataVariable
1060            }
1061            BNInstructionTextTokenContext::StringDisplayTokenContext => Self::StringDisplay,
1062            // For use with [`InstructionTextTokenKind::CollapseStateIndicator`]
1063            BNInstructionTextTokenContext::ContentCollapsedContext => Self::Collapsed,
1064            BNInstructionTextTokenContext::ContentExpandedContext => Self::Expanded,
1065            BNInstructionTextTokenContext::ContentCollapsiblePadding => Self::CollapsiblePadding,
1066            BNInstructionTextTokenContext::DerivedStringReferenceTokenContext => {
1067                Self::DerivedStringReference
1068            }
1069        }
1070    }
1071}
1072
1073impl From<InstructionTextTokenContext> for BNInstructionTextTokenContext {
1074    fn from(value: InstructionTextTokenContext) -> Self {
1075        match value {
1076            InstructionTextTokenContext::Normal => Self::NoTokenContext,
1077            InstructionTextTokenContext::LocalVariable => Self::LocalVariableTokenContext,
1078            InstructionTextTokenContext::DataVariable => Self::DataVariableTokenContext,
1079            InstructionTextTokenContext::FunctionReturn => Self::FunctionReturnTokenContext,
1080            InstructionTextTokenContext::InstructionAddress => Self::InstructionAddressTokenContext,
1081            InstructionTextTokenContext::ILInstructionIndex => Self::ILInstructionIndexTokenContext,
1082            InstructionTextTokenContext::ConstData => Self::ConstDataTokenContext,
1083            InstructionTextTokenContext::ConstStringData => Self::ConstStringDataTokenContext,
1084            InstructionTextTokenContext::StringReference => Self::StringReferenceTokenContext,
1085            InstructionTextTokenContext::StringDataVariable => Self::StringDataVariableTokenContext,
1086            InstructionTextTokenContext::StringDisplay => Self::StringDisplayTokenContext,
1087            InstructionTextTokenContext::Collapsed => Self::ContentCollapsedContext,
1088            InstructionTextTokenContext::Expanded => Self::ContentExpandedContext,
1089            InstructionTextTokenContext::CollapsiblePadding => Self::ContentCollapsiblePadding,
1090            InstructionTextTokenContext::DerivedStringReference => {
1091                Self::DerivedStringReferenceTokenContext
1092            }
1093        }
1094    }
1095}
1096
1097#[repr(transparent)]
1098pub struct DisassemblyTextRenderer {
1099    handle: NonNull<BNDisassemblyTextRenderer>,
1100}
1101
1102impl DisassemblyTextRenderer {
1103    pub unsafe fn ref_from_raw(handle: NonNull<BNDisassemblyTextRenderer>) -> Ref<Self> {
1104        Ref::new(Self { handle })
1105    }
1106
1107    pub fn from_function(func: &Function, settings: Option<&DisassemblySettings>) -> Ref<Self> {
1108        let settings_ptr = settings.map(|s| s.handle).unwrap_or(ptr::null_mut());
1109        let result = unsafe { BNCreateDisassemblyTextRenderer(func.handle, settings_ptr) };
1110        unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) }
1111    }
1112
1113    pub fn from_llil<M: FunctionMutability, F: FunctionForm>(
1114        func: &LowLevelILFunction<M, F>,
1115        settings: Option<&DisassemblySettings>,
1116    ) -> Ref<Self> {
1117        let settings_ptr = settings.map(|s| s.handle).unwrap_or(ptr::null_mut());
1118        let result =
1119            unsafe { BNCreateLowLevelILDisassemblyTextRenderer(func.handle, settings_ptr) };
1120        unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) }
1121    }
1122
1123    pub fn from_mlil(
1124        func: &MediumLevelILFunction,
1125        settings: Option<&DisassemblySettings>,
1126    ) -> Ref<Self> {
1127        let settings_ptr = settings.map(|s| s.handle).unwrap_or(ptr::null_mut());
1128        let result =
1129            unsafe { BNCreateMediumLevelILDisassemblyTextRenderer(func.handle, settings_ptr) };
1130        unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) }
1131    }
1132
1133    pub fn from_hlil(
1134        func: &HighLevelILFunction,
1135        settings: Option<&DisassemblySettings>,
1136    ) -> Ref<Self> {
1137        let settings_ptr = settings.map(|s| s.handle).unwrap_or(ptr::null_mut());
1138        let result =
1139            unsafe { BNCreateHighLevelILDisassemblyTextRenderer(func.handle, settings_ptr) };
1140        unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) }
1141    }
1142
1143    pub fn function(&self) -> Ref<Function> {
1144        let result = unsafe { BNGetDisassemblyTextRendererFunction(self.handle.as_ptr()) };
1145        assert!(!result.is_null());
1146        unsafe { Function::ref_from_raw(result) }
1147    }
1148
1149    pub fn llil<M: FunctionMutability, F: FunctionForm>(&self) -> Ref<LowLevelILFunction<M, F>> {
1150        let result =
1151            unsafe { BNGetDisassemblyTextRendererLowLevelILFunction(self.handle.as_ptr()) };
1152        assert!(!result.is_null());
1153        unsafe { LowLevelILFunction::ref_from_raw(result) }
1154    }
1155
1156    pub fn mlil(&self) -> Ref<MediumLevelILFunction> {
1157        let result =
1158            unsafe { BNGetDisassemblyTextRendererMediumLevelILFunction(self.handle.as_ptr()) };
1159        assert!(!result.is_null());
1160        unsafe { MediumLevelILFunction::ref_from_raw(result) }
1161    }
1162
1163    pub fn hlil(&self) -> Ref<HighLevelILFunction> {
1164        let result =
1165            unsafe { BNGetDisassemblyTextRendererHighLevelILFunction(self.handle.as_ptr()) };
1166        assert!(!result.is_null());
1167        unsafe { HighLevelILFunction::ref_from_raw(result, true) }
1168    }
1169
1170    pub fn basic_block(&self) -> Option<Ref<BasicBlock<NativeBlock>>> {
1171        let result = unsafe { BNGetDisassemblyTextRendererBasicBlock(self.handle.as_ptr()) };
1172        if result.is_null() {
1173            return None;
1174        }
1175        Some(unsafe { Ref::new(BasicBlock::from_raw(result, NativeBlock::new())) })
1176    }
1177
1178    pub fn set_basic_block(&self, value: Option<&BasicBlock<NativeBlock>>) {
1179        let block_ptr = value.map(|b| b.handle).unwrap_or(ptr::null_mut());
1180        unsafe { BNSetDisassemblyTextRendererBasicBlock(self.handle.as_ptr(), block_ptr) }
1181    }
1182
1183    pub fn arch(&self) -> CoreArchitecture {
1184        let result = unsafe { BNGetDisassemblyTextRendererArchitecture(self.handle.as_ptr()) };
1185        assert!(!result.is_null());
1186        unsafe { CoreArchitecture::from_raw(result) }
1187    }
1188
1189    pub fn set_arch(&self, value: CoreArchitecture) {
1190        unsafe { BNSetDisassemblyTextRendererArchitecture(self.handle.as_ptr(), value.handle) }
1191    }
1192
1193    pub fn settings(&self) -> Ref<DisassemblySettings> {
1194        let result = unsafe { BNGetDisassemblyTextRendererSettings(self.handle.as_ptr()) };
1195        unsafe { DisassemblySettings::ref_from_raw(result) }
1196    }
1197
1198    pub fn set_settings(&self, settings: Option<&DisassemblySettings>) {
1199        let settings_ptr = settings.map(|s| s.handle).unwrap_or(ptr::null_mut());
1200        unsafe { BNSetDisassemblyTextRendererSettings(self.handle.as_ptr(), settings_ptr) }
1201    }
1202
1203    pub fn is_il(&self) -> bool {
1204        unsafe { BNIsILDisassemblyTextRenderer(self.handle.as_ptr()) }
1205    }
1206
1207    pub fn has_data_flow(&self) -> bool {
1208        unsafe { BNDisassemblyTextRendererHasDataFlow(self.handle.as_ptr()) }
1209    }
1210
1211    /// Gets the instructions annotations, like displaying register constant values.
1212    pub fn instruction_annotations(&self, addr: u64) -> Array<InstructionTextToken> {
1213        let mut count = 0;
1214        let result = unsafe {
1215            BNGetDisassemblyTextRendererInstructionAnnotations(
1216                self.handle.as_ptr(),
1217                addr,
1218                &mut count,
1219            )
1220        };
1221        assert!(!result.is_null());
1222        unsafe { Array::new(result, count, ()) }
1223    }
1224
1225    /// Gets the disassembly instruction text only, with no annotations.
1226    pub fn instruction_text(&self, addr: u64) -> Option<(Array<DisassemblyTextLine>, usize)> {
1227        let mut count = 0;
1228        let mut length = 0;
1229        let mut lines: *mut BNDisassemblyTextLine = ptr::null_mut();
1230        let result = unsafe {
1231            BNGetDisassemblyTextRendererInstructionText(
1232                self.handle.as_ptr(),
1233                addr,
1234                &mut length,
1235                &mut lines,
1236                &mut count,
1237            )
1238        };
1239        result.then(|| (unsafe { Array::new(lines, count, ()) }, length))
1240    }
1241
1242    // Gets the disassembly text as it would appear in the UI, with annotations.
1243    pub fn disassembly_text(&self, addr: u64) -> Option<(Array<DisassemblyTextLine>, usize)> {
1244        let mut count = 0;
1245        let mut length = 0;
1246        let mut lines: *mut BNDisassemblyTextLine = ptr::null_mut();
1247        let result = unsafe {
1248            BNGetDisassemblyTextRendererLines(
1249                self.handle.as_ptr(),
1250                addr,
1251                &mut length,
1252                &mut lines,
1253                &mut count,
1254            )
1255        };
1256        result.then(|| (unsafe { Array::new(lines, count, ()) }, length))
1257    }
1258
1259    // TODO post_process_lines BNPostProcessDisassemblyTextRendererLines
1260
1261    pub fn is_integer_token(token_type: InstructionTextTokenType) -> bool {
1262        unsafe { BNIsIntegerToken(token_type) }
1263    }
1264
1265    pub fn reset_deduplicated_comments(&self) {
1266        unsafe { BNResetDisassemblyTextRendererDeduplicatedComments(self.handle.as_ptr()) }
1267    }
1268
1269    pub fn symbol_tokens(
1270        &self,
1271        addr: u64,
1272        size: usize,
1273        operand: Option<usize>,
1274    ) -> Option<Array<InstructionTextToken>> {
1275        let operand = operand.unwrap_or(0xffffffff);
1276        let mut count = 0;
1277        let mut tokens: *mut BNInstructionTextToken = ptr::null_mut();
1278        let result = unsafe {
1279            BNGetDisassemblyTextRendererSymbolTokens(
1280                self.handle.as_ptr(),
1281                addr,
1282                size,
1283                operand,
1284                &mut tokens,
1285                &mut count,
1286            )
1287        };
1288        result.then(|| unsafe { Array::new(tokens, count, ()) })
1289    }
1290
1291    pub fn stack_var_reference_tokens(
1292        &self,
1293        stack_ref: StackVariableReference,
1294    ) -> Array<InstructionTextToken> {
1295        let mut stack_ref_raw = StackVariableReference::into_raw(stack_ref);
1296        let mut count = 0;
1297        let tokens = unsafe {
1298            BNGetDisassemblyTextRendererStackVariableReferenceTokens(
1299                self.handle.as_ptr(),
1300                &mut stack_ref_raw,
1301                &mut count,
1302            )
1303        };
1304        StackVariableReference::free_raw(stack_ref_raw);
1305        assert!(!tokens.is_null());
1306        unsafe { Array::new(tokens, count, ()) }
1307    }
1308
1309    pub fn integer_token(
1310        &self,
1311        int_token: InstructionTextToken,
1312        location: impl Into<Location>,
1313    ) -> Array<InstructionTextToken> {
1314        let location = location.into();
1315        let arch = location
1316            .arch
1317            .map(|a| a.handle)
1318            .unwrap_or_else(std::ptr::null_mut);
1319        let mut count = 0;
1320        let mut int_token_raw = InstructionTextToken::into_raw(int_token);
1321        let tokens = unsafe {
1322            BNGetDisassemblyTextRendererIntegerTokens(
1323                self.handle.as_ptr(),
1324                &mut int_token_raw,
1325                arch,
1326                location.addr,
1327                &mut count,
1328            )
1329        };
1330        InstructionTextToken::free_raw(int_token_raw);
1331        assert!(!tokens.is_null());
1332        unsafe { Array::new(tokens, count, ()) }
1333    }
1334
1335    pub fn wrap_comment(
1336        &self,
1337        cur_line: DisassemblyTextLine,
1338        comment: &str,
1339        has_auto_annotations: bool,
1340        leading_spaces: &str,
1341        indent_spaces: &str,
1342    ) -> Array<DisassemblyTextLine> {
1343        let cur_line_raw = DisassemblyTextLine::into_raw(cur_line);
1344        let comment_raw = comment.to_cstr();
1345        let leading_spaces_raw = leading_spaces.to_cstr();
1346        let indent_spaces_raw = indent_spaces.to_cstr();
1347        let mut count = 0;
1348        let lines = unsafe {
1349            BNDisassemblyTextRendererWrapComment(
1350                self.handle.as_ptr(),
1351                &cur_line_raw,
1352                &mut count,
1353                comment_raw.as_ref().as_ptr() as *const ffi::c_char,
1354                has_auto_annotations,
1355                leading_spaces_raw.as_ref().as_ptr() as *const ffi::c_char,
1356                indent_spaces_raw.as_ref().as_ptr() as *const ffi::c_char,
1357            )
1358        };
1359        DisassemblyTextLine::free_raw(cur_line_raw);
1360        assert!(!lines.is_null());
1361        unsafe { Array::new(lines, count, ()) }
1362    }
1363}
1364
1365impl ToOwned for DisassemblyTextRenderer {
1366    type Owned = Ref<Self>;
1367
1368    fn to_owned(&self) -> Self::Owned {
1369        unsafe { RefCountable::inc_ref(self) }
1370    }
1371}
1372
1373unsafe impl RefCountable for DisassemblyTextRenderer {
1374    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
1375        Ref::new(Self {
1376            handle: NonNull::new(BNNewDisassemblyTextRendererReference(
1377                handle.handle.as_ptr(),
1378            ))
1379            .unwrap(),
1380        })
1381    }
1382
1383    unsafe fn dec_ref(handle: &Self) {
1384        BNFreeDisassemblyTextRenderer(handle.handle.as_ptr());
1385    }
1386}
1387
1388// TODO: Make a builder for this.
1389#[derive(PartialEq, Eq, Hash)]
1390pub struct DisassemblySettings {
1391    pub(crate) handle: *mut BNDisassemblySettings,
1392}
1393
1394impl DisassemblySettings {
1395    pub fn ref_from_raw(handle: *mut BNDisassemblySettings) -> Ref<Self> {
1396        debug_assert!(!handle.is_null());
1397        unsafe { Ref::new(Self { handle }) }
1398    }
1399
1400    pub fn new() -> Ref<Self> {
1401        let handle = unsafe { BNCreateDisassemblySettings() };
1402        Self::ref_from_raw(handle)
1403    }
1404
1405    pub fn set_option(&self, option: DisassemblyOption, state: bool) {
1406        unsafe { BNSetDisassemblySettingsOption(self.handle, option, state) }
1407    }
1408
1409    pub fn is_option_set(&self, option: DisassemblyOption) -> bool {
1410        unsafe { BNIsDisassemblySettingsOptionSet(self.handle, option) }
1411    }
1412}
1413
1414impl ToOwned for DisassemblySettings {
1415    type Owned = Ref<Self>;
1416
1417    fn to_owned(&self) -> Self::Owned {
1418        unsafe { RefCountable::inc_ref(self) }
1419    }
1420}
1421
1422unsafe impl RefCountable for DisassemblySettings {
1423    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
1424        Ref::new(Self {
1425            handle: BNNewDisassemblySettingsReference(handle.handle),
1426        })
1427    }
1428
1429    unsafe fn dec_ref(handle: &Self) {
1430        BNFreeDisassemblySettings(handle.handle);
1431    }
1432}