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
use super::super::super::HighLevelEmitter;

impl HighLevelEmitter {
    /// Collapses `if true { ... }` blocks into their body.
    pub(crate) fn collapse_if_true(statements: &mut Vec<String>) {
        let mut index = 0;
        while index < statements.len() {
            if statements[index].trim() != "if true {" {
                index += 1;
                continue;
            }
            let Some(end) = Self::find_block_end(statements, index) else {
                index += 1;
                continue;
            };
            if statements[end].trim() != "}" {
                index += 1;
                continue;
            }
            statements.remove(end);
            statements.remove(index);
        }
    }

    /// Inverts `if cond { } else { ... }` -> `if !(cond) { ... }`.
    pub(crate) fn invert_empty_if_else(statements: &mut Vec<String>) {
        let mut index = 0;
        while index < statements.len() {
            let trimmed = statements[index].trim();
            if !trimmed.starts_with("if ") || !trimmed.ends_with('{') {
                index += 1;
                continue;
            }
            let mut close_if = index + 1;
            while close_if < statements.len() {
                let t = statements[close_if].trim();
                if !t.is_empty() && !t.starts_with("//") {
                    break;
                }
                close_if += 1;
            }
            if close_if >= statements.len() || statements[close_if].trim() != "}" {
                index += 1;
                continue;
            }
            if close_if + 1 >= statements.len() || statements[close_if + 1].trim() != "else {" {
                index += 1;
                continue;
            }
            let else_line = close_if + 1;
            let Some(_else_end) = Self::find_block_end(statements, else_line) else {
                index += 1;
                continue;
            };

            let indent = &statements[index][..statements[index].len() - trimmed.len()];
            let cond = &trimmed[3..trimmed.len() - 2];
            let negated = Self::negate_condition(cond);
            statements[index] = format!("{indent}if {negated} {{");
            statements.drain(close_if..=else_line);
        }
    }

    /// Removes `if cond { }` blocks with no else branch.
    pub(crate) fn remove_empty_if(statements: &mut Vec<String>) {
        let mut index = 0;
        while index < statements.len() {
            let trimmed = statements[index].trim();
            if !trimmed.starts_with("if ") || !trimmed.ends_with('{') {
                index += 1;
                continue;
            }
            let mut close_if = index + 1;
            while close_if < statements.len() {
                let t = statements[close_if].trim();
                if !t.is_empty() && !t.starts_with("//") {
                    break;
                }
                close_if += 1;
            }
            if close_if >= statements.len() || statements[close_if].trim() != "}" {
                index += 1;
                continue;
            }
            if close_if + 1 < statements.len()
                && statements[close_if + 1].trim().starts_with("else")
            {
                index += 1;
                continue;
            }
            statements.drain(index..=close_if);
        }
    }

    fn negate_condition(cond: &str) -> String {
        let cond = cond.trim();
        if cond.contains(" && ") || cond.contains(" || ") {
            return format!("!({cond})");
        }
        for (op, neg) in [
            (" == ", " != "),
            (" != ", " == "),
            (" >= ", " < "),
            (" <= ", " > "),
            (" > ", " <= "),
            (" < ", " >= "),
        ] {
            if let Some(pos) = cond.find(op) {
                return format!("{}{}{}", &cond[..pos], neg, &cond[pos + op.len()..]);
            }
        }
        if let Some(inner) = cond.strip_prefix('!') {
            return inner.to_string();
        }
        format!("!({cond})")
    }
}