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 std::fmt;

use crate::decompiler::cfg::ssa::variable::SsaVariable;
use crate::decompiler::ir::{BinOp, Literal, UnaryOp};

/// An expression in SSA form.
///
/// SSA expressions reference `SsaVariable` instead of raw strings,
/// ensuring version tracking through the SSA transformation.
#[derive(Debug, Clone, PartialEq)]
pub enum SsaExpr {
    /// SSA variable reference.
    Variable(SsaVariable),

    /// Literal constant value.
    Literal(Literal),

    /// Binary operation.
    Binary {
        /// The binary operator.
        op: BinOp,
        /// Left-hand operand.
        left: Box<SsaExpr>,
        /// Right-hand operand.
        right: Box<SsaExpr>,
    },

    /// Unary operation.
    Unary {
        /// The unary operator.
        op: UnaryOp,
        /// The operand.
        operand: Box<SsaExpr>,
    },

    /// Function or syscall invocation.
    Call {
        /// Function name.
        name: String,
        /// Call arguments.
        args: Vec<SsaExpr>,
    },

    /// Array/map index access.
    Index {
        /// Base expression being indexed.
        base: Box<SsaExpr>,
        /// Index expression.
        index: Box<SsaExpr>,
    },

    /// Field/member access.
    Member {
        /// Base expression.
        base: Box<SsaExpr>,
        /// Field name.
        name: String,
    },

    /// Type cast.
    Cast {
        /// Expression being cast.
        expr: Box<SsaExpr>,
        /// Target type name.
        target_type: String,
    },

    /// Array literal.
    Array(Vec<SsaExpr>),

    /// Map literal (key-value pairs).
    Map(Vec<(SsaExpr, SsaExpr)>),

    /// Ternary conditional expression.
    Ternary {
        /// Condition expression.
        condition: Box<SsaExpr>,
        /// Value when condition is true.
        then_expr: Box<SsaExpr>,
        /// Value when condition is false.
        else_expr: Box<SsaExpr>,
    },
}

impl SsaExpr {
    /// Create a variable reference.
    #[must_use]
    pub fn var(var: SsaVariable) -> Self {
        Self::Variable(var)
    }

    /// Create a literal expression.
    #[must_use]
    pub const fn lit(literal: Literal) -> Self {
        Self::Literal(literal)
    }

    /// Create a binary expression.
    #[must_use]
    pub fn binary(op: BinOp, left: SsaExpr, right: SsaExpr) -> Self {
        Self::Binary {
            op,
            left: Box::new(left),
            right: Box::new(right),
        }
    }

    /// Create a unary expression.
    #[must_use]
    pub fn unary(op: UnaryOp, operand: SsaExpr) -> Self {
        Self::Unary {
            op,
            operand: Box::new(operand),
        }
    }

    /// Create a function call expression.
    #[must_use]
    pub fn call(name: String, args: Vec<SsaExpr>) -> Self {
        Self::Call { name, args }
    }
}

impl fmt::Display for SsaExpr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Variable(var) => write!(f, "{}", var),
            Self::Literal(lit) => write!(f, "{}", lit),
            Self::Binary { op, left, right } => write!(f, "({} {} {})", left, op, right),
            Self::Unary { op, operand } => write!(f, "{}({})", op, operand),
            Self::Call { name, args } => write_call(f, name, args),
            Self::Index { base, index } => write!(f, "{}[{}]", base, index),
            Self::Member { base, name } => write!(f, "{}.{}", base, name),
            Self::Cast { expr, target_type } => write!(f, "{} as {}", expr, target_type),
            Self::Array(elements) => write_sequence(f, "[", "]", elements),
            Self::Map(pairs) => write_map(f, pairs),
            Self::Ternary {
                condition,
                then_expr,
                else_expr,
            } => write!(f, "{} ? {} : {}", condition, then_expr, else_expr),
        }
    }
}

fn write_call(f: &mut fmt::Formatter<'_>, name: &str, args: &[SsaExpr]) -> fmt::Result {
    write!(f, "{name}(")?;
    for (i, arg) in args.iter().enumerate() {
        if i > 0 {
            write!(f, ", ")?;
        }
        write!(f, "{}", arg)?;
    }
    write!(f, ")")
}

fn write_sequence(
    f: &mut fmt::Formatter<'_>,
    open: &str,
    close: &str,
    elements: &[SsaExpr],
) -> fmt::Result {
    write!(f, "{open}")?;
    for (i, elem) in elements.iter().enumerate() {
        if i > 0 {
            write!(f, ", ")?;
        }
        write!(f, "{}", elem)?;
    }
    write!(f, "{close}")
}

fn write_map(f: &mut fmt::Formatter<'_>, pairs: &[(SsaExpr, SsaExpr)]) -> fmt::Result {
    write!(f, "{{")?;
    for (i, (key, value)) in pairs.iter().enumerate() {
        if i > 0 {
            write!(f, ", ")?;
        }
        write!(f, "{}: {}", key, value)?;
    }
    write!(f, "}}")
}