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 std::collections::{HashSet, HashSet as StdHashSet};

use crate::decompiler::cfg::ssa::DominanceInfo;
use crate::decompiler::cfg::{BlockId, Cfg, Terminator};
use crate::decompiler::ir::{Block as IrBlock, ControlFlow, Stmt};

use super::StructCtx;

/// Identify loop-header blocks using back-edges.
pub(super) fn compute_loop_headers(cfg: &Cfg, dominance: &DominanceInfo) -> HashSet<BlockId> {
    let mut headers = HashSet::new();
    for block in cfg.blocks() {
        for pred in cfg.predecessors(block.id) {
            if *pred == block.id || dominance.strictly_dominates(block.id, *pred) {
                headers.insert(block.id);
                break;
            }
        }
    }
    headers
}

impl<'a> StructCtx<'a> {
    pub(super) fn emit_while_loop(
        &self,
        out: &mut IrBlock,
        bid: BlockId,
        then_target: BlockId,
        else_target: BlockId,
        visited: &mut StdHashSet<BlockId>,
    ) -> Option<BlockId> {
        let cond = Self::comparison_condition_for_block(self.ssa, bid)
            .unwrap_or_else(|| self.condition_for_block(bid));
        let mut body = self.structure_region(then_target, Some(bid), visited, true);
        self.build_loop(out, cond, &mut body);
        Some(else_target)
    }

    pub(super) fn try_emit_do_while(
        &self,
        out: &mut IrBlock,
        bid: BlockId,
        visited: &mut StdHashSet<BlockId>,
    ) -> Option<Option<BlockId>> {
        let (latch, exit) = self.find_dowhile_latch(bid)?;
        let body = self.structure_region(bid, Some(latch), visited, false);
        let cond = self.condition_for_block(latch);
        out.push(Stmt::ControlFlow(Box::new(ControlFlow::do_while(
            body, cond,
        ))));
        visited.insert(latch);
        Some(Some(exit))
    }

    fn build_loop(&self, out: &mut IrBlock, cond: crate::decompiler::ir::Expr, body: &mut IrBlock) {
        out.push(Stmt::ControlFlow(Box::new(ControlFlow::while_loop(
            cond,
            std::mem::take(body),
        ))));
    }

    /// For a do-while loop header, find its branch latch and exit target.
    fn find_dowhile_latch(&self, header: BlockId) -> Option<(BlockId, BlockId)> {
        for pred in self.cfg.predecessors(header) {
            if !self.ssa.dominance.strictly_dominates(header, *pred) {
                continue;
            }
            if let Terminator::Branch {
                then_target,
                else_target,
            } = self.terminator(*pred)
            {
                if then_target == header {
                    return Some((*pred, else_target));
                }
                if else_target == header {
                    return Some((*pred, then_target));
                }
            }
        }
        None
    }
}