binaryninja/flowgraph/
node.rs

1//! Represents a single node in a flow graph, typically backed by a [`BasicBlock`].
2
3use crate::architecture::BranchType;
4use crate::basic_block::{BasicBlock, BlockContext};
5use crate::disassembly::DisassemblyTextLine;
6use crate::flowgraph::edge::{EdgeStyle, FlowGraphEdge, Point};
7use crate::flowgraph::FlowGraph;
8use crate::function::HighlightColor;
9use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable};
10use binaryninjacore_sys::*;
11use std::fmt::{Debug, Formatter};
12use std::hash::Hash;
13
14// Used for documentation purposes.
15#[allow(unused)]
16use crate::flowgraph::layout::FlowGraphLayout;
17
18/// The node of a flow graph containing lines of text tokens, and edges flowing into and out of it.
19///
20/// A node can also be backed by a [`BasicBlock`], which makes the node function as a basic block node.
21///
22/// A node is positioned absolutely within the flow graph and is typically positioned by a [`FlowGraphLayout`].
23#[repr(transparent)]
24#[derive(PartialEq, Eq, Hash)]
25pub struct FlowGraphNode {
26    pub(crate) handle: *mut BNFlowGraphNode,
27}
28
29impl FlowGraphNode {
30    pub(crate) unsafe fn from_raw(raw: *mut BNFlowGraphNode) -> Self {
31        Self { handle: raw }
32    }
33
34    pub(crate) unsafe fn ref_from_raw(raw: *mut BNFlowGraphNode) -> Ref<Self> {
35        Ref::new(Self { handle: raw })
36    }
37
38    pub fn new(graph: &FlowGraph) -> Ref<Self> {
39        unsafe { FlowGraphNode::ref_from_raw(BNCreateFlowGraphNode(graph.handle)) }
40    }
41
42    pub fn basic_block<C: BlockContext>(&self, context: C) -> Option<Ref<BasicBlock<C>>> {
43        let block_ptr = unsafe { BNGetFlowGraphBasicBlock(self.handle) };
44        if block_ptr.is_null() {
45            return None;
46        }
47        Some(unsafe { BasicBlock::ref_from_raw(block_ptr, context) })
48    }
49
50    pub fn set_basic_block<C: BlockContext>(&self, block: Option<&BasicBlock<C>>) {
51        match block {
52            Some(block) => unsafe { BNSetFlowGraphBasicBlock(self.handle, block.handle) },
53            None => unsafe { BNSetFlowGraphBasicBlock(self.handle, std::ptr::null_mut()) },
54        }
55    }
56
57    pub fn lines(&self) -> Array<DisassemblyTextLine> {
58        let mut count = 0;
59        let result = unsafe { BNGetFlowGraphNodeLines(self.handle, &mut count) };
60        assert!(!result.is_null());
61        unsafe { Array::new(result, count, ()) }
62    }
63
64    pub fn set_lines(&self, lines: impl IntoIterator<Item = DisassemblyTextLine>) {
65        // NOTE: This will create allocations and increment tag refs, we must call DisassemblyTextLine::free_raw
66        let mut raw_lines: Vec<BNDisassemblyTextLine> = lines
67            .into_iter()
68            .map(DisassemblyTextLine::into_raw)
69            .collect();
70        unsafe {
71            BNSetFlowGraphNodeLines(self.handle, raw_lines.as_mut_ptr(), raw_lines.len());
72            for raw_line in raw_lines {
73                DisassemblyTextLine::free_raw(raw_line);
74            }
75        }
76    }
77
78    /// Returns the graph position of the node in X, Y form.
79    pub fn position(&self) -> (i32, i32) {
80        let pos_x = unsafe { BNGetFlowGraphNodeX(self.handle) };
81        let pos_y = unsafe { BNGetFlowGraphNodeY(self.handle) };
82        (pos_x, pos_y)
83    }
84
85    /// Returns the size of the node in width, height form.
86    pub fn size(&self) -> (i32, i32) {
87        let w = unsafe { BNGetFlowGraphNodeWidth(self.handle) };
88        let h = unsafe { BNGetFlowGraphNodeHeight(self.handle) };
89        (w, h)
90    }
91
92    /// Sets the graph position of the node.
93    pub fn set_position(&self, x: i32, y: i32) {
94        unsafe { BNFlowGraphNodeSetX(self.handle, x) };
95        unsafe { BNFlowGraphNodeSetY(self.handle, y) };
96    }
97
98    pub fn highlight_color(&self) -> HighlightColor {
99        let raw = unsafe { BNGetFlowGraphNodeHighlight(self.handle) };
100        HighlightColor::from(raw)
101    }
102
103    pub fn set_highlight_color(&self, highlight: HighlightColor) {
104        unsafe { BNSetFlowGraphNodeHighlight(self.handle, highlight.into()) };
105    }
106
107    /// Edges flowing _into_ this node.
108    pub fn incoming_edges(&self) -> Array<FlowGraphEdge> {
109        let mut count = 0;
110        let result = unsafe { BNGetFlowGraphNodeIncomingEdges(self.handle, &mut count) };
111        assert!(!result.is_null());
112        unsafe { Array::new(result, count, ()) }
113    }
114
115    /// Edges flowing _out of_ this node.
116    pub fn outgoing_edges(&self) -> Array<FlowGraphEdge> {
117        let mut count = 0;
118        let result = unsafe { BNGetFlowGraphNodeOutgoingEdges(self.handle, &mut count) };
119        assert!(!result.is_null());
120        unsafe { Array::new(result, count, ()) }
121    }
122
123    /// Connects two flow graph nodes with an edge.
124    pub fn add_outgoing_edge(
125        &self,
126        type_: BranchType,
127        target: &FlowGraphNode,
128        edge_style: EdgeStyle,
129    ) {
130        unsafe {
131            BNAddFlowGraphNodeOutgoingEdge(self.handle, type_, target.handle, edge_style.into())
132        }
133    }
134
135    /// Sets the outgoing edge points for `edge_idx`.
136    ///
137    /// This is typically called when laying out a [`FlowGraph`] using [`FlowGraphLayout`], without
138    /// calling this for all given node edges in a graph; the nodes will be rendered without any
139    /// edges between them, as edge routing is _not_ automatic.
140    pub fn set_outgoing_edge_points(&self, edge_idx: usize, points: &[Point]) {
141        let mut points_raw: Vec<BNPoint> = points.iter().map(|p| (*p).into()).collect();
142        unsafe {
143            BNFlowGraphNodeSetOutgoingEdgePoints(
144                self.handle,
145                edge_idx,
146                points_raw.as_mut_ptr(),
147                points.len(),
148            )
149        };
150    }
151
152    // TODO: Setting visibility region should be automatic, and not a requirement for the layout implementation.
153    /// Sets the graph-coordinate bounding rectangle used for visibility/layout calculations.
154    ///
155    /// This is what is used to determine the nodes returned by [`FlowGraph::visible_nodes`].
156    ///
157    /// Custom [`FlowGraphLayout`] implementations should call this after positioning the node and
158    /// routing its outgoing edges, before setting the final graph size. The region should cover the
159    /// node itself and any routed edge points owned by this node, so the renderer can determine what
160    /// part of the graph needs to be visible/redrawn for the node.
161    pub fn set_visibility_region(&self, x: i32, y: i32, w: i32, h: i32) {
162        unsafe { BNFlowGraphNodeSetVisibilityRegion(self.handle, x, y, w, h) };
163    }
164}
165
166impl Debug for FlowGraphNode {
167    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
168        f.debug_struct("FlowGraphNode")
169            .field("lines", &self.lines().to_vec())
170            .finish()
171    }
172}
173
174unsafe impl RefCountable for FlowGraphNode {
175    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
176        Ref::new(Self {
177            handle: BNNewFlowGraphNodeReference(handle.handle),
178        })
179    }
180
181    unsafe fn dec_ref(handle: &Self) {
182        BNFreeFlowGraphNode(handle.handle);
183    }
184}
185
186impl ToOwned for FlowGraphNode {
187    type Owned = Ref<Self>;
188
189    fn to_owned(&self) -> Self::Owned {
190        unsafe { RefCountable::inc_ref(self) }
191    }
192}
193
194impl CoreArrayProvider for FlowGraphNode {
195    type Raw = *mut BNFlowGraphNode;
196    type Context = ();
197    type Wrapped<'a> = FlowGraphNode;
198}
199
200unsafe impl CoreArrayProviderInner for FlowGraphNode {
201    unsafe fn free(raw: *mut Self::Raw, count: usize, _: &Self::Context) {
202        BNFreeFlowGraphNodeList(raw, count);
203    }
204
205    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
206        Self::from_raw(*raw)
207    }
208}