use super::*;
use crate::instruction::OpCode;
#[test]
fn csharp_synthetic_script_entry_exposes_initslot_args_and_preserves_return() {
let nef_bytes = build_nef(&[
OpCode::Initslot.byte(),
0x00,
0x01,
OpCode::Ldarg0.byte(),
OpCode::Isnull.byte(),
OpCode::Not.byte(),
OpCode::Ret.byte(),
]);
let decompilation = Decompiler::new()
.decompile_bytes_with_manifest(&nef_bytes, None, OutputFormat::All)
.expect("decompile succeeds");
let csharp = decompilation.csharp.as_deref().expect("csharp output");
assert!(
csharp.contains("public static object ScriptEntry(object arg0)"),
"synthetic ScriptEntry should declare INITSLOT-counted args: {csharp}"
);
assert!(
csharp.contains("return !t0;") || csharp.contains("return !(arg0 is null);"),
"lifted return value should be preserved (not dropped via void signature): {csharp}"
);
assert!(
csharp.contains("(arg0 is null)") || csharp.contains("is_null(arg0)"),
"body should reference arg0 from the new parameter: {csharp}"
);
}
#[test]
fn csharp_omits_trailing_return_in_void_methods() {
let nef_bytes = build_nef(&[OpCode::Ret.byte()]);
let decompilation = Decompiler::new()
.decompile_bytes_with_manifest(&nef_bytes, None, OutputFormat::All)
.expect("decompile succeeds");
let csharp = decompilation.csharp.as_deref().expect("csharp output");
assert!(
csharp.contains("public static object ScriptEntry()"),
"synthetic ScriptEntry should default to object return when no manifest is provided: {csharp}"
);
assert!(
csharp.contains("return default;"),
"synthetic ScriptEntry with bare RET should yield `return default;`: {csharp}"
);
}
#[test]
fn csharp_keeps_explicit_return_value_in_non_void_methods() {
let nef_bytes = build_nef(&[OpCode::Push1.byte(), OpCode::Ret.byte()]);
let manifest = ContractManifest::from_json_str(
r#"
{
"name": "ReturnsInt",
"abi": {
"methods": [
{
"name": "main",
"parameters": [],
"returntype": "Integer",
"offset": 0
}
],
"events": []
},
"permissions": [],
"trusts": "*"
}
"#,
)
.expect("manifest parsed");
let decompilation = Decompiler::new()
.decompile_bytes_with_manifest(&nef_bytes, Some(manifest), OutputFormat::All)
.expect("decompile succeeds");
let csharp = decompilation.csharp.as_deref().expect("csharp output");
assert!(
csharp.contains("return 1;"),
"non-void method should keep its computed return: {csharp}"
);
}
#[test]
fn csharp_non_void_method_with_empty_body_throws_not_implemented() {
let nef_bytes = build_nef(&[
OpCode::Initslot.byte(),
0x00,
0x00,
OpCode::Push1.byte(),
OpCode::Push2.byte(),
OpCode::Add.byte(),
OpCode::Ret.byte(),
OpCode::Push16.byte(),
OpCode::Ret.byte(),
]);
let manifest = ContractManifest::from_json_str(
r#"{"name":"Demo","abi":{"methods":[
{"name":"prologue","returntype":"Integer","offset":0,"parameters":[],"safe":false},
{"name":"body","returntype":"Integer","offset":3,"parameters":[],"safe":false}
],"events":[]}}"#,
)
.expect("manifest parsed");
let decompilation = Decompiler::new()
.with_trace_comments(false)
.decompile_bytes_with_manifest(&nef_bytes, Some(manifest), OutputFormat::All)
.expect("decompile succeeds");
let csharp = decompilation.csharp.as_deref().expect("csharp output");
assert!(
csharp.contains("BigInteger prologue()"),
"prologue should render with its declared return type: {csharp}"
);
assert!(
csharp.contains("throw new NotImplementedException();"),
"non-void method with an empty lifted body must throw, not emit a bare comment: {csharp}"
);
}