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 crate::decompiler::ir::{BinOp, Literal, UnaryOp};

use super::super::form::SsaExpr;

/// Extract a [`Literal`] from an expression that is already literal.
pub(super) fn as_literal(expr: &SsaExpr) -> Option<Literal> {
    if let SsaExpr::Literal(l) = expr {
        Some(l.clone())
    } else {
        None
    }
}

/// Constant-fold a binary operation on two integer literals when it is safe.
pub(super) fn fold_binary(op: BinOp, a: &Literal, b: &Literal) -> Option<Literal> {
    let (Literal::Int(x), Literal::Int(y)) = (a, b) else {
        return None;
    };
    let (x, y) = (*x, *y);
    Some(match op {
        BinOp::Add => Literal::Int(x.wrapping_add(y)),
        BinOp::Sub => Literal::Int(x.wrapping_sub(y)),
        BinOp::Mul => Literal::Int(x.wrapping_mul(y)),
        BinOp::Div => {
            if y == 0 {
                return None;
            }
            Literal::Int(x.wrapping_div(y))
        }
        BinOp::Mod => {
            if y == 0 {
                return None;
            }
            Literal::Int(x.wrapping_rem(y))
        }
        BinOp::Pow => (0..y)
            .take(1024)
            .try_fold(1i64, |acc, _| acc.checked_mul(x))
            .map(Literal::Int)?,
        BinOp::And => Literal::Int(x & y),
        BinOp::Or => Literal::Int(x | y),
        BinOp::Xor => Literal::Int(x ^ y),
        BinOp::Shl => Literal::Int(x.wrapping_shl((y & 63) as u32)),
        BinOp::Shr => Literal::Int(x.wrapping_shr((y & 63) as u32)),
        BinOp::Eq => Literal::Bool(x == y),
        BinOp::Ne => Literal::Bool(x != y),
        BinOp::Lt => Literal::Bool(x < y),
        BinOp::Le => Literal::Bool(x <= y),
        BinOp::Gt => Literal::Bool(x > y),
        BinOp::Ge => Literal::Bool(x >= y),
        BinOp::LogicalAnd => Literal::Bool(x != 0 && y != 0),
        BinOp::LogicalOr => Literal::Bool(x != 0 || y != 0),
    })
}

/// Constant-fold a unary operation on an integer literal.
pub(super) fn fold_unary(op: UnaryOp, a: &Literal) -> Option<Literal> {
    let Literal::Int(x) = a else {
        return None;
    };
    Some(match op {
        UnaryOp::Neg => Literal::Int(x.wrapping_neg()),
        UnaryOp::Not => Literal::Int(!x),
        UnaryOp::Abs => Literal::Int(x.wrapping_abs()),
        UnaryOp::Inc => Literal::Int(x.wrapping_add(1)),
        UnaryOp::Dec => Literal::Int(x.wrapping_sub(1)),
        UnaryOp::Sign => Literal::Int(x.signum()),
        UnaryOp::LogicalNot => Literal::Bool(*x == 0),
    })
}