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 crate::instruction::{Instruction, OpCode};

use super::super::HighLevelEmitter;

impl HighLevelEmitter {
    pub(super) fn try_emit_collection_ops(&mut self, instruction: &Instruction) -> bool {
        use OpCode::*;

        match instruction.opcode {
            Newbuffer => self.unary_op(instruction, |val| format!("new_buffer({val})")),
            Memcpy => self.emit_call(instruction, "memcpy", 5, false),
            Newarray0 => self.push_literal(instruction, "[]".into()),
            Newarray => self.unary_op(instruction, |val| format!("new_array({val})")),
            NewarrayT => {
                // Unrecognized StackItemType bytes surface as the raw byte
                // (0xNN), matching the ISTYPE fallback, so the reader can
                // see which type the bytecode actually requested.
                let ty = instruction
                    .operand
                    .as_ref()
                    .and_then(super::super::convert_target_name)
                    .map(|t| format!("{t:?}"))
                    .or_else(|| {
                        instruction
                            .operand
                            .as_ref()
                            .map(super::super::format_type_operand)
                    })
                    .unwrap_or_else(|| "unknown".to_string());
                self.unary_op(instruction, |val| format!("new_array_t({val}, {ty})"))
            }
            Newstruct0 => self.push_literal(instruction, "Struct()".into()),
            Newstruct => self.unary_op(instruction, |val| format!("new_struct({val})")),
            Newmap => self.push_literal(instruction, "Map()".into()),
            Pack => self.emit_pack(instruction, "array"),
            Packmap => self.emit_pack(instruction, "map"),
            Packstruct => self.emit_pack(instruction, "struct"),
            Unpack => self.emit_unpack(instruction),
            Pickitem => self.binary_index(instruction),
            Setitem => self.emit_call(instruction, "set_item", 3, false),
            Append => self.emit_call(instruction, "append", 2, false),
            Reverseitems => self.emit_call(instruction, "reverse_items", 1, false),
            Remove => self.emit_call(instruction, "remove_item", 2, false),
            Clearitems => self.emit_call(instruction, "clear_items", 1, false),
            Popitem => self.emit_call(instruction, "pop_item", 1, true),
            Isnull => self.unary_op(instruction, |val| format!("is_null({val})")),
            Istype => self.emit_is_type(instruction),
            // Emit the call form directly (`has_key(target, key)`) rather than
            // an infix `target has_key key` that a later pass rewrites: the
            // infix->call rewrite mangles a tail-position result into
            // `has_key(return target, key)` once single-use inlining has folded
            // the map temp into the RET. Emitting the call up front keeps the
            // result a tail expression and matches the JS port.
            Haskey => self.emit_call(instruction, "has_key", 2, true),
            Keys => self.unary_op(instruction, |val| format!("keys({val})")),
            Values => self.unary_op(instruction, |val| format!("values({val})")),
            Size => self.unary_op(instruction, |val| format!("len({val})")),
            Convert => self.emit_convert(instruction),
            _ => return false,
        }

        true
    }
}