neo-decompiler 0.10.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
use crate::instruction::OpCode;

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

impl HighLevelEmitter {
    pub(super) fn detect_else(
        &self,
        branch_offset: usize,
        false_offset: usize,
    ) -> Option<(usize, usize)> {
        let target_index = *self.index_by_offset.get(&false_offset)?;
        if target_index == 0 {
            return None;
        }
        let jump = self.program.get(target_index.checked_sub(1)?)?;
        if !matches!(jump.opcode, OpCode::Jmp | OpCode::Jmp_L) {
            return None;
        }
        let target = self.forward_jump_target(jump)?;
        if target <= false_offset {
            return None;
        }

        let jump_offset = jump.offset;
        let has_external_entry = self.program.iter().any(|ins| {
            Self::is_conditional_branch(ins.opcode)
                && ins.offset < branch_offset
                && self
                    .forward_jump_target(ins)
                    .map(|target| target > branch_offset && target <= jump_offset)
                    .unwrap_or(false)
        });
        if has_external_entry {
            return None;
        }

        Some((jump_offset, target))
    }

    /// Detect an implicit else branch when the if-true body ends with a
    /// noreturn instruction and the false target begins a substantive body.
    pub(super) fn detect_implicit_else(
        &self,
        branch_offset: usize,
        false_offset: usize,
    ) -> Option<usize> {
        let target_index = *self.index_by_offset.get(&false_offset)?;
        if target_index == 0 {
            return None;
        }
        let prev = self.program.get(target_index.checked_sub(1)?)?;

        let is_direct_terminator = matches!(
            prev.opcode,
            OpCode::Abort | OpCode::Abortmsg | OpCode::Throw | OpCode::Ret
        );
        let is_noreturn_call = matches!(prev.opcode, OpCode::Call | OpCode::Call_L)
            && self
                .call_targets_by_offset
                .get(&prev.offset)
                .is_some_and(|target| self.noreturn_method_offsets.contains(target));
        if !is_direct_terminator && !is_noreturn_call {
            return None;
        }

        if self.catch_targets.contains_key(&false_offset)
            || self.finally_targets.contains_key(&false_offset)
        {
            return None;
        }

        if self.has_inner_escape_to_false_target(branch_offset, false_offset) {
            return None;
        }
        if self
            .pending_closers
            .get(&false_offset)
            .copied()
            .unwrap_or(0)
            > 1
        {
            return None;
        }

        let else_end = self.detect_implicit_else_end(false_offset);
        if !self.implicit_else_has_body(target_index, else_end) {
            return None;
        }
        if self.finally_body_has_sequential_cleanup(false_offset, target_index, else_end) {
            return None;
        }
        if self
            .pending_closers
            .keys()
            .any(|&offset| offset > false_offset && offset < else_end)
        {
            return None;
        }

        Some(else_end)
    }

    fn has_inner_escape_to_false_target(&self, branch_offset: usize, false_offset: usize) -> bool {
        self.program.iter().any(|ins| {
            ins.offset > branch_offset
                && ins.offset < false_offset
                && Self::is_conditional_branch(ins.opcode)
                && self.forward_jump_target(ins) == Some(false_offset)
        })
    }

    fn detect_implicit_else_end(&self, false_offset: usize) -> usize {
        let next_boundary = self
            .catch_targets
            .keys()
            .chain(self.finally_targets.keys())
            .filter(|&&offset| offset > false_offset)
            .min()
            .copied();

        let fallback = self.program.last().map(|ins| ins.offset + 1);
        next_boundary.unwrap_or_else(|| fallback.unwrap_or(false_offset + 1))
    }

    fn implicit_else_has_body(&self, target_index: usize, else_end: usize) -> bool {
        let end_index = self
            .index_by_offset
            .range(else_end..)
            .next()
            .map(|(_, idx)| *idx)
            .unwrap_or(self.program.len());
        self.program[target_index..end_index].iter().any(|ins| {
            !matches!(
                ins.opcode,
                OpCode::Endtry | OpCode::EndtryL | OpCode::Nop | OpCode::Jmp | OpCode::Jmp_L
            )
        })
    }

    fn finally_body_has_sequential_cleanup(
        &self,
        false_offset: usize,
        target_index: usize,
        else_end: usize,
    ) -> bool {
        let inside_finally = self
            .finally_body_ranges
            .iter()
            .any(|&(start, end)| false_offset >= start && false_offset < end);
        if !inside_finally {
            return false;
        }

        let end_index = self
            .index_by_offset
            .range(else_end..)
            .next()
            .map(|(_, idx)| *idx)
            .unwrap_or(self.program.len());
        self.program[target_index..end_index]
            .iter()
            .any(|ins| matches!(ins.opcode, OpCode::Endfinally))
    }
}