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::{BTreeMap, BTreeSet};

use crate::decompiler::cfg::BlockId;

use super::super::variable::{PhiNode, SsaVariable};
use super::state::{fresh_var, phi_var, SlotState};
use super::SsaBuilder;

impl SsaBuilder<'_> {
    /// Compute a block's entry symbolic stack and the phi nodes it needs from
    /// predecessors' current exit stacks.
    pub(super) fn compute_join_entry(
        &self,
        bid: BlockId,
        exit_stacks: &BTreeMap<BlockId, Vec<SsaVariable>>,
    ) -> (Vec<SsaVariable>, Vec<PhiNode>) {
        let preds = self.cfg.predecessors(bid);
        if preds.is_empty() {
            return (Vec::new(), Vec::new());
        }

        let mut entry = Vec::new();
        let mut phis = Vec::new();
        let mut depth = 0usize;
        loop {
            let mut operands: Vec<(BlockId, SsaVariable)> = Vec::new();
            for pred in preds {
                if let Some(stack) = exit_stacks.get(pred) {
                    if let Some(var) = stack.get(depth) {
                        operands.push((*pred, var.clone()));
                    }
                }
            }
            if operands.is_empty() {
                break;
            }

            let first = operands[0].1.clone();
            if operands.iter().all(|(_, v)| *v == first) {
                entry.push(first);
            } else {
                let target = phi_var(bid, depth);
                let mut phi = PhiNode::new(target.clone());
                for (pred, var) in &operands {
                    phi.add_operand(*pred, var.clone());
                }
                entry.push(target);
                phis.push(phi);
            }
            depth += 1;
        }

        (entry, phis)
    }

    /// Compute a block's entry slot state and the phi nodes it needs from
    /// predecessors' current exit slot states.
    pub(super) fn compute_join_slots(
        &self,
        bid: BlockId,
        exit_slots: &BTreeMap<BlockId, SlotState>,
        versions: &mut BTreeMap<String, usize>,
    ) -> (SlotState, Vec<PhiNode>) {
        let preds = self.cfg.predecessors(bid);
        if preds.is_empty() {
            return (SlotState::new(), Vec::new());
        }

        let mut names: BTreeSet<String> = BTreeSet::new();
        for pred in preds {
            if let Some(state) = exit_slots.get(pred) {
                names.extend(state.keys().cloned());
            }
        }

        let mut entry = SlotState::new();
        let mut phis = Vec::new();
        for name in names {
            let operands: Vec<(BlockId, SsaVariable)> = preds
                .iter()
                .filter_map(|pred| {
                    exit_slots
                        .get(pred)
                        .and_then(|slots| slots.get(&name))
                        .map(|var| (*pred, var.clone()))
                })
                .collect();

            if operands.is_empty() {
                continue;
            }

            let first = operands[0].1.clone();
            if operands.iter().all(|(_, v)| *v == first) {
                entry.insert(name, first);
            } else {
                let target = fresh_var(versions, &name);
                let mut phi = PhiNode::new(target.clone());
                for (pred, var) in &operands {
                    phi.add_operand(*pred, var.clone());
                }
                entry.insert(name, target);
                phis.push(phi);
            }
        }

        (entry, phis)
    }
}