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};
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    pub kind: InstructionTextTokenKind,
271}
272
273impl InstructionTextToken {
274    pub(crate) fn from_raw(value: &BNInstructionTextToken) -> Self {
275        Self {
276            address: value.address,
277            text: raw_to_string(value.text).unwrap(),
278            confidence: value.confidence,
279            context: value.context.into(),
280            expr_index: match value.exprIndex {
281                BN_INVALID_EXPR => None,
282                index => Some(index),
283            },
284            kind: InstructionTextTokenKind::from_raw(value),
285        }
286    }
287
288    pub(crate) fn into_raw(value: Self) -> BNInstructionTextToken {
289        let bn_text = BnString::new(value.text);
290        // These can be gathered from value.kind
291        let kind_value = value.kind.try_value().unwrap_or(0);
292        let operand = value.kind.try_operand().unwrap_or(0);
293        let size = value.kind.try_size().unwrap_or(0);
294        let type_names = value.kind.try_type_names().unwrap_or_default();
295        BNInstructionTextToken {
296            type_: value.kind.into(),
297            // NOTE: Expected to be freed with `InstructionTextToken::free_raw`.
298            text: BnString::into_raw(bn_text),
299            value: kind_value,
300            // TODO: Where is this even used?
301            width: 0,
302            size,
303            operand,
304            context: value.context.into(),
305            confidence: value.confidence,
306            address: value.address,
307            // NOTE: Expected to be freed with `InstructionTextToken::free_raw`.
308            typeNames: strings_to_string_list(&type_names),
309            namesCount: type_names.len(),
310            exprIndex: value.expr_index.unwrap_or(BN_INVALID_EXPR),
311        }
312    }
313
314    pub(crate) fn free_raw(value: BNInstructionTextToken) {
315        unsafe { BnString::free_raw(value.text) };
316        if !value.typeNames.is_null() {
317            unsafe { BNFreeStringList(value.typeNames, value.namesCount) };
318        }
319    }
320
321    /// Construct a new token **without** an associated address.
322    ///
323    /// You most likely want to call [`InstructionTextToken::new_with_address`], while also adjusting
324    /// the [`InstructionTextToken::expr_index`] field where applicable.
325    pub fn new(text: impl Into<String>, kind: InstructionTextTokenKind) -> Self {
326        Self {
327            address: 0,
328            text: text.into(),
329            confidence: MAX_CONFIDENCE,
330            context: InstructionTextTokenContext::Normal,
331            expr_index: None,
332            kind,
333        }
334    }
335
336    pub fn new_with_address(
337        address: u64,
338        text: impl Into<String>,
339        kind: InstructionTextTokenKind,
340    ) -> Self {
341        Self {
342            address,
343            text: text.into(),
344            confidence: MAX_CONFIDENCE,
345            context: InstructionTextTokenContext::Normal,
346            expr_index: None,
347            kind,
348        }
349    }
350}
351
352impl Display for InstructionTextToken {
353    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
354        self.text.fmt(f)
355    }
356}
357
358impl CoreArrayProvider for InstructionTextToken {
359    type Raw = BNInstructionTextToken;
360    type Context = ();
361    type Wrapped<'a> = Self;
362}
363
364unsafe impl CoreArrayProviderInner for InstructionTextToken {
365    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
366        // SAFETY: The Array MUST have been allocated on the core side. This will `delete[] raw`.
367        BNFreeInstructionText(raw, count)
368    }
369
370    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
371        Self::from_raw(raw)
372    }
373}
374
375impl CoreArrayProvider for Array<InstructionTextToken> {
376    type Raw = BNInstructionTextLine;
377    type Context = ();
378    type Wrapped<'a> = std::mem::ManuallyDrop<Self>;
379}
380
381unsafe impl CoreArrayProviderInner for Array<InstructionTextToken> {
382    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
383        // SAFETY: The Array MUST have been allocated on the core side. This will `delete[] raw`.
384        BNFreeInstructionTextLines(raw, count)
385    }
386
387    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
388        // TODO: This is insane.
389        std::mem::ManuallyDrop::new(Self::new(raw.tokens, raw.count, ()))
390    }
391}
392
393#[derive(Clone, PartialEq, Debug)]
394pub enum InstructionTextTokenKind {
395    Text,
396    Instruction,
397    OperandSeparator,
398    Register,
399    Integer {
400        value: u64,
401        /// Size of the integer
402        size: Option<usize>,
403    },
404    PossibleAddress {
405        value: u64,
406        /// Size of the address
407        size: Option<usize>,
408    },
409    BeginMemoryOperand,
410    EndMemoryOperand,
411    FloatingPoint {
412        value: f64,
413        /// Size of the floating point
414        size: Option<usize>,
415    },
416    Annotation,
417    CodeRelativeAddress {
418        value: u64,
419        size: Option<usize>,
420    },
421    ArgumentName {
422        // TODO: The argument index?
423        value: u64,
424    },
425    HexDumpByteValue {
426        value: u8,
427    },
428    HexDumpSkippedByte,
429    HexDumpInvalidByte,
430    HexDumpText {
431        // TODO: Explain what this does
432        width: u64,
433    },
434    Opcode,
435    String {
436        // TODO: What is this?
437        // TODO: It seems like people just throw things in here...
438        value: u64,
439    },
440    /// String content is only present for:
441    /// - [`InstructionTextTokenContext::StringReference`]
442    /// - [`InstructionTextTokenContext::StringDisplay`]
443    StringContent {
444        ty: StringType,
445    },
446    CharacterConstant,
447    Keyword {
448        // Example usage can be found for `BNAnalysisWarningActionType`.
449        value: u64,
450    },
451    TypeName,
452    FieldName {
453        /// Offset to this field in the respective structure
454        offset: u64,
455        /// Stores the type names for the referenced field name.
456        ///
457        /// This is typically just the members name.
458        /// For example MyStructure.my_field will have type_names be \["my_field"\].
459        type_names: Vec<String>,
460    },
461    NameSpace,
462    NameSpaceSeparator,
463    Tag,
464    StructOffset {
465        /// Offset to this field in the respective structure
466        offset: u64,
467        // TODO: This makes no sense for struct offset, they dont have types?
468        /// Stores the type names for the referenced field name.
469        type_names: Vec<String>,
470    },
471    // TODO: Unused?
472    StructOffsetByteValue,
473    // TODO: Unused?
474    StructureHexDumpText {
475        // TODO: Explain what this does
476        width: u64,
477    },
478    GotoLabel {
479        target: u64,
480    },
481    Comment {
482        target: u64,
483    },
484    PossibleValue {
485        value: u64,
486    },
487    // TODO: This is weird, you pass the value type as the text, we should restrict this behavior and type it
488    PossibleValueType,
489    ArrayIndex {
490        index: u64,
491    },
492    Indentation,
493    UnknownMemory,
494    EnumerationMember {
495        value: u64,
496        // TODO: Document where this type id comes from
497        // TODO: Can we type this to something other than a string?
498        /// The enumerations type id
499        type_id: Option<String>,
500    },
501    /// Operations like +, -, %
502    Operation,
503    BaseStructureName,
504    BaseStructureSeparator,
505    Brace {
506        // TODO: Explain what this is
507        hash: Option<u64>,
508    },
509    ValueLocation,
510    CodeSymbol {
511        // Target address of the symbol
512        value: u64,
513        // TODO: Size of what?
514        size: usize, // TODO: Operand?
515    },
516    DataSymbol {
517        // Target address of the symbol
518        value: u64,
519        // TODO: Size of what?
520        size: usize, // TODO: Operand?
521    },
522    LocalVariable {
523        // This comes from the token.value
524        // TODO: Do we have a variable id type we can attach to this?
525        // TODO: Probably not considering this is used at multiple IL levels.
526        variable_id: u64,
527        /// NOTE: This is only valid in SSA form
528        ssa_version: usize,
529    },
530    Import {
531        // TODO: Looks to be the target address from the import.
532        target: u64,
533    },
534    AddressDisplay {
535        address: u64,
536    },
537    // TODO: BAD
538    IndirectImport {
539        /// The address of the import
540        ///
541        /// If you want the address of the import token use [`InstructionTextToken::address`] instead.
542        target: u64,
543        /// Size of the instruction this token is apart of
544        size: usize,
545        // TODO: Type this
546        source_operand: usize,
547    },
548    ExternalSymbol {
549        // TODO: Value of what?
550        value: u64,
551    },
552    StackVariable {
553        // TODO: Do we have a variable id type we can attach to this?
554        // TODO: Probably not considering this is used at multiple IL levels.
555        variable_id: u64,
556    },
557    AddressSeparator,
558    CollapsedInformation,
559    CollapseStateIndicator {
560        // TODO: Explain what this is
561        hash: Option<u64>,
562    },
563    NewLine {
564        // Offset into instruction that this new line is associated with
565        value: u64,
566    },
567}
568
569impl InstructionTextTokenKind {
570    pub(crate) fn from_raw(value: &BNInstructionTextToken) -> Self {
571        match value.type_ {
572            BNInstructionTextTokenType::TextToken => Self::Text,
573            BNInstructionTextTokenType::InstructionToken => Self::Instruction,
574            BNInstructionTextTokenType::OperandSeparatorToken => Self::OperandSeparator,
575            BNInstructionTextTokenType::RegisterToken => Self::Register,
576            BNInstructionTextTokenType::IntegerToken => Self::Integer {
577                value: value.value,
578                size: match value.size {
579                    0 => None,
580                    size => Some(size),
581                },
582            },
583            BNInstructionTextTokenType::PossibleAddressToken => Self::PossibleAddress {
584                value: value.value,
585                size: match value.size {
586                    0 => None,
587                    size => Some(size),
588                },
589            },
590            BNInstructionTextTokenType::BeginMemoryOperandToken => Self::BeginMemoryOperand,
591            BNInstructionTextTokenType::EndMemoryOperandToken => Self::EndMemoryOperand,
592            BNInstructionTextTokenType::FloatingPointToken => Self::FloatingPoint {
593                value: value.value as f64,
594                size: match value.size {
595                    0 => None,
596                    size => Some(size),
597                },
598            },
599            BNInstructionTextTokenType::AnnotationToken => Self::Annotation,
600            BNInstructionTextTokenType::CodeRelativeAddressToken => Self::CodeRelativeAddress {
601                value: value.value,
602                size: match value.size {
603                    0 => None,
604                    size => Some(size),
605                },
606            },
607            BNInstructionTextTokenType::ArgumentNameToken => {
608                Self::ArgumentName { value: value.value }
609            }
610            BNInstructionTextTokenType::HexDumpByteValueToken => Self::HexDumpByteValue {
611                value: value.value as u8,
612            },
613            BNInstructionTextTokenType::HexDumpSkippedByteToken => Self::HexDumpSkippedByte,
614            BNInstructionTextTokenType::HexDumpInvalidByteToken => Self::HexDumpInvalidByte,
615            BNInstructionTextTokenType::HexDumpTextToken => {
616                Self::HexDumpText { width: value.value }
617            }
618            BNInstructionTextTokenType::OpcodeToken => Self::Opcode,
619            BNInstructionTextTokenType::StringToken => match value.context {
620                BNInstructionTextTokenContext::StringReferenceTokenContext
621                | BNInstructionTextTokenContext::StringDisplayTokenContext => {
622                    match value.value {
623                        0 => Self::StringContent {
624                            ty: StringType::AsciiString,
625                        },
626                        1 => Self::StringContent {
627                            ty: StringType::Utf8String,
628                        },
629                        2 => Self::StringContent {
630                            ty: StringType::Utf16String,
631                        },
632                        3 => Self::StringContent {
633                            ty: StringType::Utf32String,
634                        },
635                        // If we reach here all hope is lost.
636                        // Reaching here means someone made a ref or display context token with no
637                        // StringType and instead some other random value...
638                        value => Self::String { value },
639                    }
640                }
641                _ => Self::String { value: value.value },
642            },
643            BNInstructionTextTokenType::CharacterConstantToken => Self::CharacterConstant,
644            BNInstructionTextTokenType::KeywordToken => Self::Keyword { value: value.value },
645            BNInstructionTextTokenType::TypeNameToken => Self::TypeName,
646            BNInstructionTextTokenType::FieldNameToken => Self::FieldName {
647                offset: value.value,
648                type_names: {
649                    // NOTE: Do not need to free, this is a part of the From<&> impl
650                    let raw_names =
651                        unsafe { std::slice::from_raw_parts(value.typeNames, value.namesCount) };
652                    raw_names.iter().filter_map(|&r| raw_to_string(r)).collect()
653                },
654            },
655            BNInstructionTextTokenType::NameSpaceToken => Self::NameSpace,
656            BNInstructionTextTokenType::NameSpaceSeparatorToken => Self::NameSpaceSeparator,
657            BNInstructionTextTokenType::TagToken => Self::Tag,
658            BNInstructionTextTokenType::StructOffsetToken => Self::StructOffset {
659                offset: value.value,
660                type_names: {
661                    // NOTE: Do not need to free, this is a part of the From<&> impl
662                    let raw_names =
663                        unsafe { std::slice::from_raw_parts(value.typeNames, value.namesCount) };
664                    raw_names.iter().filter_map(|&r| raw_to_string(r)).collect()
665                },
666            },
667            BNInstructionTextTokenType::StructOffsetByteValueToken => Self::StructOffsetByteValue,
668            BNInstructionTextTokenType::StructureHexDumpTextToken => {
669                Self::StructureHexDumpText { width: value.value }
670            }
671            BNInstructionTextTokenType::GotoLabelToken => Self::GotoLabel {
672                target: value.value,
673            },
674            BNInstructionTextTokenType::CommentToken => Self::Comment {
675                target: value.value,
676            },
677            BNInstructionTextTokenType::PossibleValueToken => {
678                Self::PossibleValue { value: value.value }
679            }
680            // NOTE: See my comment about this type in [`Self::PossibleValueType`]
681            BNInstructionTextTokenType::PossibleValueTypeToken => Self::PossibleValueType,
682            BNInstructionTextTokenType::ArrayIndexToken => Self::ArrayIndex { index: value.value },
683            BNInstructionTextTokenType::IndentationToken => Self::Indentation,
684            BNInstructionTextTokenType::UnknownMemoryToken => Self::UnknownMemory,
685            BNInstructionTextTokenType::EnumerationMemberToken => Self::EnumerationMember {
686                value: value.value,
687                type_id: {
688                    // NOTE: Type id comes from value.typeNames, it should be the first one (hence the .next)
689                    // NOTE: Do not need to free, this is a part of the From<&> impl
690                    let raw_names =
691                        unsafe { std::slice::from_raw_parts(value.typeNames, value.namesCount) };
692                    raw_names.iter().filter_map(|&r| raw_to_string(r)).next()
693                },
694            },
695            BNInstructionTextTokenType::OperationToken => Self::Operation,
696            BNInstructionTextTokenType::BaseStructureNameToken => Self::BaseStructureName,
697            BNInstructionTextTokenType::BaseStructureSeparatorToken => Self::BaseStructureSeparator,
698            BNInstructionTextTokenType::BraceToken => Self::Brace {
699                hash: match value.value {
700                    0 => None,
701                    hash => Some(hash),
702                },
703            },
704            BNInstructionTextTokenType::ValueLocationToken => Self::ValueLocation,
705            BNInstructionTextTokenType::CodeSymbolToken => Self::CodeSymbol {
706                value: value.value,
707                size: value.size,
708            },
709            BNInstructionTextTokenType::DataSymbolToken => Self::DataSymbol {
710                value: value.value,
711                size: value.size,
712            },
713            BNInstructionTextTokenType::LocalVariableToken => Self::LocalVariable {
714                variable_id: value.value,
715                ssa_version: value.operand,
716            },
717            BNInstructionTextTokenType::ImportToken => Self::Import {
718                target: value.value,
719            },
720            BNInstructionTextTokenType::AddressDisplayToken => Self::AddressDisplay {
721                address: value.value,
722            },
723            BNInstructionTextTokenType::IndirectImportToken => Self::IndirectImport {
724                target: value.value,
725                size: value.size,
726                source_operand: value.operand,
727            },
728            BNInstructionTextTokenType::ExternalSymbolToken => {
729                Self::ExternalSymbol { value: value.value }
730            }
731            BNInstructionTextTokenType::StackVariableToken => Self::StackVariable {
732                variable_id: value.value,
733            },
734            BNInstructionTextTokenType::AddressSeparatorToken => Self::AddressSeparator,
735            BNInstructionTextTokenType::CollapsedInformationToken => Self::CollapsedInformation,
736            BNInstructionTextTokenType::CollapseStateIndicatorToken => {
737                Self::CollapseStateIndicator {
738                    hash: match value.value {
739                        0 => None,
740                        hash => Some(hash),
741                    },
742                }
743            }
744            BNInstructionTextTokenType::NewLineToken => Self::NewLine { value: value.value },
745        }
746    }
747
748    /// Mapping to the [`BNInstructionTextTokenType::value`] field.
749    fn try_value(&self) -> Option<u64> {
750        // TODO: Double check to make sure these are correct.
751        match self {
752            InstructionTextTokenKind::Integer { value, .. } => Some(*value),
753            InstructionTextTokenKind::PossibleAddress { value, .. } => Some(*value),
754            InstructionTextTokenKind::PossibleValue { value, .. } => Some(*value),
755            InstructionTextTokenKind::FloatingPoint { value, .. } => Some(*value as u64),
756            InstructionTextTokenKind::CodeRelativeAddress { value, .. } => Some(*value),
757            InstructionTextTokenKind::ArgumentName { value, .. } => Some(*value),
758            InstructionTextTokenKind::HexDumpByteValue { value, .. } => Some(*value as u64),
759            InstructionTextTokenKind::HexDumpText { width, .. } => Some(*width),
760            InstructionTextTokenKind::String { value, .. } => Some(*value),
761            InstructionTextTokenKind::StringContent { ty, .. } => Some(*ty as u64),
762            InstructionTextTokenKind::Keyword { value, .. } => Some(*value),
763            InstructionTextTokenKind::FieldName { offset, .. } => Some(*offset),
764            InstructionTextTokenKind::StructOffset { offset, .. } => Some(*offset),
765            InstructionTextTokenKind::StructureHexDumpText { width, .. } => Some(*width),
766            InstructionTextTokenKind::GotoLabel { target, .. } => Some(*target),
767            InstructionTextTokenKind::Comment { target, .. } => Some(*target),
768            InstructionTextTokenKind::ArrayIndex { index, .. } => Some(*index),
769            InstructionTextTokenKind::EnumerationMember { value, .. } => Some(*value),
770            InstructionTextTokenKind::LocalVariable { variable_id, .. } => Some(*variable_id),
771            InstructionTextTokenKind::Import { target, .. } => Some(*target),
772            InstructionTextTokenKind::AddressDisplay { address, .. } => Some(*address),
773            InstructionTextTokenKind::IndirectImport { target, .. } => Some(*target),
774            InstructionTextTokenKind::Brace { hash, .. } => *hash,
775            InstructionTextTokenKind::CodeSymbol { value, .. } => Some(*value),
776            InstructionTextTokenKind::DataSymbol { value, .. } => Some(*value),
777            InstructionTextTokenKind::ExternalSymbol { value, .. } => Some(*value),
778            InstructionTextTokenKind::StackVariable { variable_id, .. } => Some(*variable_id),
779            InstructionTextTokenKind::CollapseStateIndicator { hash, .. } => *hash,
780            InstructionTextTokenKind::NewLine { value, .. } => Some(*value),
781            _ => None,
782        }
783    }
784
785    /// Mapping to the [`BNInstructionTextTokenType::size`] field.
786    fn try_size(&self) -> Option<usize> {
787        match self {
788            InstructionTextTokenKind::Integer { size, .. } => *size,
789            InstructionTextTokenKind::FloatingPoint { size, .. } => *size,
790            InstructionTextTokenKind::PossibleAddress { size, .. } => *size,
791            InstructionTextTokenKind::CodeRelativeAddress { size, .. } => *size,
792            InstructionTextTokenKind::CodeSymbol { size, .. } => Some(*size),
793            InstructionTextTokenKind::DataSymbol { size, .. } => Some(*size),
794            InstructionTextTokenKind::IndirectImport { size, .. } => Some(*size),
795            _ => None,
796        }
797    }
798
799    /// Mapping to the [`BNInstructionTextTokenType::operand`] field.
800    fn try_operand(&self) -> Option<usize> {
801        match self {
802            InstructionTextTokenKind::LocalVariable { ssa_version, .. } => Some(*ssa_version),
803            InstructionTextTokenKind::IndirectImport { source_operand, .. } => {
804                Some(*source_operand)
805            }
806            _ => None,
807        }
808    }
809
810    /// Mapping to the [`BNInstructionTextTokenType::typeNames`] field.
811    fn try_type_names(&self) -> Option<Vec<String>> {
812        match self {
813            InstructionTextTokenKind::FieldName { type_names, .. } => Some(type_names.clone()),
814            InstructionTextTokenKind::StructOffset { type_names, .. } => Some(type_names.clone()),
815            InstructionTextTokenKind::EnumerationMember { type_id, .. } => {
816                Some(vec![type_id.clone()?])
817            }
818            _ => None,
819        }
820    }
821}
822
823impl From<InstructionTextTokenKind> for BNInstructionTextTokenType {
824    fn from(value: InstructionTextTokenKind) -> Self {
825        match value {
826            InstructionTextTokenKind::Text => BNInstructionTextTokenType::TextToken,
827            InstructionTextTokenKind::Instruction => BNInstructionTextTokenType::InstructionToken,
828            InstructionTextTokenKind::OperandSeparator => {
829                BNInstructionTextTokenType::OperandSeparatorToken
830            }
831            InstructionTextTokenKind::Register => BNInstructionTextTokenType::RegisterToken,
832            InstructionTextTokenKind::Integer { .. } => BNInstructionTextTokenType::IntegerToken,
833            InstructionTextTokenKind::PossibleAddress { .. } => {
834                BNInstructionTextTokenType::PossibleAddressToken
835            }
836            InstructionTextTokenKind::BeginMemoryOperand => {
837                BNInstructionTextTokenType::BeginMemoryOperandToken
838            }
839            InstructionTextTokenKind::EndMemoryOperand => {
840                BNInstructionTextTokenType::EndMemoryOperandToken
841            }
842            InstructionTextTokenKind::FloatingPoint { .. } => {
843                BNInstructionTextTokenType::FloatingPointToken
844            }
845            InstructionTextTokenKind::Annotation => BNInstructionTextTokenType::AnnotationToken,
846            InstructionTextTokenKind::CodeRelativeAddress { .. } => {
847                BNInstructionTextTokenType::CodeRelativeAddressToken
848            }
849            InstructionTextTokenKind::ArgumentName { .. } => {
850                BNInstructionTextTokenType::ArgumentNameToken
851            }
852            InstructionTextTokenKind::HexDumpByteValue { .. } => {
853                BNInstructionTextTokenType::HexDumpByteValueToken
854            }
855            InstructionTextTokenKind::HexDumpSkippedByte => {
856                BNInstructionTextTokenType::HexDumpSkippedByteToken
857            }
858            InstructionTextTokenKind::HexDumpInvalidByte => {
859                BNInstructionTextTokenType::HexDumpInvalidByteToken
860            }
861            InstructionTextTokenKind::HexDumpText { .. } => {
862                BNInstructionTextTokenType::HexDumpTextToken
863            }
864            InstructionTextTokenKind::Opcode => BNInstructionTextTokenType::OpcodeToken,
865            InstructionTextTokenKind::String { .. } => BNInstructionTextTokenType::StringToken,
866            InstructionTextTokenKind::StringContent { .. } => {
867                BNInstructionTextTokenType::StringToken
868            }
869            InstructionTextTokenKind::CharacterConstant => {
870                BNInstructionTextTokenType::CharacterConstantToken
871            }
872            InstructionTextTokenKind::Keyword { .. } => BNInstructionTextTokenType::KeywordToken,
873            InstructionTextTokenKind::TypeName => BNInstructionTextTokenType::TypeNameToken,
874            InstructionTextTokenKind::FieldName { .. } => {
875                BNInstructionTextTokenType::FieldNameToken
876            }
877            InstructionTextTokenKind::NameSpace => BNInstructionTextTokenType::NameSpaceToken,
878            InstructionTextTokenKind::NameSpaceSeparator => {
879                BNInstructionTextTokenType::NameSpaceSeparatorToken
880            }
881            InstructionTextTokenKind::Tag => BNInstructionTextTokenType::TagToken,
882            InstructionTextTokenKind::StructOffset { .. } => {
883                BNInstructionTextTokenType::StructOffsetToken
884            }
885            InstructionTextTokenKind::StructOffsetByteValue => {
886                BNInstructionTextTokenType::StructOffsetByteValueToken
887            }
888            InstructionTextTokenKind::StructureHexDumpText { .. } => {
889                BNInstructionTextTokenType::StructureHexDumpTextToken
890            }
891            InstructionTextTokenKind::GotoLabel { .. } => {
892                BNInstructionTextTokenType::GotoLabelToken
893            }
894            InstructionTextTokenKind::Comment { .. } => BNInstructionTextTokenType::CommentToken,
895            InstructionTextTokenKind::PossibleValue { .. } => {
896                BNInstructionTextTokenType::PossibleValueToken
897            }
898            InstructionTextTokenKind::PossibleValueType => {
899                BNInstructionTextTokenType::PossibleValueTypeToken
900            }
901            InstructionTextTokenKind::ArrayIndex { .. } => {
902                BNInstructionTextTokenType::ArrayIndexToken
903            }
904            InstructionTextTokenKind::Indentation => BNInstructionTextTokenType::IndentationToken,
905            InstructionTextTokenKind::UnknownMemory => {
906                BNInstructionTextTokenType::UnknownMemoryToken
907            }
908            InstructionTextTokenKind::EnumerationMember { .. } => {
909                BNInstructionTextTokenType::EnumerationMemberToken
910            }
911            InstructionTextTokenKind::Operation => BNInstructionTextTokenType::OperationToken,
912            InstructionTextTokenKind::BaseStructureName => {
913                BNInstructionTextTokenType::BaseStructureNameToken
914            }
915            InstructionTextTokenKind::BaseStructureSeparator => {
916                BNInstructionTextTokenType::BaseStructureSeparatorToken
917            }
918            InstructionTextTokenKind::Brace { .. } => BNInstructionTextTokenType::BraceToken,
919            InstructionTextTokenKind::ValueLocation => {
920                BNInstructionTextTokenType::ValueLocationToken
921            }
922            InstructionTextTokenKind::CodeSymbol { .. } => {
923                BNInstructionTextTokenType::CodeSymbolToken
924            }
925            InstructionTextTokenKind::DataSymbol { .. } => {
926                BNInstructionTextTokenType::DataSymbolToken
927            }
928            InstructionTextTokenKind::LocalVariable { .. } => {
929                BNInstructionTextTokenType::LocalVariableToken
930            }
931            InstructionTextTokenKind::Import { .. } => BNInstructionTextTokenType::ImportToken,
932            InstructionTextTokenKind::AddressDisplay { .. } => {
933                BNInstructionTextTokenType::AddressDisplayToken
934            }
935            InstructionTextTokenKind::IndirectImport { .. } => {
936                BNInstructionTextTokenType::IndirectImportToken
937            }
938            InstructionTextTokenKind::ExternalSymbol { .. } => {
939                BNInstructionTextTokenType::ExternalSymbolToken
940            }
941            InstructionTextTokenKind::StackVariable { .. } => {
942                BNInstructionTextTokenType::StackVariableToken
943            }
944            InstructionTextTokenKind::AddressSeparator => {
945                BNInstructionTextTokenType::AddressSeparatorToken
946            }
947            InstructionTextTokenKind::CollapsedInformation => {
948                BNInstructionTextTokenType::CollapsedInformationToken
949            }
950            InstructionTextTokenKind::CollapseStateIndicator { .. } => {
951                BNInstructionTextTokenType::CollapseStateIndicatorToken
952            }
953            InstructionTextTokenKind::NewLine { .. } => BNInstructionTextTokenType::NewLineToken,
954        }
955    }
956}
957
958impl Eq for InstructionTextTokenKind {}
959
960#[derive(Clone, Copy, PartialEq, Eq, Debug)]
961pub enum InstructionTextTokenContext {
962    Normal,
963    LocalVariable,
964    DataVariable,
965    FunctionReturn,
966    InstructionAddress,
967    ILInstructionIndex,
968    ConstData,
969    /// Use only with [`InstructionTextTokenKind::String`]
970    ConstStringData,
971    /// Use only with [`InstructionTextTokenKind::String`]
972    StringReference,
973    /// Use only with [`InstructionTextTokenKind::String`]
974    StringDataVariable,
975    /// For displaying strings which aren't associated with an address
976    ///
977    /// Use only with [`InstructionTextTokenKind::String`]
978    StringDisplay,
979    /// Use only with [`InstructionTextTokenKind::CollapseStateIndicator`]
980    Collapsed,
981    /// Use only with [`InstructionTextTokenKind::CollapseStateIndicator`]
982    Expanded,
983    /// Use only with [`InstructionTextTokenKind::CollapseStateIndicator`]
984    CollapsiblePadding,
985    /// Use only with [`InstructionTextTokenKind::String`]
986    DerivedStringReference,
987}
988
989impl From<BNInstructionTextTokenContext> for InstructionTextTokenContext {
990    fn from(value: BNInstructionTextTokenContext) -> Self {
991        match value {
992            BNInstructionTextTokenContext::NoTokenContext => Self::Normal,
993            BNInstructionTextTokenContext::LocalVariableTokenContext => Self::LocalVariable,
994            BNInstructionTextTokenContext::DataVariableTokenContext => Self::DataVariable,
995            BNInstructionTextTokenContext::FunctionReturnTokenContext => Self::FunctionReturn,
996            BNInstructionTextTokenContext::InstructionAddressTokenContext => {
997                Self::InstructionAddress
998            }
999            BNInstructionTextTokenContext::ILInstructionIndexTokenContext => {
1000                Self::ILInstructionIndex
1001            }
1002            BNInstructionTextTokenContext::ConstDataTokenContext => Self::ConstData,
1003            // For use with [`InstructionTextTokenKind::String`]
1004            BNInstructionTextTokenContext::ConstStringDataTokenContext => Self::ConstStringData,
1005            BNInstructionTextTokenContext::StringReferenceTokenContext => Self::StringReference,
1006            BNInstructionTextTokenContext::StringDataVariableTokenContext => {
1007                Self::StringDataVariable
1008            }
1009            BNInstructionTextTokenContext::StringDisplayTokenContext => Self::StringDisplay,
1010            // For use with [`InstructionTextTokenKind::CollapseStateIndicator`]
1011            BNInstructionTextTokenContext::ContentCollapsedContext => Self::Collapsed,
1012            BNInstructionTextTokenContext::ContentExpandedContext => Self::Expanded,
1013            BNInstructionTextTokenContext::ContentCollapsiblePadding => Self::CollapsiblePadding,
1014            BNInstructionTextTokenContext::DerivedStringReferenceTokenContext => {
1015                Self::DerivedStringReference
1016            }
1017        }
1018    }
1019}
1020
1021impl From<InstructionTextTokenContext> for BNInstructionTextTokenContext {
1022    fn from(value: InstructionTextTokenContext) -> Self {
1023        match value {
1024            InstructionTextTokenContext::Normal => Self::NoTokenContext,
1025            InstructionTextTokenContext::LocalVariable => Self::LocalVariableTokenContext,
1026            InstructionTextTokenContext::DataVariable => Self::DataVariableTokenContext,
1027            InstructionTextTokenContext::FunctionReturn => Self::FunctionReturnTokenContext,
1028            InstructionTextTokenContext::InstructionAddress => Self::InstructionAddressTokenContext,
1029            InstructionTextTokenContext::ILInstructionIndex => Self::ILInstructionIndexTokenContext,
1030            InstructionTextTokenContext::ConstData => Self::ConstDataTokenContext,
1031            InstructionTextTokenContext::ConstStringData => Self::ConstStringDataTokenContext,
1032            InstructionTextTokenContext::StringReference => Self::StringReferenceTokenContext,
1033            InstructionTextTokenContext::StringDataVariable => Self::StringDataVariableTokenContext,
1034            InstructionTextTokenContext::StringDisplay => Self::StringDisplayTokenContext,
1035            InstructionTextTokenContext::Collapsed => Self::ContentCollapsedContext,
1036            InstructionTextTokenContext::Expanded => Self::ContentExpandedContext,
1037            InstructionTextTokenContext::CollapsiblePadding => Self::ContentCollapsiblePadding,
1038            InstructionTextTokenContext::DerivedStringReference => {
1039                Self::DerivedStringReferenceTokenContext
1040            }
1041        }
1042    }
1043}
1044
1045#[repr(transparent)]
1046pub struct DisassemblyTextRenderer {
1047    handle: NonNull<BNDisassemblyTextRenderer>,
1048}
1049
1050impl DisassemblyTextRenderer {
1051    pub unsafe fn ref_from_raw(handle: NonNull<BNDisassemblyTextRenderer>) -> Ref<Self> {
1052        Ref::new(Self { handle })
1053    }
1054
1055    pub fn from_function(func: &Function, settings: Option<&DisassemblySettings>) -> Ref<Self> {
1056        let settings_ptr = settings.map(|s| s.handle).unwrap_or(ptr::null_mut());
1057        let result = unsafe { BNCreateDisassemblyTextRenderer(func.handle, settings_ptr) };
1058        unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) }
1059    }
1060
1061    pub fn from_llil<M: FunctionMutability, F: FunctionForm>(
1062        func: &LowLevelILFunction<M, F>,
1063        settings: Option<&DisassemblySettings>,
1064    ) -> Ref<Self> {
1065        let settings_ptr = settings.map(|s| s.handle).unwrap_or(ptr::null_mut());
1066        let result =
1067            unsafe { BNCreateLowLevelILDisassemblyTextRenderer(func.handle, settings_ptr) };
1068        unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) }
1069    }
1070
1071    pub fn from_mlil(
1072        func: &MediumLevelILFunction,
1073        settings: Option<&DisassemblySettings>,
1074    ) -> Ref<Self> {
1075        let settings_ptr = settings.map(|s| s.handle).unwrap_or(ptr::null_mut());
1076        let result =
1077            unsafe { BNCreateMediumLevelILDisassemblyTextRenderer(func.handle, settings_ptr) };
1078        unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) }
1079    }
1080
1081    pub fn from_hlil(
1082        func: &HighLevelILFunction,
1083        settings: Option<&DisassemblySettings>,
1084    ) -> Ref<Self> {
1085        let settings_ptr = settings.map(|s| s.handle).unwrap_or(ptr::null_mut());
1086        let result =
1087            unsafe { BNCreateHighLevelILDisassemblyTextRenderer(func.handle, settings_ptr) };
1088        unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) }
1089    }
1090
1091    pub fn function(&self) -> Ref<Function> {
1092        let result = unsafe { BNGetDisassemblyTextRendererFunction(self.handle.as_ptr()) };
1093        assert!(!result.is_null());
1094        unsafe { Function::ref_from_raw(result) }
1095    }
1096
1097    pub fn llil<M: FunctionMutability, F: FunctionForm>(&self) -> Ref<LowLevelILFunction<M, F>> {
1098        let result =
1099            unsafe { BNGetDisassemblyTextRendererLowLevelILFunction(self.handle.as_ptr()) };
1100        assert!(!result.is_null());
1101        unsafe { LowLevelILFunction::ref_from_raw(result) }
1102    }
1103
1104    pub fn mlil(&self) -> Ref<MediumLevelILFunction> {
1105        let result =
1106            unsafe { BNGetDisassemblyTextRendererMediumLevelILFunction(self.handle.as_ptr()) };
1107        assert!(!result.is_null());
1108        unsafe { MediumLevelILFunction::ref_from_raw(result) }
1109    }
1110
1111    pub fn hlil(&self) -> Ref<HighLevelILFunction> {
1112        let result =
1113            unsafe { BNGetDisassemblyTextRendererHighLevelILFunction(self.handle.as_ptr()) };
1114        assert!(!result.is_null());
1115        unsafe { HighLevelILFunction::ref_from_raw(result, true) }
1116    }
1117
1118    pub fn basic_block(&self) -> Option<Ref<BasicBlock<NativeBlock>>> {
1119        let result = unsafe { BNGetDisassemblyTextRendererBasicBlock(self.handle.as_ptr()) };
1120        if result.is_null() {
1121            return None;
1122        }
1123        Some(unsafe { Ref::new(BasicBlock::from_raw(result, NativeBlock::new())) })
1124    }
1125
1126    pub fn set_basic_block(&self, value: Option<&BasicBlock<NativeBlock>>) {
1127        let block_ptr = value.map(|b| b.handle).unwrap_or(ptr::null_mut());
1128        unsafe { BNSetDisassemblyTextRendererBasicBlock(self.handle.as_ptr(), block_ptr) }
1129    }
1130
1131    pub fn arch(&self) -> CoreArchitecture {
1132        let result = unsafe { BNGetDisassemblyTextRendererArchitecture(self.handle.as_ptr()) };
1133        assert!(!result.is_null());
1134        unsafe { CoreArchitecture::from_raw(result) }
1135    }
1136
1137    pub fn set_arch(&self, value: CoreArchitecture) {
1138        unsafe { BNSetDisassemblyTextRendererArchitecture(self.handle.as_ptr(), value.handle) }
1139    }
1140
1141    pub fn settings(&self) -> Ref<DisassemblySettings> {
1142        let result = unsafe { BNGetDisassemblyTextRendererSettings(self.handle.as_ptr()) };
1143        unsafe { DisassemblySettings::ref_from_raw(result) }
1144    }
1145
1146    pub fn set_settings(&self, settings: Option<&DisassemblySettings>) {
1147        let settings_ptr = settings.map(|s| s.handle).unwrap_or(ptr::null_mut());
1148        unsafe { BNSetDisassemblyTextRendererSettings(self.handle.as_ptr(), settings_ptr) }
1149    }
1150
1151    pub fn is_il(&self) -> bool {
1152        unsafe { BNIsILDisassemblyTextRenderer(self.handle.as_ptr()) }
1153    }
1154
1155    pub fn has_data_flow(&self) -> bool {
1156        unsafe { BNDisassemblyTextRendererHasDataFlow(self.handle.as_ptr()) }
1157    }
1158
1159    /// Gets the instructions annotations, like displaying register constant values.
1160    pub fn instruction_annotations(&self, addr: u64) -> Array<InstructionTextToken> {
1161        let mut count = 0;
1162        let result = unsafe {
1163            BNGetDisassemblyTextRendererInstructionAnnotations(
1164                self.handle.as_ptr(),
1165                addr,
1166                &mut count,
1167            )
1168        };
1169        assert!(!result.is_null());
1170        unsafe { Array::new(result, count, ()) }
1171    }
1172
1173    /// Gets the disassembly instruction text only, with no annotations.
1174    pub fn instruction_text(&self, addr: u64) -> Option<(Array<DisassemblyTextLine>, usize)> {
1175        let mut count = 0;
1176        let mut length = 0;
1177        let mut lines: *mut BNDisassemblyTextLine = ptr::null_mut();
1178        let result = unsafe {
1179            BNGetDisassemblyTextRendererInstructionText(
1180                self.handle.as_ptr(),
1181                addr,
1182                &mut length,
1183                &mut lines,
1184                &mut count,
1185            )
1186        };
1187        result.then(|| (unsafe { Array::new(lines, count, ()) }, length))
1188    }
1189
1190    // Gets the disassembly text as it would appear in the UI, with annotations.
1191    pub fn disassembly_text(&self, addr: u64) -> Option<(Array<DisassemblyTextLine>, usize)> {
1192        let mut count = 0;
1193        let mut length = 0;
1194        let mut lines: *mut BNDisassemblyTextLine = ptr::null_mut();
1195        let result = unsafe {
1196            BNGetDisassemblyTextRendererLines(
1197                self.handle.as_ptr(),
1198                addr,
1199                &mut length,
1200                &mut lines,
1201                &mut count,
1202            )
1203        };
1204        result.then(|| (unsafe { Array::new(lines, count, ()) }, length))
1205    }
1206
1207    // TODO post_process_lines BNPostProcessDisassemblyTextRendererLines
1208
1209    pub fn is_integer_token(token_type: InstructionTextTokenType) -> bool {
1210        unsafe { BNIsIntegerToken(token_type) }
1211    }
1212
1213    pub fn reset_deduplicated_comments(&self) {
1214        unsafe { BNResetDisassemblyTextRendererDeduplicatedComments(self.handle.as_ptr()) }
1215    }
1216
1217    pub fn symbol_tokens(
1218        &self,
1219        addr: u64,
1220        size: usize,
1221        operand: Option<usize>,
1222    ) -> Option<Array<InstructionTextToken>> {
1223        let operand = operand.unwrap_or(0xffffffff);
1224        let mut count = 0;
1225        let mut tokens: *mut BNInstructionTextToken = ptr::null_mut();
1226        let result = unsafe {
1227            BNGetDisassemblyTextRendererSymbolTokens(
1228                self.handle.as_ptr(),
1229                addr,
1230                size,
1231                operand,
1232                &mut tokens,
1233                &mut count,
1234            )
1235        };
1236        result.then(|| unsafe { Array::new(tokens, count, ()) })
1237    }
1238
1239    pub fn stack_var_reference_tokens(
1240        &self,
1241        stack_ref: StackVariableReference,
1242    ) -> Array<InstructionTextToken> {
1243        let mut stack_ref_raw = StackVariableReference::into_raw(stack_ref);
1244        let mut count = 0;
1245        let tokens = unsafe {
1246            BNGetDisassemblyTextRendererStackVariableReferenceTokens(
1247                self.handle.as_ptr(),
1248                &mut stack_ref_raw,
1249                &mut count,
1250            )
1251        };
1252        StackVariableReference::free_raw(stack_ref_raw);
1253        assert!(!tokens.is_null());
1254        unsafe { Array::new(tokens, count, ()) }
1255    }
1256
1257    pub fn integer_token(
1258        &self,
1259        int_token: InstructionTextToken,
1260        location: impl Into<Location>,
1261    ) -> Array<InstructionTextToken> {
1262        let location = location.into();
1263        let arch = location
1264            .arch
1265            .map(|a| a.handle)
1266            .unwrap_or_else(std::ptr::null_mut);
1267        let mut count = 0;
1268        let mut int_token_raw = InstructionTextToken::into_raw(int_token);
1269        let tokens = unsafe {
1270            BNGetDisassemblyTextRendererIntegerTokens(
1271                self.handle.as_ptr(),
1272                &mut int_token_raw,
1273                arch,
1274                location.addr,
1275                &mut count,
1276            )
1277        };
1278        InstructionTextToken::free_raw(int_token_raw);
1279        assert!(!tokens.is_null());
1280        unsafe { Array::new(tokens, count, ()) }
1281    }
1282
1283    pub fn wrap_comment(
1284        &self,
1285        cur_line: DisassemblyTextLine,
1286        comment: &str,
1287        has_auto_annotations: bool,
1288        leading_spaces: &str,
1289        indent_spaces: &str,
1290    ) -> Array<DisassemblyTextLine> {
1291        let cur_line_raw = DisassemblyTextLine::into_raw(cur_line);
1292        let comment_raw = comment.to_cstr();
1293        let leading_spaces_raw = leading_spaces.to_cstr();
1294        let indent_spaces_raw = indent_spaces.to_cstr();
1295        let mut count = 0;
1296        let lines = unsafe {
1297            BNDisassemblyTextRendererWrapComment(
1298                self.handle.as_ptr(),
1299                &cur_line_raw,
1300                &mut count,
1301                comment_raw.as_ref().as_ptr() as *const ffi::c_char,
1302                has_auto_annotations,
1303                leading_spaces_raw.as_ref().as_ptr() as *const ffi::c_char,
1304                indent_spaces_raw.as_ref().as_ptr() as *const ffi::c_char,
1305            )
1306        };
1307        DisassemblyTextLine::free_raw(cur_line_raw);
1308        assert!(!lines.is_null());
1309        unsafe { Array::new(lines, count, ()) }
1310    }
1311}
1312
1313impl ToOwned for DisassemblyTextRenderer {
1314    type Owned = Ref<Self>;
1315
1316    fn to_owned(&self) -> Self::Owned {
1317        unsafe { RefCountable::inc_ref(self) }
1318    }
1319}
1320
1321unsafe impl RefCountable for DisassemblyTextRenderer {
1322    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
1323        Ref::new(Self {
1324            handle: NonNull::new(BNNewDisassemblyTextRendererReference(
1325                handle.handle.as_ptr(),
1326            ))
1327            .unwrap(),
1328        })
1329    }
1330
1331    unsafe fn dec_ref(handle: &Self) {
1332        BNFreeDisassemblyTextRenderer(handle.handle.as_ptr());
1333    }
1334}
1335
1336// TODO: Make a builder for this.
1337#[derive(PartialEq, Eq, Hash)]
1338pub struct DisassemblySettings {
1339    pub(crate) handle: *mut BNDisassemblySettings,
1340}
1341
1342impl DisassemblySettings {
1343    pub fn ref_from_raw(handle: *mut BNDisassemblySettings) -> Ref<Self> {
1344        debug_assert!(!handle.is_null());
1345        unsafe { Ref::new(Self { handle }) }
1346    }
1347
1348    pub fn new() -> Ref<Self> {
1349        let handle = unsafe { BNCreateDisassemblySettings() };
1350        Self::ref_from_raw(handle)
1351    }
1352
1353    pub fn set_option(&self, option: DisassemblyOption, state: bool) {
1354        unsafe { BNSetDisassemblySettingsOption(self.handle, option, state) }
1355    }
1356
1357    pub fn is_option_set(&self, option: DisassemblyOption) -> bool {
1358        unsafe { BNIsDisassemblySettingsOptionSet(self.handle, option) }
1359    }
1360}
1361
1362impl ToOwned for DisassemblySettings {
1363    type Owned = Ref<Self>;
1364
1365    fn to_owned(&self) -> Self::Owned {
1366        unsafe { RefCountable::inc_ref(self) }
1367    }
1368}
1369
1370unsafe impl RefCountable for DisassemblySettings {
1371    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
1372        Ref::new(Self {
1373            handle: BNNewDisassemblySettingsReference(handle.handle),
1374        })
1375    }
1376
1377    unsafe fn dec_ref(handle: &Self) {
1378        BNFreeDisassemblySettings(handle.handle);
1379    }
1380}