neo-decompiler 0.11.0

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
/// Upper bound on how many items a single instruction may pop or require during
/// entry-depth inference. Counts can come from attacker-controlled literals
/// (up to `i64::MAX`); clamping keeps malformed contracts from expanding the
/// simulated stack billions of times.
pub(super) const MAX_SIMULATED_POPS: usize = 1024;

#[derive(Default)]
pub(super) struct EntryStack {
    required_entry_depth: usize,
    stack: Vec<Option<usize>>,
}

impl EntryStack {
    pub(super) fn required_entry_depth(&self) -> usize {
        self.required_entry_depth
    }

    pub(super) fn stack(&self) -> &[Option<usize>] {
        &self.stack
    }

    pub(super) fn ensure_available(&mut self, count: usize) {
        let count = count.min(MAX_SIMULATED_POPS);
        while self.stack.len() < count {
            self.stack.insert(0, None);
            self.required_entry_depth += 1;
        }
    }

    pub(super) fn pop_count(&mut self, count: usize) {
        self.ensure_available(count);
        for _ in 0..count {
            self.stack.pop();
        }
    }

    pub(super) fn push(&mut self, value: Option<usize>) {
        self.stack.push(value);
    }

    pub(super) fn push_all(&mut self, values: Vec<Option<usize>>) {
        self.stack.extend(values);
    }

    pub(super) fn top_literal(&self) -> Option<usize> {
        self.stack.last().copied().flatten()
    }

    pub(super) fn value_from_top(&self, depth: usize) -> Option<usize> {
        depth
            .checked_add(1)
            .and_then(|needed| self.stack.len().checked_sub(needed))
            .and_then(|pos| self.stack.get(pos).copied())
            .flatten()
    }

    pub(super) fn remove_from_top(&mut self, depth: usize) -> Option<Option<usize>> {
        depth
            .checked_add(1)
            .and_then(|needed| self.stack.len().checked_sub(needed))
            .map(|pos| self.stack.remove(pos))
    }

    pub(super) fn reverse_top(&mut self, count: usize) {
        let count = count.min(self.stack.len());
        let start = self.stack.len() - count;
        self.stack[start..].reverse();
    }

    pub(super) fn mark_top_unknown(&mut self) {
        if let Some(top) = self.stack.last_mut() {
            *top = None;
        }
    }
}

pub(super) struct StackEffect {
    pub(super) pops: usize,
    pub(super) pushes: Vec<Option<usize>>,
}