use super::*;
use crate::instruction::OpCode;
#[test]
fn decompile_syscall_includes_human_name() {
let script = [
OpCode::Syscall.byte(),
0xB2,
0x79,
0xFC,
0xF6,
OpCode::Ret.byte(),
];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
assert!(decompilation
.pseudocode
.as_deref()
.expect("pseudocode output")
.contains("System.Runtime.Platform"));
assert!(decompilation
.high_level
.as_deref()
.expect("high-level output")
.contains("syscall(\"System.Runtime.Platform\")"));
}
#[test]
fn void_syscall_does_not_push_stack_value() {
let script = [
OpCode::Push0.byte(),
OpCode::Push0.byte(),
OpCode::Syscall.byte(),
0x95,
0x01,
0x6F,
0x61,
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("syscall(\"System.Runtime.Notify\""),
"void syscall should be emitted as a statement"
);
assert!(
!high_level.contains("let t0 = syscall(\"System.Runtime.Notify\")"),
"void syscall should not push a temp onto the stack"
);
}
#[test]
fn unknown_syscall_is_assumed_to_return_value() {
let unknown_hash = 0xDEADBEEF;
assert!(
crate::syscalls::lookup(unknown_hash).is_none(),
"fixture hash should not be present in the syscall catalog"
);
let script = [
OpCode::Syscall.byte(),
0xEF,
0xBE,
0xAD,
0xDE,
OpCode::Ret.byte(),
];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high.contains("syscall(0xDEADBEEF)"),
"unknown syscalls should conservatively push a stack value: {high}"
);
assert!(
high.contains("// warning: unknown syscall 0xDEADBEEF"),
"unknown-syscall warning should be visible inline: {high}"
);
}
#[test]
fn syscall_arguments_render_in_declaration_order() {
let script = [
OpCode::Newarray0.byte(), OpCode::Pushdata1.byte(),
0x03,
b'e',
b'v',
b't', OpCode::Syscall.byte(),
0x95,
0x01,
0x6F,
0x61, OpCode::Ret.byte(), ];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.with_inline_single_use_temps(true)
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("syscall(\"System.Runtime.Notify\", \"evt\","),
"event name must render as the first Notify argument: {high_level}"
);
}
#[test]
fn storage_put_arguments_render_in_pop_order() {
let script = [
OpCode::Push1.byte(),
OpCode::Push2.byte(),
OpCode::Push3.byte(),
OpCode::Syscall.byte(),
0xE6,
0x3F,
0x18,
0x84,
OpCode::Ret.byte(),
];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.with_inline_single_use_temps(true)
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("syscall(\"System.Storage.Put\", 3, 2, 1)"),
"Storage.Put arguments must render in pop order: {high_level}"
);
}
#[test]
fn void_storage_syscall_is_emitted_as_statement() {
let script = [
OpCode::Push0.byte(),
OpCode::Push0.byte(),
OpCode::Push0.byte(),
OpCode::Syscall.byte(),
0xE6,
0x3F,
0x18,
0x84,
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("syscall(\"System.Storage.Put\""),
"void storage syscall should be emitted as a statement"
);
assert!(
!high_level.contains("let t0 = syscall(\"System.Storage.Put\")"),
"void storage syscall should not push a temp onto the stack"
);
}
#[test]
fn void_storage_local_syscall_is_emitted_as_statement() {
let script = [
OpCode::Push0.byte(),
OpCode::Push0.byte(),
OpCode::Syscall.byte(),
0x39,
0x0C,
0xE3,
0x0A,
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("syscall(\"System.Storage.Local.Put\""),
"void storage local syscall should be emitted as a statement"
);
assert!(
!high_level.contains("let t0 = syscall(\"System.Storage.Local.Put\")"),
"void storage local syscall should not push a temp onto the stack"
);
}
#[test]
fn syscall_contract_call_returns_value() {
let script = [
OpCode::Push0.byte(),
OpCode::Push0.byte(),
OpCode::Push0.byte(),
OpCode::Push0.byte(),
OpCode::Syscall.byte(),
0x62,
0x7D,
0x5B,
0x52,
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("syscall(\"System.Contract.Call\""),
"System.Contract.Call should resolve to human name: {high_level}"
);
assert!(
high_level.contains("= syscall(\"System.Contract.Call\""),
"System.Contract.Call returns a value and should push a temp: {high_level}"
);
}
#[test]
fn syscall_runtime_log_is_void() {
let script = [
OpCode::Push0.byte(),
OpCode::Syscall.byte(),
0xCF,
0xE7,
0x47,
0x96,
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("syscall(\"System.Runtime.Log\""),
"System.Runtime.Log is void and should be emitted as statement: {high_level}"
);
assert!(
!high_level.contains("let t0 = syscall(\"System.Runtime.Log\")"),
"void syscall should not push a temp: {high_level}"
);
}
#[test]
fn syscall_runtime_log_missing_argument_emits_warning() {
let script = [
OpCode::Syscall.byte(),
0xCF,
0xE7,
0x47,
0x96,
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("syscall(\"System.Runtime.Log\", ???)"),
"missing syscall argument should still be shown in output: {high_level}"
);
assert!(
high_level.contains("missing syscall argument values for System.Runtime.Log"),
"missing syscall argument should be called out inline in the output: {high_level}"
);
assert!(
decompilation
.warnings
.iter()
.any(|warning| warning.contains("missing syscall argument values")),
"missing syscall argument should emit a structured warning: {:?}",
decompilation.warnings
);
}
#[test]
fn syscall_runtime_log_after_packed_store_reports_consumed_slot_context() {
let script = [
OpCode::Initslot.byte(),
0x01,
0x00, OpCode::Pushdata1.byte(),
0x05,
b'H',
b'e',
b'l',
b'l',
b'o', OpCode::Push1.byte(), OpCode::Pack.byte(), OpCode::Stloc0.byte(), OpCode::Syscall.byte(),
0xCF,
0xE7,
0x47,
0x96, 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("syscall(\"System.Runtime.Log\", ???)"),
"missing syscall argument should still be rendered: {high_level}"
);
assert!(
high_level.contains("preceding STLOC0 stored a packed value into loc0"),
"packed-store context should be surfaced inline: {high_level}"
);
assert!(
decompilation.warnings.iter().any(|warning| {
warning.contains("preceding STLOC0 stored a packed value into loc0")
}),
"packed-store context should also be emitted as a structured warning: {:?}",
decompilation.warnings
);
}
#[test]
fn syscall_check_witness_returns_value() {
let script = [
OpCode::Push0.byte(),
OpCode::Syscall.byte(),
0xF8,
0x27,
0xEC,
0x8C,
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("syscall(\"System.Runtime.CheckWitness\""),
"CheckWitness should resolve to human name: {high_level}"
);
assert!(
high_level.contains("= syscall(\"System.Runtime.CheckWitness\""),
"CheckWitness returns a value: {high_level}"
);
}