neo-decompiler 0.8.1

Neo N3 NEF decompiler: parse, disassemble, lift bytecode to high-level pseudocode and C# skeletons, with a CLI, JSON reports, and optional WebAssembly bindings.
Documentation
// Bytecode offset arithmetic requires isize↔usize casts for signed jump deltas.
// NEF scripts are bounded (~1 MB), so these conversions are structurally safe.
#![allow(
    clippy::cast_possible_truncation,
    clippy::cast_possible_wrap,
    clippy::cast_sign_loss
)]

use crate::instruction::{Instruction, Operand};

use super::super::super::HighLevelEmitter;

impl HighLevelEmitter {
    pub(super) fn try_handler_targets(
        &self,
        instruction: &Instruction,
    ) -> Option<(usize, Option<usize>, Option<usize>)> {
        let Operand::Bytes(bytes) = instruction.operand.as_ref()? else {
            return None;
        };

        let (catch_delta, finally_delta) = match bytes.as_slice() {
            [catch, finally] => (*catch as i8 as isize, *finally as i8 as isize),
            slice if slice.len() == 8 => {
                let catch_delta = i32::from_le_bytes(slice[0..4].try_into().ok()?) as isize;
                let finally_delta = i32::from_le_bytes(slice[4..8].try_into().ok()?) as isize;
                (catch_delta, finally_delta)
            }
            _ => return None,
        };

        let width = 1 + bytes.len();
        let body_start = instruction.offset + width;
        let catch_target = if catch_delta != 0 {
            // Neo VM: target = opcode_offset + delta (relative to instruction start).
            let target = instruction.offset as isize + catch_delta;
            (target > instruction.offset as isize).then_some(target as usize)
        } else {
            None
        };
        let finally_target = if finally_delta != 0 {
            // Neo VM: target = opcode_offset + delta (relative to instruction start).
            let target = instruction.offset as isize + finally_delta;
            (target > instruction.offset as isize).then_some(target as usize)
        } else {
            None
        };

        Some((body_start, catch_target, finally_target))
    }
}