summaryrefslogtreecommitdiff
path: root/rust/src
diff options
context:
space:
mode:
authorMichael Krasnitski <michael.krasnitski@gmail.com>2024-02-08 19:50:49 -0500
committerKyle Martin <krm504@nyu.edu>2024-03-18 17:46:37 -0400
commit970fe84875e2d6eed918f5bdc9ff689ef37b05ce (patch)
tree31fe9d82a623693c92c5c1fc7d05daf69fad3da3 /rust/src
parentf535df40f978d221547b45650b75aa1089e197ce (diff)
Make `OperandIter` generic over its function IL
Diffstat (limited to 'rust/src')
-rw-r--r--rust/src/function.rs8
-rw-r--r--rust/src/lib.rs1
-rw-r--r--rust/src/mlil/function.rs20
-rw-r--r--rust/src/mlil/instruction.rs123
-rw-r--r--rust/src/mlil/operation.rs227
-rw-r--r--rust/src/operand_iter.rs181
6 files changed, 301 insertions, 259 deletions
diff --git a/rust/src/function.rs b/rust/src/function.rs
index 273a0861..6547762f 100644
--- a/rust/src/function.rs
+++ b/rust/src/function.rs
@@ -435,6 +435,14 @@ impl PartialEq for Function {
}
}
+pub trait ILFunction {
+ type BNInstruction;
+ type Instruction;
+
+ fn il_instruction_from_idx(&self, expr_idx: usize) -> Self::Instruction;
+ fn operands_from_idx(&self, expr_idx: usize) -> [u64; 5];
+}
+
/////////////////
// AddressRange
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index 8424cb59..11021bd7 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -124,6 +124,7 @@ extern crate rayon;
#[macro_use]
mod ffi;
+mod operand_iter;
pub mod architecture;
pub mod backgroundtask;
diff --git a/rust/src/mlil/function.rs b/rust/src/mlil/function.rs
index 16cc5102..3f96d8bb 100644
--- a/rust/src/mlil/function.rs
+++ b/rust/src/mlil/function.rs
@@ -2,16 +2,19 @@ use core::hash::{Hash, Hasher};
use binaryninjacore_sys::BNFreeMediumLevelILFunction;
use binaryninjacore_sys::BNGetMediumLevelILBasicBlockList;
+use binaryninjacore_sys::BNGetMediumLevelILByIndex;
use binaryninjacore_sys::BNGetMediumLevelILInstructionCount;
use binaryninjacore_sys::BNGetMediumLevelILOwnerFunction;
use binaryninjacore_sys::BNGetMediumLevelILSSAForm;
use binaryninjacore_sys::BNMediumLevelILFunction;
use binaryninjacore_sys::BNMediumLevelILGetInstructionStart;
+use binaryninjacore_sys::BNMediumLevelILInstruction;
+use binaryninjacore_sys::BNMediumLevelILOperation;
use binaryninjacore_sys::BNNewMediumLevelILFunctionReference;
use crate::basicblock::BasicBlock;
-use crate::function::Function;
use crate::function::Location;
+use crate::function::{Function, ILFunction};
use crate::rc::{Array, Ref, RefCountable};
use super::{MediumLevelILBlock, MediumLevelILInstruction, MediumLevelILLiftedInstruction};
@@ -93,6 +96,21 @@ impl MediumLevelILFunction {
}
}
+impl ILFunction for MediumLevelILFunction {
+ type BNInstruction = BNMediumLevelILInstruction;
+ type Instruction = MediumLevelILInstruction;
+
+ fn il_instruction_from_idx(&self, expr_idx: usize) -> Self::Instruction {
+ self.instruction_from_idx(expr_idx)
+ }
+
+ fn operands_from_idx(&self, expr_idx: usize) -> [u64; 5] {
+ let node = unsafe { BNGetMediumLevelILByIndex(self.handle, expr_idx) };
+ assert_eq!(node.operation, BNMediumLevelILOperation::MLIL_UNDEF);
+ node.operands
+ }
+}
+
impl ToOwned for MediumLevelILFunction {
type Owned = Ref<Self>;
diff --git a/rust/src/mlil/instruction.rs b/rust/src/mlil/instruction.rs
index 7b182010..a25a0859 100644
--- a/rust/src/mlil/instruction.rs
+++ b/rust/src/mlil/instruction.rs
@@ -1,8 +1,13 @@
+use binaryninjacore_sys::BNFromVariableIdentifier;
use binaryninjacore_sys::BNGetMediumLevelILByIndex;
+use binaryninjacore_sys::BNMediumLevelILInstruction;
use binaryninjacore_sys::BNMediumLevelILOperation;
+use crate::operand_iter::OperandIter;
use crate::rc::Ref;
-use crate::types::{ConstantData, ILIntrinsic, RegisterValue, RegisterValueType};
+use crate::types::{
+ ConstantData, ILIntrinsic, RegisterValue, RegisterValueType, SSAVariable, Variable,
+};
use super::lift::*;
use super::operation::*;
@@ -773,8 +778,8 @@ impl MediumLevelILInstruction {
}),
JumpTo(op) => Lifted::JumpTo(LiftedJumpTo {
dest: self.lift_operand(op.dest),
- targets: OperandIter::new(&self.function, op.first_operand, op.num_operands)
- .as_pairs()
+ targets: OperandIter::new(&*self.function, op.first_operand, op.num_operands)
+ .pairs()
.collect(),
}),
Goto(op) => Lifted::Goto(op),
@@ -812,13 +817,13 @@ impl MediumLevelILInstruction {
}),
VarPhi(op) => Lifted::VarPhi(LiftedVarPhi {
dest: op.dest,
- src: OperandIter::new(&self.function, op.first_operand, op.num_operands)
- .as_ssa_vars()
+ src: OperandIter::new(&*self.function, op.first_operand, op.num_operands)
+ .ssa_vars()
.collect(),
}),
MemPhi(op) => Lifted::MemPhi(LiftedMemPhi {
dest_memory: op.dest_memory,
- src_memory: OperandIter::new(&self.function, op.first_operand, op.num_operands)
+ src_memory: OperandIter::new(&*self.function, op.first_operand, op.num_operands)
.collect(),
}),
VarSplit(op) => Lifted::VarSplit(op),
@@ -889,31 +894,31 @@ impl MediumLevelILInstruction {
Tailcall(op) => Lifted::Tailcall(self.lift_call(op)),
Intrinsic(op) => Lifted::Intrinsic(LiftedIntrinsic {
- output: OperandIter::new(&self.function, op.first_output, op.num_outputs)
- .as_vars()
+ output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
+ .vars()
.collect(),
intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic),
- params: OperandIter::new(&self.function, op.first_param, op.num_params)
- .as_exprs()
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
.map(|expr| expr.lift())
.collect(),
}),
Syscall(op) => Lifted::Syscall(LiftedSyscallCall {
- output: OperandIter::new(&self.function, op.first_output, op.num_outputs)
- .as_vars()
+ output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
+ .vars()
.collect(),
- params: OperandIter::new(&self.function, op.first_param, op.num_params)
- .as_exprs()
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
.map(|expr| expr.lift())
.collect(),
}),
IntrinsicSsa(op) => Lifted::IntrinsicSsa(LiftedIntrinsicSsa {
- output: OperandIter::new(&self.function, op.first_output, op.num_outputs)
- .as_ssa_vars()
+ output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
+ .ssa_vars()
.collect(),
intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic),
- params: OperandIter::new(&self.function, op.first_param, op.num_params)
- .as_exprs()
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
.map(|expr| expr.lift())
.collect(),
}),
@@ -926,8 +931,8 @@ impl MediumLevelILInstruction {
SyscallSsa(op) => Lifted::SyscallSsa(LiftedSyscallSsa {
output: get_call_output_ssa(&self.function, op.output).collect(),
- params: OperandIter::new(&self.function, op.first_param, op.num_params)
- .as_exprs()
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
.map(|expr| expr.lift())
.collect(),
src_memory: op.src_memory,
@@ -983,20 +988,20 @@ impl MediumLevelILInstruction {
src_memory: op.src_memory,
}),
Ret(op) => Lifted::Ret(LiftedRet {
- src: OperandIter::new(&self.function, op.first_operand, op.num_operands)
- .as_exprs()
+ src: OperandIter::new(&*self.function, op.first_operand, op.num_operands)
+ .exprs()
.map(|expr| expr.lift())
.collect(),
}),
SeparateParamList(op) => Lifted::SeparateParamList(LiftedSeparateParamList {
- params: OperandIter::new(&self.function, op.first_param, op.num_params)
- .as_exprs()
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
.map(|expr| expr.lift())
.collect(),
}),
SharedParamSlot(op) => Lifted::SharedParamSlot(LiftedSharedParamSlot {
- params: OperandIter::new(&self.function, op.first_param, op.num_params)
- .as_exprs()
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
.map(|expr| expr.lift())
.collect(),
}),
@@ -1045,12 +1050,12 @@ impl MediumLevelILInstruction {
fn lift_call(&self, op: Call) -> LiftedCall {
LiftedCall {
- output: OperandIter::new(&self.function, op.first_output, op.num_outputs)
- .as_vars()
+ output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
+ .vars()
.collect(),
dest: self.lift_operand(op.dest),
- params: OperandIter::new(&self.function, op.first_param, op.num_params)
- .as_exprs()
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
.map(|expr| expr.lift())
.collect(),
}
@@ -1071,8 +1076,8 @@ impl MediumLevelILInstruction {
LiftedCallSsa {
output: get_call_output_ssa(&self.function, op.output).collect(),
dest: self.lift_operand(op.dest),
- params: OperandIter::new(&self.function, op.first_param, op.num_params)
- .as_exprs()
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
.map(|expr| expr.lift())
.collect(),
src_memory: op.src_memory,
@@ -1090,3 +1095,57 @@ impl MediumLevelILInstruction {
}
}
}
+
+fn get_float(value: u64, size: usize) -> f64 {
+ match size {
+ 4 => f32::from_bits(value as u32) as f64,
+ 8 => f64::from_bits(value),
+ // TODO how to handle this value?
+ size => todo!("float size {}", size),
+ }
+}
+
+fn get_raw_operation(function: &MediumLevelILFunction, idx: usize) -> BNMediumLevelILInstruction {
+ unsafe { BNGetMediumLevelILByIndex(function.handle, idx) }
+}
+
+fn get_var(id: u64) -> Variable {
+ unsafe { Variable::from_raw(BNFromVariableIdentifier(id)) }
+}
+
+fn get_var_ssa(id: u64, version: usize) -> SSAVariable {
+ SSAVariable::new(get_var(id), version)
+}
+
+fn get_call_output(function: &MediumLevelILFunction, idx: usize) -> impl Iterator<Item = Variable> {
+ let op = get_raw_operation(function, idx);
+ assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_OUTPUT);
+ OperandIter::new(function, op.operands[1] as usize, op.operands[0] as usize).vars()
+}
+
+fn get_call_params(
+ function: &MediumLevelILFunction,
+ idx: usize,
+) -> impl Iterator<Item = MediumLevelILInstruction> {
+ let op = get_raw_operation(function, idx);
+ assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_PARAM);
+ OperandIter::new(function, op.operands[1] as usize, op.operands[0] as usize).exprs()
+}
+
+fn get_call_output_ssa(
+ function: &MediumLevelILFunction,
+ idx: usize,
+) -> impl Iterator<Item = SSAVariable> {
+ let op = get_raw_operation(function, idx);
+ assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_OUTPUT_SSA);
+ OperandIter::new(function, op.operands[2] as usize, op.operands[1] as usize).ssa_vars()
+}
+
+fn get_call_params_ssa(
+ function: &MediumLevelILFunction,
+ idx: usize,
+) -> impl Iterator<Item = MediumLevelILInstruction> {
+ let op = get_raw_operation(function, idx);
+ assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_PARAM_SSA);
+ OperandIter::new(function, op.operands[2] as usize, op.operands[1] as usize).exprs()
+}
diff --git a/rust/src/mlil/operation.rs b/rust/src/mlil/operation.rs
index 319ba34b..eb0c01ea 100644
--- a/rust/src/mlil/operation.rs
+++ b/rust/src/mlil/operation.rs
@@ -1,235 +1,10 @@
use std::collections::HashMap;
-use binaryninjacore_sys::BNFromVariableIdentifier;
-use binaryninjacore_sys::BNGetMediumLevelILByIndex;
-use binaryninjacore_sys::BNMediumLevelILInstruction;
-use binaryninjacore_sys::BNMediumLevelILOperation;
-
-use crate::rc::Ref;
use crate::types;
use crate::types::ILIntrinsic;
-use crate::types::RegisterValue;
-use crate::types::RegisterValueType;
use crate::types::{SSAVariable, Variable};
-use super::{MediumLevelILFunction, MediumLevelILInstruction, MediumLevelILLiftedInstruction};
-
-pub struct OperandIter {
- function: Ref<MediumLevelILFunction>,
- remaining: usize,
- next_iter_idx: Option<usize>,
- current_iter: OperandIterInner,
-}
-
-impl OperandIter {
- pub(crate) fn new(function: &MediumLevelILFunction, idx: usize, number: usize) -> Self {
- Self {
- function: function.to_owned(),
- remaining: number,
- next_iter_idx: Some(idx),
- current_iter: OperandIterInner::empty(),
- }
- }
-
- pub fn as_pairs(self) -> OperandPairIter {
- assert_eq!(self.len() % 2, 0);
- OperandPairIter(self)
- }
-
- pub fn as_exprs(self) -> OperandExprIter {
- OperandExprIter(self)
- }
-
- pub fn as_vars(self) -> OperandVarIter {
- OperandVarIter(self)
- }
-
- pub fn as_ssa_vars(self) -> OperandSSAVarIter {
- OperandSSAVarIter(self.as_pairs())
- }
-}
-
-impl Iterator for OperandIter {
- type Item = u64;
- fn next(&mut self) -> Option<Self::Item> {
- if let Some(item) = self.current_iter.next() {
- self.remaining -= 1;
- Some(item)
- } else {
- // Will short-circuit and return `None` once iter is exhausted
- let iter_idx = self.next_iter_idx?;
- let node = get_raw_operation(&self.function, iter_idx);
- assert_eq!(node.operation, BNMediumLevelILOperation::MLIL_UNDEF);
-
- let next = if self.remaining > 4 {
- self.next_iter_idx = Some(node.operands[4] as usize);
- &node.operands[..4]
- } else {
- self.next_iter_idx = None;
- &node.operands[..self.remaining]
- };
-
- self.current_iter = OperandIterInner::from_slice(next);
- self.next()
- }
- }
-}
-impl ExactSizeIterator for OperandIter {
- fn len(&self) -> usize {
- self.remaining + self.current_iter.len()
- }
-}
-
-struct OperandIterInner {
- arr: [u64; 4],
- idx: usize,
-}
-
-impl OperandIterInner {
- fn from_slice(slice: &[u64]) -> Self {
- assert!(slice.len() <= 4);
- let idx = 4 - slice.len();
- let mut arr = [0; 4];
- arr[idx..].copy_from_slice(slice);
- Self { arr, idx }
- }
-
- fn empty() -> Self {
- Self {
- arr: [0; 4],
- idx: 4,
- }
- }
-}
-
-impl Iterator for OperandIterInner {
- type Item = u64;
-
- fn next(&mut self) -> Option<Self::Item> {
- if self.idx < 4 {
- let val = self.arr[self.idx];
- self.idx += 1;
- Some(val)
- } else {
- None
- }
- }
-}
-impl ExactSizeIterator for OperandIterInner {
- fn len(&self) -> usize {
- 4 - self.idx
- }
-}
-
-pub struct OperandPairIter(OperandIter);
-impl Iterator for OperandPairIter {
- type Item = (u64, u64);
-
- fn next(&mut self) -> Option<Self::Item> {
- let first = self.0.next()?;
- let second = self.0.next()?;
- Some((first, second))
- }
-}
-impl ExactSizeIterator for OperandPairIter {
- fn len(&self) -> usize {
- self.0.len() / 2
- }
-}
-
-pub struct OperandExprIter(OperandIter);
-impl Iterator for OperandExprIter {
- type Item = MediumLevelILInstruction;
-
- fn next(&mut self) -> Option<Self::Item> {
- self.0
- .next()
- .map(|idx| self.0.function.instruction_from_idx(idx as usize))
- }
-}
-impl ExactSizeIterator for OperandExprIter {
- fn len(&self) -> usize {
- self.0.len()
- }
-}
-
-pub struct OperandVarIter(OperandIter);
-impl Iterator for OperandVarIter {
- type Item = Variable;
-
- fn next(&mut self) -> Option<Self::Item> {
- self.0.next().map(get_var)
- }
-}
-impl ExactSizeIterator for OperandVarIter {
- fn len(&self) -> usize {
- self.0.len()
- }
-}
-
-pub struct OperandSSAVarIter(OperandPairIter);
-impl Iterator for OperandSSAVarIter {
- type Item = SSAVariable;
-
- fn next(&mut self) -> Option<Self::Item> {
- self.0
- .next()
- .map(|(id, version)| get_var_ssa(id, version as usize))
- }
-}
-impl ExactSizeIterator for OperandSSAVarIter {
- fn len(&self) -> usize {
- self.0.len()
- }
-}
-
-pub(super) fn get_float(value: u64, size: usize) -> f64 {
- match size {
- 4 => f32::from_bits(value as u32) as f64,
- 8 => f64::from_bits(value),
- // TODO how to handle this value?
- size => todo!("float size {}", size),
- }
-}
-
-fn get_raw_operation(function: &MediumLevelILFunction, idx: usize) -> BNMediumLevelILInstruction {
- unsafe { BNGetMediumLevelILByIndex(function.handle, idx) }
-}
-
-pub(super) fn get_var(id: u64) -> Variable {
- unsafe { Variable::from_raw(BNFromVariableIdentifier(id)) }
-}
-
-pub(super) fn get_var_ssa(id: u64, version: usize) -> SSAVariable {
- SSAVariable::new(get_var(id), version)
-}
-
-pub(super) fn get_call_output(function: &MediumLevelILFunction, idx: usize) -> OperandVarIter {
- let op = get_raw_operation(function, idx);
- assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_OUTPUT);
- OperandIter::new(function, op.operands[1] as usize, op.operands[0] as usize).as_vars()
-}
-
-pub(super) fn get_call_params(function: &MediumLevelILFunction, idx: usize) -> OperandExprIter {
- let op = get_raw_operation(function, idx);
- assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_PARAM);
- OperandIter::new(function, op.operands[1] as usize, op.operands[0] as usize).as_exprs()
-}
-
-pub(super) fn get_call_output_ssa(
- function: &MediumLevelILFunction,
- idx: usize,
-) -> OperandSSAVarIter {
- let op = get_raw_operation(function, idx);
- assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_OUTPUT_SSA);
- OperandIter::new(function, op.operands[2] as usize, op.operands[1] as usize).as_ssa_vars()
-}
-
-pub(super) fn get_call_params_ssa(function: &MediumLevelILFunction, idx: usize) -> OperandExprIter {
- let op = get_raw_operation(function, idx);
- assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_PARAM_SSA);
- OperandIter::new(function, op.operands[2] as usize, op.operands[1] as usize).as_exprs()
-}
+use super::MediumLevelILLiftedInstruction;
// IF
#[derive(Copy, Clone)]
diff --git a/rust/src/operand_iter.rs b/rust/src/operand_iter.rs
new file mode 100644
index 00000000..a9b3c753
--- /dev/null
+++ b/rust/src/operand_iter.rs
@@ -0,0 +1,181 @@
+use binaryninjacore_sys::BNFromVariableIdentifier;
+
+use crate::function::ILFunction;
+use crate::rc::{Ref, RefCountable};
+use crate::types::{SSAVariable, Variable};
+
+pub struct OperandIter<F: ILFunction + RefCountable> {
+ function: Ref<F>,
+ remaining: usize,
+ next_iter_idx: Option<usize>,
+ current_iter: OperandIterInner,
+}
+
+impl<F: ILFunction + RefCountable> OperandIter<F> {
+ pub(crate) fn new(function: &F, idx: usize, number: usize) -> Self {
+ Self {
+ function: function.to_owned(),
+ remaining: number,
+ next_iter_idx: Some(idx),
+ current_iter: OperandIterInner::empty(),
+ }
+ }
+
+ pub fn pairs(self) -> OperandPairIter<F> {
+ assert_eq!(self.len() % 2, 0);
+ OperandPairIter(self)
+ }
+
+ pub fn exprs(self) -> OperandExprIter<F> {
+ OperandExprIter(self)
+ }
+
+ pub fn vars(self) -> OperandVarIter<F> {
+ OperandVarIter(self)
+ }
+
+ pub fn ssa_vars(self) -> OperandSSAVarIter<F> {
+ OperandSSAVarIter(self.pairs())
+ }
+}
+
+impl<F: ILFunction + RefCountable> Iterator for OperandIter<F> {
+ type Item = u64;
+ fn next(&mut self) -> Option<Self::Item> {
+ if let Some(item) = self.current_iter.next() {
+ self.remaining -= 1;
+ Some(item)
+ } else {
+ // Will short-circuit and return `None` once iter is exhausted
+ let iter_idx = self.next_iter_idx?;
+ let operands = self.function.operands_from_idx(iter_idx);
+
+ let next = if self.remaining > 4 {
+ self.next_iter_idx = Some(operands[4] as usize);
+ &operands[..4]
+ } else {
+ self.next_iter_idx = None;
+ &operands[..self.remaining]
+ };
+
+ self.current_iter = OperandIterInner::from_slice(next);
+ self.next()
+ }
+ }
+}
+impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandIter<F> {
+ fn len(&self) -> usize {
+ self.remaining + self.current_iter.len()
+ }
+}
+
+struct OperandIterInner {
+ arr: [u64; 4],
+ idx: usize,
+}
+
+impl OperandIterInner {
+ fn from_slice(slice: &[u64]) -> Self {
+ assert!(slice.len() <= 4);
+ let idx = 4 - slice.len();
+ let mut arr = [0; 4];
+ arr[idx..].copy_from_slice(slice);
+ Self { arr, idx }
+ }
+
+ fn empty() -> Self {
+ Self {
+ arr: [0; 4],
+ idx: 4,
+ }
+ }
+}
+
+impl Iterator for OperandIterInner {
+ type Item = u64;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.idx < 4 {
+ let val = self.arr[self.idx];
+ self.idx += 1;
+ Some(val)
+ } else {
+ None
+ }
+ }
+}
+impl ExactSizeIterator for OperandIterInner {
+ fn len(&self) -> usize {
+ 4 - self.idx
+ }
+}
+
+pub struct OperandPairIter<F: ILFunction + RefCountable>(OperandIter<F>);
+impl<F: ILFunction + RefCountable> Iterator for OperandPairIter<F> {
+ type Item = (u64, u64);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let first = self.0.next()?;
+ let second = self.0.next()?;
+ Some((first, second))
+ }
+}
+impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandPairIter<F> {
+ fn len(&self) -> usize {
+ self.0.len() / 2
+ }
+}
+
+pub struct OperandExprIter<F: ILFunction + RefCountable>(OperandIter<F>);
+impl<F: ILFunction + RefCountable> Iterator for OperandExprIter<F> {
+ type Item = F::Instruction;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.0
+ .next()
+ .map(|idx| self.0.function.il_instruction_from_idx(idx as usize))
+ }
+}
+impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandExprIter<F> {
+ fn len(&self) -> usize {
+ self.0.len()
+ }
+}
+
+pub struct OperandVarIter<F: ILFunction + RefCountable>(OperandIter<F>);
+impl<F: ILFunction + RefCountable> Iterator for OperandVarIter<F> {
+ type Item = Variable;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.0.next().map(get_var)
+ }
+}
+impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandVarIter<F> {
+ fn len(&self) -> usize {
+ self.0.len()
+ }
+}
+
+pub struct OperandSSAVarIter<F: ILFunction + RefCountable>(OperandPairIter<F>);
+impl<F: ILFunction + RefCountable> Iterator for OperandSSAVarIter<F> {
+ type Item = SSAVariable;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.0
+ .next()
+ .map(|(id, version)| get_var_ssa(id, version as usize))
+ }
+}
+impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandSSAVarIter<F> {
+ fn len(&self) -> usize {
+ self.0.len()
+ }
+}
+
+pub fn get_var(id: u64) -> Variable {
+ unsafe { Variable::from_raw(BNFromVariableIdentifier(id)) }
+}
+
+pub fn get_var_ssa(id: u64, version: usize) -> SSAVariable {
+ SSAVariable::new(get_var(id), version)
+}