summaryrefslogtreecommitdiff
path: root/rust/src
diff options
context:
space:
mode:
authorrbran <git@rubens.io>2025-04-10 12:58:27 +0000
committerMason Reed <35282038+emesare@users.noreply.github.com>2025-05-12 17:45:24 -0400
commit4d8feb4921cde4b70bb6db4ccd525a69205dee7f (patch)
treeedbd6845590fef0b2813ee30c16607e1ae8d8e32 /rust/src
parent067148afd1a8f9153bb5bfe6b5361b83e9f89ab6 (diff)
[Rust] Implement `LanguageRepresentation` and `LineFormatter`
Diffstat (limited to 'rust/src')
-rw-r--r--rust/src/function.rs37
-rw-r--r--rust/src/high_level_il.rs1
-rw-r--r--rust/src/high_level_il/function.rs7
-rw-r--r--rust/src/high_level_il/token_emitter.rs400
-rw-r--r--rust/src/language_representation.rs635
-rw-r--r--rust/src/lib.rs2
-rw-r--r--rust/src/line_formatter.rs172
-rw-r--r--rust/src/render_layer.rs4
-rw-r--r--rust/src/type_parser.rs2
-rw-r--r--rust/src/type_printer.rs2
10 files changed, 1257 insertions, 5 deletions
diff --git a/rust/src/function.rs b/rust/src/function.rs
index c1c29873..edde15a7 100644
--- a/rust/src/function.rs
+++ b/rust/src/function.rs
@@ -40,6 +40,7 @@ pub use binaryninjacore_sys::BNHighlightStandardColor as HighlightStandardColor;
use crate::architecture::RegisterId;
use crate::confidence::Conf;
use crate::high_level_il::HighLevelILFunction;
+use crate::language_representation::CoreLanguageRepresentationFunction;
use crate::low_level_il::{LiftedILFunction, RegularLowLevelILFunction};
use crate::medium_level_il::MediumLevelILFunction;
use crate::variable::{
@@ -476,6 +477,42 @@ impl Function {
}
}
+ /// Get the language representation of the function.
+ ///
+ /// * `language` - The language representation, ex. "Pseudo C".
+ pub fn language_representation<S: BnStrCompatible>(
+ &self,
+ language: S,
+ ) -> Option<Ref<CoreLanguageRepresentationFunction>> {
+ let lang_name = language.into_bytes_with_nul();
+ let repr = unsafe {
+ BNGetFunctionLanguageRepresentation(
+ self.handle,
+ lang_name.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ NonNull::new(repr)
+ .map(|handle| unsafe { CoreLanguageRepresentationFunction::ref_from_raw(handle) })
+ }
+
+ /// Get the language representation of the function, if available.
+ ///
+ /// * `language` - The language representation, ex. "Pseudo C".
+ pub fn language_representation_if_available<S: BnStrCompatible>(
+ &self,
+ language: S,
+ ) -> Option<Ref<CoreLanguageRepresentationFunction>> {
+ let lang_name = language.into_bytes_with_nul();
+ let repr = unsafe {
+ BNGetFunctionLanguageRepresentationIfAvailable(
+ self.handle,
+ lang_name.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ NonNull::new(repr)
+ .map(|handle| unsafe { CoreLanguageRepresentationFunction::ref_from_raw(handle) })
+ }
+
pub fn high_level_il(&self, full_ast: bool) -> Result<Ref<HighLevelILFunction>, ()> {
unsafe {
let hlil_ptr = BNGetFunctionHighLevelIL(self.handle);
diff --git a/rust/src/high_level_il.rs b/rust/src/high_level_il.rs
index adb44a7e..8d02720d 100644
--- a/rust/src/high_level_il.rs
+++ b/rust/src/high_level_il.rs
@@ -4,6 +4,7 @@ mod function;
mod instruction;
mod lift;
pub mod operation;
+pub mod token_emitter;
pub use self::block::*;
pub use self::function::*;
diff --git a/rust/src/high_level_il/function.rs b/rust/src/high_level_il/function.rs
index 0813ad04..a65a244c 100644
--- a/rust/src/high_level_il/function.rs
+++ b/rust/src/high_level_il/function.rs
@@ -15,12 +15,17 @@ pub struct HighLevelILFunction {
}
impl HighLevelILFunction {
+ pub(crate) unsafe fn from_raw(handle: *mut BNHighLevelILFunction, full_ast: bool) -> Self {
+ debug_assert!(!handle.is_null());
+ Self { handle, full_ast }
+ }
+
pub(crate) unsafe fn ref_from_raw(
handle: *mut BNHighLevelILFunction,
full_ast: bool,
) -> Ref<Self> {
debug_assert!(!handle.is_null());
- Self { handle, full_ast }.to_owned()
+ Ref::new(Self { handle, full_ast })
}
pub fn instruction_from_index(
diff --git a/rust/src/high_level_il/token_emitter.rs b/rust/src/high_level_il/token_emitter.rs
new file mode 100644
index 00000000..2f1236a9
--- /dev/null
+++ b/rust/src/high_level_il/token_emitter.rs
@@ -0,0 +1,400 @@
+use std::ptr::NonNull;
+
+use binaryninjacore_sys::*;
+
+use crate::disassembly::{
+ DisassemblySettings, DisassemblyTextLine, InstructionTextToken, InstructionTextTokenContext,
+ InstructionTextTokenType,
+};
+use crate::high_level_il::HighLevelILFunction;
+use crate::language_representation::{OperatorPrecedence, SymbolDisplayResult, SymbolDisplayType};
+use crate::rc::{Array, Ref, RefCountable};
+use crate::variable::Variable;
+
+pub type ScopeType = BNScopeType;
+pub type TokenEmitterExpr = BNTokenEmitterExpr;
+pub type BraceRequirement = BNBraceRequirement;
+
+#[derive(PartialEq, Eq, Hash)]
+pub struct HighLevelILTokenEmitter {
+ handle: NonNull<BNHighLevelILTokenEmitter>,
+}
+
+impl HighLevelILTokenEmitter {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNHighLevelILTokenEmitter>) -> Self {
+ Self { handle }
+ }
+
+ /// Returns the list of [`InstructionTextToken`] on the current line.
+ pub fn current_tokens(&self) -> Array<InstructionTextToken> {
+ let mut count = 0;
+ let array =
+ unsafe { BNHighLevelILTokenEmitterGetCurrentTokens(self.handle.as_ptr(), &mut count) };
+ unsafe { Array::new(array, count, ()) }
+ }
+
+ /// Returns the list of [`DisassemblyTextLine`] in the output.
+ pub fn lines(&self) -> Array<DisassemblyTextLine> {
+ let mut count = 0;
+ let array = unsafe { BNHighLevelILTokenEmitterGetLines(self.handle.as_ptr(), &mut count) };
+ unsafe { Array::new(array, count, ()) }
+ }
+
+ pub fn prepend_collapse_blank_indicator(&self) {
+ unsafe { BNHighLevelILTokenPrependCollapseBlankIndicator(self.handle.as_ptr()) };
+ }
+
+ pub fn prepend_collapse_indicator(&self, context: InstructionTextTokenContext, hash: u64) {
+ unsafe {
+ BNHighLevelILTokenPrependCollapseIndicator(self.handle.as_ptr(), context.into(), hash)
+ };
+ }
+
+ pub fn has_collapsable_regions(&self) -> bool {
+ unsafe { BNHighLevelILTokenEmitterHasCollapsableRegions(self.handle.as_ptr()) }
+ }
+
+ pub fn set_has_collapsable_regions(&self, state: bool) {
+ unsafe { BNHighLevelILTokenEmitterSetHasCollapsableRegions(self.handle.as_ptr(), state) };
+ }
+
+ pub fn append(&self, token: InstructionTextToken) {
+ let mut raw_token = InstructionTextToken::into_raw(token);
+ unsafe { BNHighLevelILTokenEmitterAppend(self.handle.as_ptr(), &mut raw_token) };
+ InstructionTextToken::free_raw(raw_token);
+ }
+
+ /// Starts a new line in the output.
+ pub fn init_line(&self) {
+ unsafe { BNHighLevelILTokenEmitterInitLine(self.handle.as_ptr()) };
+ }
+
+ // TODO: Difference from `init_line`?
+ /// Starts a new line in the output.
+ pub fn new_line(&self) {
+ unsafe { BNHighLevelILTokenEmitterNewLine(self.handle.as_ptr()) };
+ }
+
+ /// Increases the indentation level by one.
+ pub fn increase_indent(&self) {
+ unsafe { BNHighLevelILTokenEmitterIncreaseIndent(self.handle.as_ptr()) };
+ }
+
+ /// Decreases the indentation level by one.
+ pub fn decrease_indent(&self) {
+ unsafe { BNHighLevelILTokenEmitterDecreaseIndent(self.handle.as_ptr()) };
+ }
+
+ /// Indicates that visual separation of scopes is desirable at the current position.
+ ///
+ /// By default, this will insert a blank line, but this can be configured by the user.
+ pub fn scope_separator(&self) {
+ unsafe { BNHighLevelILTokenEmitterScopeSeparator(self.handle.as_ptr()) };
+ }
+
+ /// Begins a new scope. Insertion of newlines and braces will be handled using the current settings.
+ pub fn begin_scope(&self, ty: ScopeType) {
+ unsafe { BNHighLevelILTokenEmitterBeginScope(self.handle.as_ptr(), ty) };
+ }
+
+ /// Ends the current scope.
+ ///
+ /// The type `ty` should be equal to what was passed to [`HighLevelILTokenEmitter::begin_scope`].
+ pub fn end_scope(&self, ty: ScopeType) {
+ unsafe { BNHighLevelILTokenEmitterEndScope(self.handle.as_ptr(), ty) };
+ }
+
+ /// Continues the previous scope with a new associated scope. This is most commonly used for else statements.
+ ///
+ /// If `force_same_line` is true, the continuation will always be placed on the same line as the previous scope.
+ pub fn scope_continuation(&self, force_same_line: bool) {
+ unsafe {
+ BNHighLevelILTokenEmitterScopeContinuation(self.handle.as_ptr(), force_same_line)
+ };
+ }
+
+ /// Finalizes the previous scope, indicating that there are no more associated scopes.
+ pub fn finalize_scope(&self) {
+ unsafe { BNHighLevelILTokenEmitterFinalizeScope(self.handle.as_ptr()) };
+ }
+
+ /// Forces there to be no indentation for the next line.
+ pub fn no_indent_for_this_line(&self) {
+ unsafe { BNHighLevelILTokenEmitterNoIndentForThisLine(self.handle.as_ptr()) };
+ }
+
+ /// Begins a region of tokens that always have zero confidence.
+ pub fn begin_force_zero_confidence(&self) {
+ unsafe { BNHighLevelILTokenEmitterBeginForceZeroConfidence(self.handle.as_ptr()) };
+ }
+
+ /// Ends a region of tokens that always have zero confidence.
+ pub fn end_force_zero_confidence(&self) {
+ unsafe { BNHighLevelILTokenEmitterEndForceZeroConfidence(self.handle.as_ptr()) };
+ }
+
+ /// Sets the current expression. Returning the [`CurrentTokenEmitterExpr`] which when dropped
+ /// will restore the previously active [`TokenEmitterExpr`].
+ pub fn set_current_expr(&self, expr: TokenEmitterExpr) -> CurrentTokenEmitterExpr {
+ let previous_expr =
+ unsafe { BNHighLevelILTokenEmitterSetCurrentExpr(self.handle.as_ptr(), expr) };
+ CurrentTokenEmitterExpr::new(self.to_owned(), expr, previous_expr)
+ }
+
+ fn restore_current_expr(&self, expr: TokenEmitterExpr) {
+ unsafe { BNHighLevelILTokenEmitterRestoreCurrentExpr(self.handle.as_ptr(), expr) };
+ }
+
+ /// Finalizes the outputted lines.
+ pub fn finalize(&self) {
+ unsafe { BNHighLevelILTokenEmitterFinalize(self.handle.as_ptr()) };
+ }
+
+ /// Appends `(`.
+ pub fn append_open_paren(&self) {
+ unsafe { BNHighLevelILTokenEmitterAppendOpenParen(self.handle.as_ptr()) };
+ }
+
+ /// Appends `)`.
+ pub fn append_close_paren(&self) {
+ unsafe { BNHighLevelILTokenEmitterAppendCloseParen(self.handle.as_ptr()) };
+ }
+
+ /// Appends `[`.
+ pub fn append_open_bracket(&self) {
+ unsafe { BNHighLevelILTokenEmitterAppendOpenBracket(self.handle.as_ptr()) };
+ }
+
+ /// Appends `]`.
+ pub fn append_close_bracket(&self) {
+ unsafe { BNHighLevelILTokenEmitterAppendCloseBracket(self.handle.as_ptr()) };
+ }
+
+ /// Appends `{`.
+ pub fn append_open_brace(&self) {
+ unsafe { BNHighLevelILTokenEmitterAppendOpenBrace(self.handle.as_ptr()) };
+ }
+
+ /// Appends `}`.
+ pub fn append_close_brace(&self) {
+ unsafe { BNHighLevelILTokenEmitterAppendCloseBrace(self.handle.as_ptr()) };
+ }
+
+ /// Appends `;`.
+ pub fn append_semicolon(&self) {
+ unsafe { BNHighLevelILTokenEmitterAppendSemicolon(self.handle.as_ptr()) };
+ }
+
+ /// Sets the requirement for insertion of braces around scopes in the output.
+ pub fn set_brace_requirement(&self, required: BraceRequirement) {
+ unsafe { BNHighLevelILTokenEmitterSetBraceRequirement(self.handle.as_ptr(), required) };
+ }
+
+ /// Sets whether cases within switch statements should always have braces around them.
+ pub fn set_braces_around_switch_cases(&self, braces: bool) {
+ unsafe {
+ BNHighLevelILTokenEmitterSetBracesAroundSwitchCases(self.handle.as_ptr(), braces)
+ };
+ }
+
+ /// Sets whether braces should default to being on the same line as the statement that begins the scope.
+ ///
+ /// If the user has explicitly set a preference, this setting will be ignored and the user's preference will be used instead.
+ pub fn set_default_braces_on_same_line(&self, same_line: bool) {
+ unsafe {
+ BNHighLevelILTokenEmitterSetDefaultBracesOnSameLine(self.handle.as_ptr(), same_line)
+ };
+ }
+
+ /// Sets whether omitting braces around single-line scopes is allowed.
+ pub fn set_simple_scope_allowed(&self, allowed: bool) {
+ unsafe { BNHighLevelILTokenEmitterSetSimpleScopeAllowed(self.handle.as_ptr(), allowed) };
+ }
+
+ pub fn brace_requirement(&self) -> BraceRequirement {
+ unsafe { BNHighLevelILTokenEmitterGetBraceRequirement(self.handle.as_ptr()) }
+ }
+
+ pub fn has_braces_around_switch_cases(&self) -> bool {
+ unsafe { BNHighLevelILTokenEmitterHasBracesAroundSwitchCases(self.handle.as_ptr()) }
+ }
+
+ pub fn default_braces_on_same_line(&self) -> bool {
+ unsafe { BNHighLevelILTokenEmitterGetDefaultBracesOnSameLine(self.handle.as_ptr()) }
+ }
+
+ pub fn is_simple_scope_allowed(&self) -> bool {
+ unsafe { BNHighLevelILTokenEmitterIsSimpleScopeAllowed(self.handle.as_ptr()) }
+ }
+
+ /// Appends a size token for the given size in the High Level IL syntax.
+ pub fn append_size_token(&self, size: usize, ty: InstructionTextTokenType) {
+ unsafe { BNAddHighLevelILSizeToken(size, ty, self.handle.as_ptr()) }
+ }
+
+ /// Appends a floating point size token for the given size in the High Level IL syntax.
+ pub fn append_float_size_token(&self, size: usize, ty: InstructionTextTokenType) {
+ unsafe { BNAddHighLevelILFloatSizeToken(size, ty, self.handle.as_ptr()) }
+ }
+
+ /// Appends tokens for access to a variable.
+ pub fn append_var_text_token(
+ &self,
+ func: &HighLevelILFunction,
+ var: Variable,
+ expr_index: usize,
+ size: usize,
+ ) {
+ unsafe {
+ BNAddHighLevelILVarTextToken(
+ func.handle,
+ &BNVariable::from(var),
+ self.handle.as_ptr(),
+ expr_index,
+ size,
+ )
+ }
+ }
+
+ /// Appends tokens for a constant integer value.
+ pub fn append_integer_text_token(
+ &self,
+ func: &HighLevelILFunction,
+ expr_index: usize,
+ val: i64,
+ size: usize,
+ ) {
+ unsafe {
+ BNAddHighLevelILIntegerTextToken(
+ func.handle,
+ expr_index,
+ val,
+ size,
+ self.handle.as_ptr(),
+ )
+ }
+ }
+
+ /// Appends tokens for accessing an array by constant index.
+ pub fn append_array_index_token(
+ &self,
+ func: &HighLevelILFunction,
+ expr_index: usize,
+ val: i64,
+ size: usize,
+ address: Option<u64>,
+ ) {
+ unsafe {
+ BNAddHighLevelILArrayIndexToken(
+ func.handle,
+ expr_index,
+ val,
+ size,
+ self.handle.as_ptr(),
+ address.unwrap_or(0),
+ )
+ }
+ }
+
+ /// Appends tokens for displaying a constant pointer value.
+ ///
+ /// If `allow_short_string` is true, then a string will be shown even if it is "short".
+ pub fn append_pointer_text_token(
+ &self,
+ func: &HighLevelILFunction,
+ expr_index: usize,
+ val: i64,
+ settings: &DisassemblySettings,
+ symbol_display: SymbolDisplayType,
+ precedence: OperatorPrecedence,
+ allow_short_string: bool,
+ ) -> SymbolDisplayResult {
+ unsafe {
+ BNAddHighLevelILPointerTextToken(
+ func.handle,
+ expr_index,
+ val,
+ self.handle.as_ptr(),
+ settings.handle,
+ symbol_display,
+ precedence,
+ allow_short_string,
+ )
+ }
+ }
+
+ /// Appends tokens for a constant value.
+ pub fn append_constant_text_token(
+ &self,
+ func: &HighLevelILFunction,
+ expr_index: usize,
+ val: i64,
+ size: usize,
+ settings: &DisassemblySettings,
+ precedence: OperatorPrecedence,
+ ) {
+ unsafe {
+ BNAddHighLevelILConstantTextToken(
+ func.handle,
+ expr_index,
+ val,
+ size,
+ self.handle.as_ptr(),
+ settings.handle,
+ precedence,
+ )
+ }
+ }
+}
+
+unsafe impl Send for HighLevelILTokenEmitter {}
+unsafe impl Sync for HighLevelILTokenEmitter {}
+
+unsafe impl RefCountable for HighLevelILTokenEmitter {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ let handle = BNNewHighLevelILTokenEmitterReference(handle.handle.as_ptr());
+ let handle = NonNull::new(handle).unwrap();
+ Ref::new(HighLevelILTokenEmitter { handle })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeHighLevelILTokenEmitter(handle.handle.as_ptr())
+ }
+}
+
+impl ToOwned for HighLevelILTokenEmitter {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+/// Manages the currently active [`TokenEmitterExpr`] for the given [`HighLevelILTokenEmitter`].
+///
+/// When this object is destroyed, the previously active [`TokenEmitterExpr`] will become active again.
+pub struct CurrentTokenEmitterExpr {
+ pub emitter: Ref<HighLevelILTokenEmitter>,
+ pub expr: TokenEmitterExpr,
+ pub previous_expr: TokenEmitterExpr,
+}
+
+impl CurrentTokenEmitterExpr {
+ pub fn new(
+ emitter: Ref<HighLevelILTokenEmitter>,
+ expr: TokenEmitterExpr,
+ previous_expr: TokenEmitterExpr,
+ ) -> Self {
+ Self {
+ emitter,
+ expr,
+ previous_expr,
+ }
+ }
+}
+
+impl Drop for CurrentTokenEmitterExpr {
+ fn drop(&mut self) {
+ self.emitter.restore_current_expr(self.previous_expr);
+ }
+}
diff --git a/rust/src/language_representation.rs b/rust/src/language_representation.rs
new file mode 100644
index 00000000..50e7b710
--- /dev/null
+++ b/rust/src/language_representation.rs
@@ -0,0 +1,635 @@
+use std::ffi::{c_char, c_void};
+use std::mem::MaybeUninit;
+use std::ptr::NonNull;
+
+use binaryninjacore_sys::*;
+
+use crate::architecture::{Architecture, CoreArchitecture};
+use crate::basic_block::{BasicBlock, BlockContext};
+use crate::binary_view::BinaryView;
+use crate::disassembly::{DisassemblySettings, DisassemblyTextLine};
+use crate::function::{Function, HighlightColor};
+use crate::high_level_il::token_emitter::HighLevelILTokenEmitter;
+use crate::high_level_il::{HighLevelILFunction, HighLevelInstructionIndex};
+use crate::line_formatter::CoreLineFormatter;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+use crate::type_parser::CoreTypeParser;
+use crate::type_printer::CoreTypePrinter;
+
+pub type InstructionTextTokenContext = BNInstructionTextTokenContext;
+pub type ScopeType = BNScopeType;
+pub type BraceRequirement = BNBraceRequirement;
+pub type SymbolDisplayType = BNSymbolDisplayType;
+pub type OperatorPrecedence = BNOperatorPrecedence;
+pub type SymbolDisplayResult = BNSymbolDisplayResult;
+
+pub fn register_language_representation_function_type<
+ C: LanguageRepresentationFunctionType,
+ F: FnOnce(CoreLanguageRepresentationFunctionType) -> C,
+ B: BnStrCompatible,
+>(
+ creator: F,
+ name: B,
+) -> CoreLanguageRepresentationFunctionType {
+ let custom = Box::leak(Box::new(MaybeUninit::uninit()));
+ let mut callbacks = BNCustomLanguageRepresentationFunctionType {
+ context: custom as *mut MaybeUninit<C> as *mut c_void,
+ create: Some(cb_create::<C>),
+ isValid: Some(cb_is_valid::<C>),
+ getTypePrinter: Some(cb_get_type_printer::<C>),
+ getTypeParser: Some(cb_get_type_parser::<C>),
+ getLineFormatter: Some(cb_get_line_formatter::<C>),
+ getFunctionTypeTokens: Some(cb_get_function_type_tokens::<C>),
+ freeLines: Some(cb_free_lines),
+ };
+ let name = name.into_bytes_with_nul();
+ let core = unsafe {
+ BNRegisterLanguageRepresentationFunctionType(
+ name.as_ref().as_ptr() as *const c_char,
+ &mut callbacks,
+ )
+ };
+ let core =
+ unsafe { CoreLanguageRepresentationFunctionType::from_raw(NonNull::new(core).unwrap()) };
+ custom.write(creator(core));
+ core
+}
+
+pub trait LanguageRepresentationFunction: Send + Sync {
+ fn on_token_emitter_init(&self, tokens: &HighLevelILTokenEmitter);
+
+ fn expr_text(
+ &self,
+ il: &HighLevelILFunction,
+ expr_index: HighLevelInstructionIndex,
+ tokens: &HighLevelILTokenEmitter,
+ settings: &DisassemblySettings,
+ as_full_ast: bool,
+ precedence: OperatorPrecedence,
+ statement: bool,
+ );
+
+ fn begin_lines(
+ &self,
+ il: &HighLevelILFunction,
+ expr_index: HighLevelInstructionIndex,
+ tokens: &HighLevelILTokenEmitter,
+ );
+
+ fn end_lines(
+ &self,
+ il: &HighLevelILFunction,
+ expr_index: HighLevelInstructionIndex,
+ tokens: &HighLevelILTokenEmitter,
+ );
+
+ fn comment_start_string(&self) -> &str;
+
+ fn comment_end_string(&self) -> &str;
+
+ fn annotation_start_string(&self) -> &str;
+
+ fn annotation_end_string(&self) -> &str;
+}
+
+pub trait LanguageRepresentationFunctionType: Send + Sync {
+ fn create(
+ &self,
+ arch: &CoreArchitecture,
+ owner: &Function,
+ high_level_il: &HighLevelILFunction,
+ ) -> Ref<CoreLanguageRepresentationFunction>;
+
+ fn is_valid(&self, view: &BinaryView) -> bool;
+
+ fn type_printer(&self) -> Option<CoreTypePrinter> {
+ None
+ }
+
+ fn type_parser(&self) -> Option<CoreTypeParser> {
+ None
+ }
+
+ fn line_formatter(&self) -> Option<CoreLineFormatter> {
+ None
+ }
+
+ fn function_type_tokens(
+ &self,
+ func: &Function,
+ settings: &DisassemblySettings,
+ ) -> Vec<DisassemblyTextLine>;
+}
+
+// NOTE static, it never gets freed, so we can clone/copy it
+#[repr(transparent)]
+#[derive(Clone, Copy)]
+pub struct CoreLanguageRepresentationFunctionType {
+ handle: NonNull<BNLanguageRepresentationFunctionType>,
+}
+
+impl CoreLanguageRepresentationFunctionType {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNLanguageRepresentationFunctionType>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) fn as_raw(&self) -> *mut BNLanguageRepresentationFunctionType {
+ self.handle.as_ptr()
+ }
+
+ pub fn from_name<S: BnStrCompatible>(name: S) -> Option<Self> {
+ let name = name.into_bytes_with_nul();
+ let result = unsafe {
+ BNGetLanguageRepresentationFunctionTypeByName(name.as_ref().as_ptr() as *const c_char)
+ };
+ NonNull::new(result).map(|handle| unsafe { Self::from_raw(handle) })
+ }
+
+ pub fn all() -> Array<Self> {
+ let mut count = 0;
+ let result = unsafe { BNGetLanguageRepresentationFunctionTypeList(&mut count) };
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ pub fn tokens(
+ &self,
+ func: &Function,
+ settings: &DisassemblySettings,
+ ) -> Array<DisassemblyTextLine> {
+ let mut count = 0;
+ let result = unsafe {
+ BNGetLanguageRepresentationFunctionTypeFunctionTypeTokens(
+ self.handle.as_ptr(),
+ func.handle,
+ settings.handle,
+ &mut count,
+ )
+ };
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ pub fn name(&self) -> BnString {
+ unsafe {
+ BnString::from_raw(BNGetLanguageRepresentationFunctionTypeName(
+ self.handle.as_ptr(),
+ ))
+ }
+ }
+
+ pub fn create(&self, func: &Function) -> Ref<CoreLanguageRepresentationFunction> {
+ let repr_func = unsafe {
+ BNCreateLanguageRepresentationFunction(
+ self.handle.as_ptr(),
+ func.arch().handle,
+ func.handle,
+ match func.high_level_il(false) {
+ Ok(hlil) => hlil.handle,
+ Err(_) => std::ptr::null_mut(),
+ },
+ )
+ };
+
+ unsafe {
+ CoreLanguageRepresentationFunction::ref_from_raw(NonNull::new(repr_func).unwrap())
+ }
+ }
+
+ pub fn is_valid(&self, view: &BinaryView) -> bool {
+ unsafe { BNIsLanguageRepresentationFunctionTypeValid(self.handle.as_ptr(), view.handle) }
+ }
+
+ pub fn printer(&self) -> CoreTypePrinter {
+ let type_printer =
+ unsafe { BNGetLanguageRepresentationFunctionTypePrinter(self.handle.as_ptr()) };
+ unsafe { CoreTypePrinter::from_raw(NonNull::new(type_printer).unwrap()) }
+ }
+
+ pub fn parser(&self) -> CoreTypeParser {
+ let type_parser =
+ unsafe { BNGetLanguageRepresentationFunctionTypeParser(self.handle.as_ptr()) };
+ unsafe { CoreTypeParser::from_raw(NonNull::new(type_parser).unwrap()) }
+ }
+
+ pub fn line_formatter(&self) -> CoreLineFormatter {
+ let formatter =
+ unsafe { BNGetLanguageRepresentationFunctionTypeLineFormatter(self.handle.as_ptr()) };
+ CoreLineFormatter::from_raw(NonNull::new(formatter).unwrap())
+ }
+}
+
+impl CoreArrayProvider for CoreLanguageRepresentationFunctionType {
+ type Raw = *mut BNLanguageRepresentationFunctionType;
+ type Context = ();
+ type Wrapped<'a> = &'a CoreLanguageRepresentationFunctionType;
+}
+
+unsafe impl CoreArrayProviderInner for CoreLanguageRepresentationFunctionType {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeLanguageRepresentationFunctionTypeList(raw)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ // SAFETY: CoreLanguageRepresentationFunctionType and BNCoreLanguageRepresentationFunctionType
+ // transparent
+ std::mem::transmute::<
+ &*mut BNLanguageRepresentationFunctionType,
+ &CoreLanguageRepresentationFunctionType,
+ >(raw)
+ }
+}
+
+pub struct CoreLanguageRepresentationFunction {
+ handle: NonNull<BNLanguageRepresentationFunction>,
+}
+
+impl CoreLanguageRepresentationFunction {
+ pub(crate) unsafe fn ref_from_raw(
+ handle: NonNull<BNLanguageRepresentationFunction>,
+ ) -> Ref<Self> {
+ unsafe { Ref::new(Self { handle }) }
+ }
+
+ pub fn new<C: LanguageRepresentationFunction, A: Architecture>(
+ repr_type: &CoreLanguageRepresentationFunctionType,
+ repr_context: C,
+ arch: &A,
+ func: &Function,
+ high_level_il: &HighLevelILFunction,
+ ) -> Ref<Self> {
+ let core_arch: &CoreArchitecture = arch.as_ref();
+ let context: &mut C = Box::leak(Box::new(repr_context));
+ let mut callbacks = BNCustomLanguageRepresentationFunction {
+ context: context as *mut C as *mut c_void,
+ freeObject: Some(cb_free_object::<C>),
+ externalRefTaken: Some(cb_external_ref_taken::<C>),
+ externalRefReleased: Some(cb_external_ref_released::<C>),
+ initTokenEmitter: Some(cb_init_token_emitter::<C>),
+ getExprText: Some(cb_get_expr_text::<C>),
+ beginLines: Some(cb_begin_lines::<C>),
+ endLines: Some(cb_end_lines::<C>),
+ getCommentStartString: Some(cb_get_comment_start_string::<C>),
+ getCommentEndString: Some(cb_get_comment_end_string::<C>),
+ getAnnotationStartString: Some(cb_get_annotation_start_string::<C>),
+ getAnnotationEndString: Some(cb_get_annotation_end_string::<C>),
+ };
+ let handle = unsafe {
+ BNCreateCustomLanguageRepresentationFunction(
+ repr_type.as_raw(),
+ core_arch.handle,
+ func.handle,
+ high_level_il.handle,
+ &mut callbacks,
+ )
+ };
+ unsafe { Self::ref_from_raw(NonNull::new(handle).unwrap()) }
+ }
+
+ pub fn expr_text(
+ &self,
+ il: &HighLevelILFunction,
+ expr_index: HighLevelInstructionIndex,
+ settings: &DisassemblySettings,
+ as_full_ast: bool,
+ precedence: OperatorPrecedence,
+ statement: bool,
+ ) -> Array<DisassemblyTextLine> {
+ let mut count = 0;
+ let result = unsafe {
+ BNGetLanguageRepresentationFunctionExprText(
+ self.handle.as_ptr(),
+ il.handle,
+ expr_index.0,
+ settings.handle,
+ as_full_ast,
+ precedence,
+ statement,
+ &mut count,
+ )
+ };
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ pub fn linear_lines(
+ &self,
+ il: &HighLevelILFunction,
+ expr_index: HighLevelInstructionIndex,
+ settings: &DisassemblySettings,
+ as_full_ast: bool,
+ ) -> Array<DisassemblyTextLine> {
+ let mut count = 0;
+ let result = unsafe {
+ BNGetLanguageRepresentationFunctionLinearLines(
+ self.handle.as_ptr(),
+ il.handle,
+ expr_index.0,
+ settings.handle,
+ as_full_ast,
+ &mut count,
+ )
+ };
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ pub fn block_lines<C: BlockContext>(
+ &self,
+ block: &BasicBlock<C>,
+ settings: &DisassemblySettings,
+ ) -> Array<DisassemblyTextLine> {
+ let mut count = 0;
+ let result = unsafe {
+ BNGetLanguageRepresentationFunctionBlockLines(
+ self.handle.as_ptr(),
+ block.handle,
+ settings.handle,
+ &mut count,
+ )
+ };
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ pub fn highlight<C: BlockContext>(&self, block: &BasicBlock<C>) -> HighlightColor {
+ let result = unsafe {
+ BNGetLanguageRepresentationFunctionHighlight(self.handle.as_ptr(), block.handle)
+ };
+ result.into()
+ }
+
+ pub fn get_type(&self) -> CoreLanguageRepresentationFunctionType {
+ let repr_type = unsafe { BNGetLanguageRepresentationType(self.handle.as_ptr()) };
+ unsafe {
+ CoreLanguageRepresentationFunctionType::from_raw(NonNull::new(repr_type).unwrap())
+ }
+ }
+
+ pub fn arch(&self) -> CoreArchitecture {
+ let arch = unsafe { BNGetLanguageRepresentationArchitecture(self.handle.as_ptr()) };
+ unsafe { CoreArchitecture::from_raw(arch) }
+ }
+
+ pub fn owner_function(&self) -> Ref<Function> {
+ let func = unsafe { BNGetLanguageRepresentationOwnerFunction(self.handle.as_ptr()) };
+ unsafe { Function::ref_from_raw(func) }
+ }
+
+ pub fn hlil(&self) -> Ref<HighLevelILFunction> {
+ let hlil = unsafe { BNGetLanguageRepresentationILFunction(self.handle.as_ptr()) };
+ unsafe { HighLevelILFunction::ref_from_raw(hlil, false) }
+ }
+
+ pub fn comment_start_string(&self) -> BnString {
+ unsafe {
+ BnString::from_raw(BNGetLanguageRepresentationFunctionCommentStartString(
+ self.handle.as_ptr(),
+ ))
+ }
+ }
+
+ pub fn comment_end_string(&self) -> BnString {
+ unsafe {
+ BnString::from_raw(BNGetLanguageRepresentationFunctionCommentEndString(
+ self.handle.as_ptr(),
+ ))
+ }
+ }
+
+ pub fn annotation_start_string(&self) -> BnString {
+ unsafe {
+ BnString::from_raw(BNGetLanguageRepresentationFunctionAnnotationStartString(
+ self.handle.as_ptr(),
+ ))
+ }
+ }
+
+ pub fn annotation_end_string(&self) -> BnString {
+ unsafe {
+ BnString::from_raw(BNGetLanguageRepresentationFunctionAnnotationEndString(
+ self.handle.as_ptr(),
+ ))
+ }
+ }
+}
+
+unsafe impl RefCountable for CoreLanguageRepresentationFunction {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Self::ref_from_raw(
+ NonNull::new(BNNewLanguageRepresentationFunctionReference(
+ handle.handle.as_ptr(),
+ ))
+ .unwrap(),
+ )
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeLanguageRepresentationFunction(handle.handle.as_ptr())
+ }
+}
+
+impl ToOwned for CoreLanguageRepresentationFunction {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { <Self as RefCountable>::inc_ref(self) }
+ }
+}
+
+unsafe extern "C" fn cb_create<C: LanguageRepresentationFunctionType>(
+ ctxt: *mut c_void,
+ arch: *mut BNArchitecture,
+ owner: *mut BNFunction,
+ high_level_il: *mut BNHighLevelILFunction,
+) -> *mut BNLanguageRepresentationFunction {
+ let ctxt = ctxt as *mut C;
+ let arch = CoreArchitecture::from_raw(arch);
+ let owner = Function::from_raw(owner);
+ let high_level_il = HighLevelILFunction {
+ full_ast: false,
+ handle: high_level_il,
+ };
+ let result = (*ctxt).create(&arch, &owner, &high_level_il);
+ Ref::into_raw(result).handle.as_ptr()
+}
+
+unsafe extern "C" fn cb_is_valid<C: LanguageRepresentationFunctionType>(
+ ctxt: *mut c_void,
+ view: *mut BNBinaryView,
+) -> bool {
+ let ctxt = ctxt as *mut C;
+ let view = BinaryView::from_raw(view);
+ (*ctxt).is_valid(&view)
+}
+
+unsafe extern "C" fn cb_get_type_printer<C: LanguageRepresentationFunctionType>(
+ ctxt: *mut c_void,
+) -> *mut BNTypePrinter {
+ let ctxt = ctxt as *mut C;
+ match (*ctxt).type_printer() {
+ None => std::ptr::null_mut(),
+ Some(printer) => printer.handle.as_ptr(),
+ }
+}
+
+unsafe extern "C" fn cb_get_type_parser<C: LanguageRepresentationFunctionType>(
+ ctxt: *mut c_void,
+) -> *mut BNTypeParser {
+ let ctxt = ctxt as *mut C;
+ match (*ctxt).type_parser() {
+ None => std::ptr::null_mut(),
+ Some(parser) => parser.handle.as_ptr(),
+ }
+}
+
+unsafe extern "C" fn cb_get_line_formatter<C: LanguageRepresentationFunctionType>(
+ ctxt: *mut c_void,
+) -> *mut BNLineFormatter {
+ let ctxt = ctxt as *mut C;
+ match (*ctxt).line_formatter() {
+ None => std::ptr::null_mut(),
+ Some(formatter) => formatter.handle.as_ptr(),
+ }
+}
+
+unsafe extern "C" fn cb_get_function_type_tokens<C: LanguageRepresentationFunctionType>(
+ ctxt: *mut c_void,
+ func: *mut BNFunction,
+ settings: *mut BNDisassemblySettings,
+ count: *mut usize,
+) -> *mut BNDisassemblyTextLine {
+ let ctxt = ctxt as *mut C;
+ let func = Function::from_raw(func);
+ let settings = DisassemblySettings { handle: settings };
+ let result = (*ctxt).function_type_tokens(&func, &settings);
+ *count = result.len();
+ let result: Box<[BNDisassemblyTextLine]> = result
+ .into_iter()
+ .map(DisassemblyTextLine::into_raw)
+ .collect();
+ // NOTE freed by function_type_free_lines_ffi
+ Box::leak(result).as_mut_ptr()
+}
+
+unsafe extern "C" fn cb_free_lines(
+ _ctxt: *mut c_void,
+ lines: *mut BNDisassemblyTextLine,
+ count: usize,
+) {
+ let lines: Box<[BNDisassemblyTextLine]> =
+ Box::from_raw(core::slice::from_raw_parts_mut(lines, count));
+ for line in lines {
+ DisassemblyTextLine::free_raw(line);
+ }
+}
+
+unsafe extern "C" fn cb_free_object<C: LanguageRepresentationFunction>(ctxt: *mut c_void) {
+ let ctxt = ctxt as *mut C;
+ drop(Box::from_raw(ctxt))
+}
+
+unsafe extern "C" fn cb_external_ref_taken<C: LanguageRepresentationFunction>(_ctxt: *mut c_void) {
+ // TODO Make an Arc? conflict with free?
+}
+
+unsafe extern "C" fn cb_external_ref_released<C: LanguageRepresentationFunction>(
+ _ctxt: *mut c_void,
+) {
+ // TODO Make an Arc? conflict with free?
+}
+
+unsafe extern "C" fn cb_init_token_emitter<C: LanguageRepresentationFunction>(
+ ctxt: *mut c_void,
+ tokens: *mut BNHighLevelILTokenEmitter,
+) {
+ let ctxt = ctxt as *mut C;
+ let tokens = HighLevelILTokenEmitter::from_raw(NonNull::new(tokens).unwrap());
+ (*ctxt).on_token_emitter_init(&tokens)
+}
+
+unsafe extern "C" fn cb_get_expr_text<C: LanguageRepresentationFunction>(
+ ctxt: *mut c_void,
+ il: *mut BNHighLevelILFunction,
+ expr_index: usize,
+ tokens: *mut BNHighLevelILTokenEmitter,
+ settings: *mut BNDisassemblySettings,
+ as_full_ast: bool,
+ precedence: BNOperatorPrecedence,
+ statement: bool,
+) {
+ let ctxt = ctxt as *mut C;
+ let il = HighLevelILFunction {
+ full_ast: as_full_ast,
+ handle: il,
+ };
+ let tokens = HighLevelILTokenEmitter::from_raw(NonNull::new(tokens).unwrap());
+ let settings = DisassemblySettings { handle: settings };
+ (*ctxt).expr_text(
+ &il,
+ expr_index.into(),
+ &tokens,
+ &settings,
+ as_full_ast,
+ precedence,
+ statement,
+ );
+}
+
+unsafe extern "C" fn cb_begin_lines<C: LanguageRepresentationFunction>(
+ ctxt: *mut c_void,
+ il: *mut BNHighLevelILFunction,
+ expr_index: usize,
+ tokens: *mut BNHighLevelILTokenEmitter,
+) {
+ let ctxt = ctxt as *mut C;
+ let il = HighLevelILFunction {
+ full_ast: false,
+ handle: il,
+ };
+ let tokens = HighLevelILTokenEmitter::from_raw(NonNull::new(tokens).unwrap());
+ (*ctxt).begin_lines(&il, expr_index.into(), &tokens)
+}
+
+unsafe extern "C" fn cb_end_lines<C: LanguageRepresentationFunction>(
+ ctxt: *mut c_void,
+ il: *mut BNHighLevelILFunction,
+ expr_index: usize,
+ tokens: *mut BNHighLevelILTokenEmitter,
+) {
+ let ctxt = ctxt as *mut C;
+ let il = HighLevelILFunction {
+ full_ast: false,
+ handle: il,
+ };
+ let tokens = HighLevelILTokenEmitter::from_raw(NonNull::new(tokens).unwrap());
+ (*ctxt).end_lines(&il, expr_index.into(), &tokens)
+}
+
+unsafe extern "C" fn cb_get_comment_start_string<C: LanguageRepresentationFunction>(
+ ctxt: *mut c_void,
+) -> *mut c_char {
+ let ctxt = ctxt as *mut C;
+ let result = (*ctxt).comment_start_string();
+ BnString::into_raw(BnString::new(result))
+}
+
+unsafe extern "C" fn cb_get_comment_end_string<C: LanguageRepresentationFunction>(
+ ctxt: *mut c_void,
+) -> *mut c_char {
+ let ctxt = ctxt as *mut C;
+ let result = (*ctxt).comment_end_string();
+ BnString::into_raw(BnString::new(result))
+}
+
+unsafe extern "C" fn cb_get_annotation_start_string<C: LanguageRepresentationFunction>(
+ ctxt: *mut c_void,
+) -> *mut c_char {
+ let ctxt = ctxt as *mut C;
+ let result = (*ctxt).annotation_start_string();
+ BnString::into_raw(BnString::new(result))
+}
+
+unsafe extern "C" fn cb_get_annotation_end_string<C: LanguageRepresentationFunction>(
+ ctxt: *mut c_void,
+) -> *mut c_char {
+ let ctxt = ctxt as *mut C;
+ let result = (*ctxt).annotation_end_string();
+ BnString::into_raw(BnString::new(result))
+}
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index 7056e529..1b02da0d 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -57,6 +57,8 @@ pub mod function_recognizer;
pub mod headless;
pub mod high_level_il;
pub mod interaction;
+pub mod language_representation;
+pub mod line_formatter;
pub mod linear_view;
pub mod logger;
pub mod low_level_il;
diff --git a/rust/src/line_formatter.rs b/rust/src/line_formatter.rs
new file mode 100644
index 00000000..5dc07c5c
--- /dev/null
+++ b/rust/src/line_formatter.rs
@@ -0,0 +1,172 @@
+use std::ffi::{c_char, c_void};
+use std::ptr::NonNull;
+
+use binaryninjacore_sys::*;
+
+use crate::disassembly::DisassemblyTextLine;
+use crate::high_level_il::HighLevelILFunction;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
+use crate::string::{raw_to_string, BnStrCompatible, BnString};
+
+/// Register a [`LineFormatter`] with the API.
+pub fn register_line_formatter<C: LineFormatter, B: BnStrCompatible>(
+ name: B,
+ formatter: C,
+) -> CoreLineFormatter {
+ let custom = Box::leak(Box::new(formatter));
+ let mut callbacks = BNCustomLineFormatter {
+ context: custom as *mut C as *mut c_void,
+ formatLines: Some(cb_format_lines::<C>),
+ freeLines: Some(cb_free_lines),
+ };
+ let name = name.into_bytes_with_nul();
+ let handle =
+ unsafe { BNRegisterLineFormatter(name.as_ref().as_ptr() as *const c_char, &mut callbacks) };
+ CoreLineFormatter::from_raw(NonNull::new(handle).unwrap())
+}
+
+pub trait LineFormatter: Sized {
+ fn format_lines(
+ &self,
+ lines: &[DisassemblyTextLine],
+ settings: &LineFormatterSettings,
+ ) -> Vec<DisassemblyTextLine>;
+}
+
+#[repr(transparent)]
+pub struct CoreLineFormatter {
+ pub(crate) handle: NonNull<BNLineFormatter>,
+}
+
+impl CoreLineFormatter {
+ pub fn from_raw(handle: NonNull<BNLineFormatter>) -> Self {
+ Self { handle }
+ }
+
+ /// Get the default [`CoreLineFormatter`] if available, because the user might have disabled it.
+ pub fn default_if_available() -> Option<Self> {
+ Some(unsafe { Self::from_raw(NonNull::new(BNGetDefaultLineFormatter())?) })
+ }
+
+ pub fn all() -> Array<CoreLineFormatter> {
+ let mut count = 0;
+ let result = unsafe { BNGetLineFormatterList(&mut count) };
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ pub fn from_name<S: BnStrCompatible>(name: S) -> Option<CoreLineFormatter> {
+ let name_raw = name.into_bytes_with_nul();
+ let result =
+ unsafe { BNGetLineFormatterByName(name_raw.as_ref().as_ptr() as *const c_char) };
+ NonNull::new(result).map(Self::from_raw)
+ }
+
+ pub fn name(&self) -> BnString {
+ unsafe { BnString::from_raw(BNGetLineFormatterName(self.handle.as_ptr())) }
+ }
+}
+
+impl CoreArrayProvider for CoreLineFormatter {
+ type Raw = *mut BNLineFormatter;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for CoreLineFormatter {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeLineFormatterList(raw)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ // TODO: Because handle is a NonNull we should prob make Self::Raw that as well...
+ let handle = NonNull::new(*raw).unwrap();
+ CoreLineFormatter::from_raw(handle)
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct LineFormatterSettings {
+ pub high_level_il: Option<Ref<HighLevelILFunction>>,
+ pub desired_line_len: usize,
+ pub min_content_len: usize,
+ pub tab_width: usize,
+ pub lang_name: String,
+ pub comment_start: String,
+ pub comment_end: String,
+ pub annotation_start: String,
+ pub annotation_end: String,
+}
+
+impl LineFormatterSettings {
+ pub(crate) fn from_raw(value: &BNLineFormatterSettings) -> Self {
+ Self {
+ high_level_il: match value.highLevelIL.is_null() {
+ false => Some(
+ unsafe { HighLevelILFunction::from_raw(value.highLevelIL, false) }.to_owned(),
+ ),
+ true => None,
+ },
+ desired_line_len: value.desiredLineLength,
+ min_content_len: value.minimumContentLength,
+ tab_width: value.tabWidth,
+ lang_name: raw_to_string(value.languageName as *mut _).unwrap(),
+ comment_start: raw_to_string(value.commentStartString as *mut _).unwrap(),
+ comment_end: raw_to_string(value.commentEndString as *mut _).unwrap(),
+ annotation_start: raw_to_string(value.annotationStartString as *mut _).unwrap(),
+ annotation_end: raw_to_string(value.annotationEndString as *mut _).unwrap(),
+ }
+ }
+
+ #[allow(unused)]
+ pub(crate) fn from_owned_raw(value: BNLineFormatterSettings) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ #[allow(unused)]
+ pub(crate) fn free_raw(value: BNLineFormatterSettings) {
+ let _ = unsafe { HighLevelILFunction::ref_from_raw(value.highLevelIL, false) };
+ let _ = unsafe { BnString::from_raw(value.languageName as *mut _) };
+ let _ = unsafe { BnString::from_raw(value.commentStartString as *mut _) };
+ let _ = unsafe { BnString::from_raw(value.commentEndString as *mut _) };
+ let _ = unsafe { BnString::from_raw(value.annotationStartString as *mut _) };
+ let _ = unsafe { BnString::from_raw(value.annotationEndString as *mut _) };
+ }
+}
+
+unsafe extern "C" fn cb_format_lines<C: LineFormatter>(
+ ctxt: *mut c_void,
+ in_lines: *mut BNDisassemblyTextLine,
+ in_count: usize,
+ raw_settings: *const BNLineFormatterSettings,
+ out_count: *mut usize,
+) -> *mut BNDisassemblyTextLine {
+ // NOTE dropped by line_formatter_free_lines_ffi
+ let ctxt = ctxt as *mut C;
+ let lines_slice = core::slice::from_raw_parts(in_lines, in_count);
+ let lines: Vec<_> = lines_slice
+ .iter()
+ .map(DisassemblyTextLine::from_raw)
+ .collect();
+ let settings = LineFormatterSettings::from_raw(&*raw_settings);
+ let result = (*ctxt).format_lines(&lines, &settings);
+ *out_count = result.len();
+ let result: Box<[BNDisassemblyTextLine]> = result
+ .into_iter()
+ .map(DisassemblyTextLine::into_raw)
+ .collect();
+ Box::leak(result).as_mut_ptr()
+}
+
+unsafe extern "C" fn cb_free_lines(
+ _ctxt: *mut c_void,
+ raw_lines: *mut BNDisassemblyTextLine,
+ count: usize,
+) {
+ let lines: Box<[BNDisassemblyTextLine]> =
+ Box::from_raw(core::slice::from_raw_parts_mut(raw_lines, count));
+ for line in lines {
+ DisassemblyTextLine::free_raw(line);
+ }
+}
diff --git a/rust/src/render_layer.rs b/rust/src/render_layer.rs
index 08553552..181294d4 100644
--- a/rust/src/render_layer.rs
+++ b/rust/src/render_layer.rs
@@ -293,13 +293,13 @@ impl CoreRenderLayer {
Self { handle }
}
- pub fn render_layers() -> Array<CoreRenderLayer> {
+ pub fn all() -> Array<CoreRenderLayer> {
let mut count = 0;
let result = unsafe { BNGetRenderLayerList(&mut count) };
unsafe { Array::new(result, count, ()) }
}
- pub fn render_layer_by_name(name: &str) -> Option<CoreRenderLayer> {
+ pub fn from_name(name: &str) -> Option<CoreRenderLayer> {
let name_raw = name.to_cstr();
let result = unsafe { BNGetRenderLayerByName(name_raw.as_ptr()) };
NonNull::new(result).map(Self::from_raw)
diff --git a/rust/src/type_parser.rs b/rust/src/type_parser.rs
index 0ce46a50..13985c44 100644
--- a/rust/src/type_parser.rs
+++ b/rust/src/type_parser.rs
@@ -37,7 +37,7 @@ pub fn register_type_parser<T: TypeParser>(
#[repr(transparent)]
pub struct CoreTypeParser {
- handle: NonNull<BNTypeParser>,
+ pub(crate) handle: NonNull<BNTypeParser>,
}
impl CoreTypeParser {
diff --git a/rust/src/type_printer.rs b/rust/src/type_printer.rs
index c9bec609..a7495f1c 100644
--- a/rust/src/type_printer.rs
+++ b/rust/src/type_printer.rs
@@ -42,7 +42,7 @@ pub fn register_type_printer<T: TypePrinter>(
#[repr(transparent)]
pub struct CoreTypePrinter {
- handle: NonNull<BNTypePrinter>,
+ pub(crate) handle: NonNull<BNTypePrinter>,
}
impl CoreTypePrinter {