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 super::super::*;
use crate::instruction::OpCode;

#[test]
fn high_level_lifts_local_slots() {
    // Script: INITSLOT 1,0; PUSH1; STLOC0; LDLOC0; RET
    let script = [
        OpCode::Initslot.byte(),
        0x01,
        0x00,
        OpCode::Push1.byte(),
        OpCode::Stloc0.byte(),
        OpCode::Ldloc0.byte(),
        OpCode::Ret.byte(),
    ];
    let nef_bytes = build_nef(&script);
    let decompilation = Decompiler::new()
        .decompile_bytes(&nef_bytes)
        .expect("decompile succeeds");

    let high_level = decompilation
        .high_level
        .as_deref()
        .expect("high-level output");
    assert!(high_level.contains("// declare 1 locals, 0 arguments"));
    assert!(
        high_level.contains("let loc0 = 1;"),
        "expected collapsed store: {high_level}"
    );
    assert!(high_level.contains("return loc0;"));
}

#[test]
fn high_level_lifts_all_local_slot_variants() {
    // Exercise LDLOC0-6 (0x68-0x6E) and STLOC0-6 (0x70-0x76).
    // Script: INITSLOT 7,0;
    //   PUSH0; STLOC0; PUSH1; STLOC1; PUSH2; STLOC2;
    //   PUSH3; STLOC3; PUSH4; STLOC4; PUSH5; STLOC5;
    //   PUSH6; STLOC6;
    //   LDLOC0; LDLOC1; LDLOC2; LDLOC3; LDLOC4; LDLOC5; LDLOC6;
    //   RET
    let script = [
        OpCode::Initslot.byte(),
        0x07,
        0x00, // INITSLOT 7 locals, 0 args
        OpCode::Push0.byte(),
        OpCode::Stloc0.byte(), // PUSH0; STLOC0
        OpCode::Push1.byte(),
        OpCode::Stloc1.byte(), // PUSH1; STLOC1
        OpCode::Push2.byte(),
        OpCode::Stloc2.byte(), // PUSH2; STLOC2
        OpCode::Push3.byte(),
        OpCode::Stloc3.byte(), // PUSH3; STLOC3
        OpCode::Push4.byte(),
        OpCode::Stloc4.byte(), // PUSH4; STLOC4
        OpCode::Push5.byte(),
        OpCode::Stloc5.byte(), // PUSH5; STLOC5
        OpCode::Push6.byte(),
        OpCode::Stloc6.byte(), // PUSH6; STLOC6
        OpCode::Ldloc0.byte(),
        OpCode::Ldloc1.byte(),
        OpCode::Ldloc2.byte(),
        OpCode::Ldloc3.byte(),
        OpCode::Ldloc4.byte(),
        OpCode::Ldloc5.byte(),
        OpCode::Ldloc6.byte(), // LDLOC0-6
        OpCode::Ret.byte(),    // RET
    ];
    let nef_bytes = build_nef(&script);
    let decompilation = Decompiler::new()
        .decompile_bytes(&nef_bytes)
        .expect("decompile succeeds");

    let high_level = decompilation
        .high_level
        .as_deref()
        .expect("high-level output");
    assert!(
        high_level.contains("// declare 7 locals, 0 arguments"),
        "should declare 7 locals: {high_level}"
    );
    for i in 0..7 {
        assert!(
            high_level.contains(&format!("loc{i}")),
            "local slot {i} should appear in output: {high_level}"
        );
    }
}

#[test]
fn high_level_lifts_all_argument_slot_variants() {
    // Exercise LDARG0-6 (0x78-0x7E) and STARG0-6 (0x80-0x86).
    // Script: INITSLOT 0,7;
    //   LDARG0; STARG0; LDARG1; STARG1; LDARG2; STARG2;
    //   LDARG3; STARG3; LDARG4; STARG4; LDARG5; STARG5;
    //   LDARG6; STARG6;
    //   RET
    let script = [
        OpCode::Initslot.byte(),
        0x00,
        0x07, // INITSLOT 0 locals, 7 args
        OpCode::Ldarg0.byte(),
        OpCode::Starg0.byte(), // LDARG0; STARG0
        OpCode::Ldarg1.byte(),
        OpCode::Starg1.byte(), // LDARG1; STARG1
        OpCode::Ldarg2.byte(),
        OpCode::Starg2.byte(), // LDARG2; STARG2
        OpCode::Ldarg3.byte(),
        OpCode::Starg3.byte(), // LDARG3; STARG3
        OpCode::Ldarg4.byte(),
        OpCode::Starg4.byte(), // LDARG4; STARG4
        OpCode::Ldarg5.byte(),
        OpCode::Starg5.byte(), // LDARG5; STARG5
        OpCode::Ldarg6.byte(),
        OpCode::Starg6.byte(), // LDARG6; STARG6
        OpCode::Ret.byte(),    // RET
    ];
    let nef_bytes = build_nef(&script);
    let decompilation = Decompiler::new()
        .decompile_bytes(&nef_bytes)
        .expect("decompile succeeds");

    let high_level = decompilation
        .high_level
        .as_deref()
        .expect("high-level output");
    assert!(
        high_level.contains("// declare 0 locals, 7 arguments"),
        "should declare 7 arguments: {high_level}"
    );
    for i in 0..7 {
        assert!(
            high_level.contains(&format!("arg{i}")),
            "argument slot {i} should appear in output: {high_level}"
        );
    }
}

#[test]
fn high_level_lifts_indexed_local_slot() {
    // Exercise LDLOC (0x6F) and STLOC (0x77) with index operand.
    // Script: INITSLOT 8,0; PUSH1; STLOC 7; LDLOC 7; RET
    let script = [
        OpCode::Initslot.byte(),
        0x08,
        0x00,                 // INITSLOT 8 locals, 0 args
        OpCode::Push1.byte(), // PUSH1
        OpCode::Stloc.byte(),
        0x07, // STLOC 7
        OpCode::Ldloc.byte(),
        0x07,               // LDLOC 7
        OpCode::Ret.byte(), // RET
    ];
    let nef_bytes = build_nef(&script);
    let decompilation = Decompiler::new()
        .decompile_bytes(&nef_bytes)
        .expect("decompile succeeds");

    let high_level = decompilation
        .high_level
        .as_deref()
        .expect("high-level output");
    assert!(
        high_level.contains("loc7"),
        "indexed LDLOC/STLOC should reference loc7: {high_level}"
    );
}

#[test]
fn high_level_lifts_indexed_argument_slot() {
    // Exercise LDARG (0x7F) and STARG (0x87) with index operand.
    // Script: INITSLOT 0,8; LDARG 7; PUSH1; STARG 7; RET
    let script = [
        OpCode::Initslot.byte(),
        0x00,
        0x08, // INITSLOT 0 locals, 8 args
        OpCode::Ldarg.byte(),
        0x07,                 // LDARG 7
        OpCode::Push1.byte(), // PUSH1
        OpCode::Starg.byte(),
        0x07,               // STARG 7
        OpCode::Ret.byte(), // RET
    ];
    let nef_bytes = build_nef(&script);
    let decompilation = Decompiler::new()
        .decompile_bytes(&nef_bytes)
        .expect("decompile succeeds");

    let high_level = decompilation
        .high_level
        .as_deref()
        .expect("high-level output");
    assert!(
        high_level.contains("arg7"),
        "indexed LDARG/STARG should reference arg7: {high_level}"
    );
}