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, HighLevelOutput};

impl HighLevelEmitter {
    pub(crate) fn finish(mut self) -> HighLevelOutput {
        self.flush_pending_closers();
        self.postprocess_statements();
        self.statements.retain(|line| !line.trim().is_empty());
        HighLevelOutput {
            statements: self.statements,
            warnings: self.warnings,
        }
    }

    fn flush_pending_closers(&mut self) {
        // Flush remaining block closers (BTreeMap iterates in key order; no
        // sort needed). Clamp to the current open-brace depth: malformed TRY
        // operands can register a closer whose catch/finally header is never
        // emitted, and clamping avoids a stray `}` in that case.
        let mut depth = Self::open_brace_depth(&self.statements);
        for (_, count) in std::mem::take(&mut self.pending_closers) {
            for _ in 0..count {
                if depth <= 0 {
                    break;
                }
                self.statements.push("}".into());
                depth -= 1;
            }
        }
    }

    fn postprocess_statements(&mut self) {
        Self::rewrite_else_if_chains(&mut self.statements);
        Self::collapse_overflow_checks(&mut self.statements);
        Self::rewrite_goto_do_while(&mut self.statements);
        Self::rewrite_if_goto_to_while(&mut self.statements);
        Self::eliminate_fallthrough_gotos(&mut self.statements);
        Self::rewrite_label_goto_to_loop(&mut self.statements);
        Self::remove_orphaned_labels(&mut self.statements);
        Self::rewrite_for_loops(&mut self.statements);
        // Note: inline_single_use_temps is available but disabled by default
        // as it can be too aggressive for some use cases. Enable selectively.
        Self::inline_condition_temps(&mut self.statements);
        Self::inline_for_increment_temps(&mut self.statements);
        if self.inline_single_use_temps {
            Self::inline_single_use_temps(&mut self.statements);
        }
        Self::rewrite_compound_assignments(&mut self.statements);
        Self::rewrite_indexing_syntax(&mut self.statements);
        Self::collapse_if_true(&mut self.statements);
        Self::invert_empty_if_else(&mut self.statements);
        Self::remove_empty_if(&mut self.statements);
        Self::strip_stack_comments(&mut self.statements);
        Self::eliminate_identity_temps(&mut self.statements);
        Self::collapse_temp_into_store(&mut self.statements);
        if self.inline_single_use_temps {
            // Pairs with the inliner: removing dead `let tN = pure_value;`
            // makes outputs after empty-if elimination read as natural code.
            Self::eliminate_dead_temps(&mut self.statements);
            // Inliner adds parentheses around multi-token substitutions for
            // precedence safety; collapse redundant nested pairs afterwards.
            Self::reduce_double_parens(&mut self.statements);
        }
        // Inlining and dead-temp removal can collapse the body between a
        // preserved transfer and its label target. Re-run elimination and
        // orphan-label cleanup so those pairs drop from clean output.
        Self::eliminate_fallthrough_gotos(&mut self.statements);
        Self::remove_orphaned_labels(&mut self.statements);
        Self::rewrite_switch_statements(&mut self.statements);
        Self::rewrite_switch_break_gotos(&mut self.statements);
        // Final formatting cleanup: join `}\n<chain>` pairs into K&R form.
        // Runs last so other passes can keep asserting intermediate line
        // vectors with separate `}` and `else {` entries.
        Self::join_close_brace_with_chain(&mut self.statements);
    }

    /// Net open-brace depth across the emitted statements, using the same
    /// structural heuristic as the postprocess passes: a line that ends with
    /// `{` opens a block and a line that is exactly `}` (or starts with `} `)
    /// closes one. Braces inside string literals never reach the end of a
    /// statement line, so they do not affect the count.
    fn open_brace_depth(statements: &[String]) -> i32 {
        let mut depth = 0i32;
        for line in statements {
            let trimmed = line.trim();
            if trimmed.ends_with('{') {
                depth += 1;
            } else if trimmed == "}" || trimmed.starts_with("} ") {
                depth -= 1;
            }
        }
        depth
    }
}