neo-decompiler 0.10.2

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 super::super::{HighLevelEmitter, LoopContext};

impl HighLevelEmitter {
    pub(crate) fn advance_to(&mut self, offset: usize) {
        let entering_else = self.else_targets.contains_key(&offset);
        if let Some(count) = self.pending_closers.remove(&offset) {
            for _ in 0..count {
                self.statements.push("}".into());
            }
            if entering_else {
                // Before restoring else-entry state, capture the then-branch
                // terminal stack for the upcoming merge closer (if any). This
                // allows merge-time recovery when the else branch terminates.
                if let Some((&merge_offset, _)) = self.pending_closers.range((offset + 1)..).next()
                {
                    self.branch_saved_stacks
                        .entry(merge_offset)
                        .or_insert_with(|| self.stack.clone());
                }
                // Entering an else block: its entry stack must match the
                // pre-branch stack snapshot, not the stack mutated by the
                // then-branch instructions emitted just above.
                if let Some(saved) = self.branch_saved_stacks.get(&offset).cloned() {
                    self.stack = saved;
                }
            } else {
                self.reconcile_branch_stack_at(offset);
            }
        }

        // Restore try block's exit stack at the resume point after a
        // try-catch. This must live outside the pending_closers gate because
        // the catch closer may be registered at the finally offset rather than
        // the ENDTRY target offset.
        if let Some(saved) = self.try_exit_stacks.remove(&offset) {
            self.stack = saved;
        }

        self.close_loops_at(offset);
        self.open_exception_handlers_at(offset);
        self.open_structural_headers_at(offset);
    }

    fn reconcile_branch_stack_at(&mut self, offset: usize) {
        // Merge point after an if/else (or plain if). Reconcile the stack
        // states from both branches.
        let Some(saved) = self.branch_saved_stacks.remove(&offset) else {
            return;
        };
        let pre_depth = self.pre_branch_stack_depth.remove(&offset).unwrap_or(0);

        if self.stack.is_empty() && !saved.is_empty() {
            self.stack = saved;
        } else if !self.stack.is_empty()
            && !saved.is_empty()
            && self.stack.len() == saved.len()
            && self.stack.len() > pre_depth
        {
            let close_idx = self
                .statements
                .iter()
                .rposition(|s| s.trim() == "}")
                .unwrap_or(self.statements.len());
            let mut inserts = Vec::new();
            for (current, saved_name) in self.stack.iter().zip(saved.iter()).skip(pre_depth) {
                if current != saved_name {
                    inserts.push(format!("let {} = {};", saved_name, current));
                }
            }
            for (j, stmt) in inserts.into_iter().enumerate() {
                self.statements.insert(close_idx + j, stmt);
            }
            self.stack = saved;
        }
    }

    fn open_exception_handlers_at(&mut self, offset: usize) {
        // Catch/finally MUST be emitted before else so that exception handlers
        // appear as siblings of the try block rather than nesting inside an
        // else branch when both targets share the same offset.
        if let Some(count) = self.catch_targets.remove(&offset) {
            // Save the try block's exit stack before the catch handler clears
            // it. This lets us restore the stack at the resume point after the
            // try-catch so values carried through ENDTRY are not lost.
            if let Some(resume) = self.try_catch_resume.remove(&offset) {
                if !self.stack.is_empty() {
                    self.try_exit_stacks
                        .entry(resume)
                        .or_insert_with(|| self.stack.clone());
                }
            }
            for _ in 0..count {
                self.statements.push("catch {".into());
            }
            // Neo VM enters catch handlers with the exception object on top of
            // an unwound evaluation stack.
            self.stack.clear();
            self.stack.push("exception".into());
        }

        if let Some(count) = self.finally_targets.remove(&offset) {
            for _ in 0..count {
                self.statements.push("finally {".into());
            }
        }
    }

    fn open_structural_headers_at(&mut self, offset: usize) {
        if let Some(count) = self.else_targets.remove(&offset) {
            for _ in 0..count {
                self.statements.push("else {".into());
            }
            // Keep the saved pre-branch snapshot until the else block closes.
            // If the else branch terminates, merge-time restoration still
            // needs this snapshot.
        }

        if let Some(entries) = self.do_while_headers.remove(&offset) {
            for entry in entries {
                self.statements.push("do {".into());
                self.active_do_while_tails.insert(entry.tail_offset);
                self.loop_stack.push(LoopContext {
                    break_offset: entry.break_offset,
                    continue_offset: entry.tail_offset,
                });
            }
        }

        if let Some(headers) = self.pending_if_headers.remove(&offset) {
            for header in headers {
                self.statements.push(header);
            }
        }

        if self.transfer_labels.remove(&offset) {
            self.statements
                .push(format!("{}:", Self::transfer_label_name(offset)));
        }
    }
}