neo-decompiler 0.8.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::instruction::{Instruction, Operand};

use super::{HighLevelEmitter, LiteralValue, SlotKind};

impl HighLevelEmitter {
    pub(super) fn emit_init_static_slots(&mut self, instruction: &Instruction) {
        self.push_comment(instruction);
        match instruction.operand {
            Some(Operand::U8(count)) => {
                if self.emit_trace_comments {
                    self.statements
                        .push(format!("// declare {count} static slots"));
                }
                self.static_pointer_values.clear();
            }
            _ => self.statements.push("// missing INITSSLOT operand".into()),
        }
    }

    pub(super) fn emit_init_slots(&mut self, instruction: &Instruction) {
        self.push_comment(instruction);
        match &instruction.operand {
            Some(Operand::Bytes(bytes)) if bytes.len() >= 2 => {
                let locals = bytes[0];
                let args = bytes[1];
                if self.emit_trace_comments {
                    self.statements
                        .push(format!("// declare {locals} locals, {args} arguments"));
                }
                self.local_pointer_values.clear();
            }
            _ => self.statements.push("// missing INITSLOT operand".into()),
        }
    }

    pub(super) fn emit_load_slot(
        &mut self,
        instruction: &Instruction,
        kind: SlotKind,
        index: usize,
    ) {
        self.push_comment(instruction);
        let name = self.slot_label(kind, index);
        match kind {
            SlotKind::Local => {
                if let Some(pointer) = self.local_pointer_values.get(&index).copied() {
                    self.literal_values
                        .insert(name.clone(), LiteralValue::Pointer(pointer));
                } else {
                    self.literal_values.remove(&name);
                }
            }
            SlotKind::Static => {
                if let Some(pointer) = self.static_pointer_values.get(&index).copied() {
                    self.literal_values
                        .insert(name.clone(), LiteralValue::Pointer(pointer));
                } else {
                    self.literal_values.remove(&name);
                }
            }
            SlotKind::Argument => {}
        }
        self.stack.push(name);
    }

    pub(super) fn emit_load_slot_from_operand(
        &mut self,
        instruction: &Instruction,
        kind: SlotKind,
    ) {
        let Some(index) = Self::slot_index_from_operand(instruction) else {
            self.warn(
                instruction,
                &format!("{} missing operand", instruction.opcode),
            );
            return;
        };
        self.emit_load_slot(instruction, kind, index);
    }

    pub(super) fn emit_store_slot(
        &mut self,
        instruction: &Instruction,
        kind: SlotKind,
        index: usize,
    ) {
        self.push_comment(instruction);
        if let Some((value, literal)) = self.pop_stack_value_with_literal() {
            let name = self.slot_label(kind, index);
            let use_let = match kind {
                SlotKind::Local => self.initialized_locals.insert(index),
                SlotKind::Static => self.initialized_statics.insert(index),
                SlotKind::Argument => false,
            };
            if use_let {
                self.statements.push(format!("let {name} = {value};"));
            } else {
                self.statements.push(format!("{name} = {value};"));
            }
            if let Some(elements) = self.packed_values_by_name.get(&value).cloned() {
                self.packed_values_by_name.insert(name.clone(), elements);
            } else {
                self.packed_values_by_name.remove(&name);
            }
            match kind {
                SlotKind::Local => {
                    if let Some(LiteralValue::Pointer(pointer)) = literal {
                        self.local_pointer_values.insert(index, pointer);
                    } else {
                        self.local_pointer_values.remove(&index);
                    }
                }
                SlotKind::Static => {
                    if let Some(LiteralValue::Pointer(pointer)) = literal {
                        self.static_pointer_values.insert(index, pointer);
                    } else {
                        self.static_pointer_values.remove(&index);
                    }
                }
                SlotKind::Argument => {}
            }
        } else {
            self.stack_underflow(instruction, 1);
        }
    }

    pub(super) fn emit_store_slot_from_operand(
        &mut self,
        instruction: &Instruction,
        kind: SlotKind,
    ) {
        let Some(index) = Self::slot_index_from_operand(instruction) else {
            self.warn(
                instruction,
                &format!("{} missing operand", instruction.opcode),
            );
            return;
        };
        self.emit_store_slot(instruction, kind, index);
    }

    fn slot_label(&self, kind: SlotKind, index: usize) -> String {
        match kind {
            SlotKind::Local => format!("loc{index}"),
            SlotKind::Argument => self
                .argument_labels
                .get(&index)
                .cloned()
                .unwrap_or_else(|| format!("arg{index}")),
            SlotKind::Static => format!("static{index}"),
        }
    }

    fn slot_index_from_operand(instruction: &Instruction) -> Option<usize> {
        match instruction.operand {
            Some(Operand::U8(value)) => Some(value as usize),
            _ => None,
        }
    }
}