2026-03-24 13:40:42 +00:00

133 lines
5.5 KiB
Rust

#[cfg(any())]
mod moved {
use prometeu_bytecode::FunctionMeta;
use prometeu_bytecode::isa::core::CoreOpCode as OpCode;
use crate::prometeu_vm::verifier::{Verifier, VerifierError};
// Re-export path shim for tests since this file is in integration tests crate scope
mod prometeu_vm { pub use prometeu_vm::*; }
#[test]
fn closure_call_valid_passes() {
// F0: PushI32 7; MakeClosure(fn=1, cap=0); CallClosure argc=1; PopN 1; Ret
// F1: PushI32 1; Ret (param_slots=2: hidden arg0 + 1 user arg; returns 1)
let mut code = Vec::new();
// F0 @ 0
code.push(OpCode::PushI32 as u8); code.push(0x00);
code.extend_from_slice(&7u32.to_le_bytes());
code.push(OpCode::MakeClosure as u8); code.push(0x00);
code.extend_from_slice(&1u32.to_le_bytes()); // fn id
code.extend_from_slice(&0u32.to_le_bytes()); // cap count
code.push(OpCode::CallClosure as u8); code.push(0x00);
code.extend_from_slice(&1u32.to_le_bytes()); // argc = 1 (excludes hidden)
code.push(OpCode::PopN as u8); code.push(0x00);
code.extend_from_slice(&1u32.to_le_bytes());
code.push(OpCode::Ret as u8); code.push(0x00);
let f0_len = code.len() as u32;
// F1 @ f0_len
code.push(OpCode::PushI32 as u8); code.push(0x00);
code.extend_from_slice(&1u32.to_le_bytes());
code.push(OpCode::Ret as u8); code.push(0x00);
let f1_len = (code.len() as u32) - f0_len;
let functions = vec![
FunctionMeta { code_offset: 0, code_len: f0_len, return_slots: 0, ..Default::default() },
FunctionMeta { code_offset: f0_len, code_len: f1_len, param_slots: 2, return_slots: 1, ..Default::default() },
];
let res = Verifier::verify(&code, &functions).unwrap();
// Max stack in F0: PushI32 (1), MakeClosure (2), CallClosure pops 2 pushes 1 => (1), PopN to 0
assert!(res[0] >= 2);
}
#[test]
fn closure_call_wrong_argc_fails() {
// Same as previous but argc = 0 while callee expects 1 user arg
let mut code = Vec::new();
// F0 @ 0
code.push(OpCode::PushI32 as u8); code.push(0x00);
code.extend_from_slice(&7u32.to_le_bytes());
code.push(OpCode::MakeClosure as u8); code.push(0x00);
code.extend_from_slice(&1u32.to_le_bytes()); // fn id
code.extend_from_slice(&0u32.to_le_bytes()); // cap count
code.push(OpCode::CallClosure as u8); code.push(0x00);
code.extend_from_slice(&0u32.to_le_bytes()); // argc = 0 (mismatch)
code.push(OpCode::Ret as u8); code.push(0x00);
let f0_len = code.len() as u32;
// F1 @ f0_len
code.push(OpCode::PushI32 as u8); code.push(0x00);
code.extend_from_slice(&1u32.to_le_bytes());
code.push(OpCode::Ret as u8); code.push(0x00);
let f1_len = (code.len() as u32) - f0_len;
let functions = vec![
FunctionMeta { code_offset: 0, code_len: f0_len, return_slots: 0, ..Default::default() },
FunctionMeta { code_offset: f0_len, code_len: f1_len, param_slots: 2, return_slots: 1, ..Default::default() },
];
let res = Verifier::verify(&code, &functions);
assert!(matches!(res, Err(VerifierError::BadClosureArgCount { .. })));
}
#[test]
fn call_closure_on_non_closure_fails() {
// F0: PushI32 7; CallClosure argc=0; Ret
let mut code = Vec::new();
code.push(OpCode::PushI32 as u8); code.push(0x00);
code.extend_from_slice(&7u32.to_le_bytes());
code.push(OpCode::CallClosure as u8); code.push(0x00);
code.extend_from_slice(&0u32.to_le_bytes());
code.push(OpCode::Ret as u8); code.push(0x00);
let functions = vec![FunctionMeta { code_offset: 0, code_len: code.len() as u32, return_slots: 0, ..Default::default() }];
let res = Verifier::verify(&code, &functions);
assert!(matches!(res, Err(VerifierError::NotAClosureOnCallClosure { .. })));
}
#[test]
fn nested_closure_calls_verify() {
// F0: MakeClosure(fn=1,0); CallClosure argc=0; PopN 1; Ret
// F1: MakeClosure(fn=2,0); CallClosure argc=0; Ret (param=1 hidden, ret=1)
// F2: PushI32 5; Ret (param=1 hidden, ret=1)
let mut code = Vec::new();
// F0 @ 0
code.push(OpCode::MakeClosure as u8); code.push(0x00);
code.extend_from_slice(&1u32.to_le_bytes()); // F1
code.extend_from_slice(&0u32.to_le_bytes()); // cap=0
code.push(OpCode::CallClosure as u8); code.push(0x00);
code.extend_from_slice(&0u32.to_le_bytes()); // argc=0
code.push(OpCode::PopN as u8); code.push(0x00);
code.extend_from_slice(&1u32.to_le_bytes());
code.push(OpCode::Ret as u8); code.push(0x00);
let f0_len = code.len() as u32;
// F1 @ f0_len
code.push(OpCode::MakeClosure as u8); code.push(0x00);
code.extend_from_slice(&2u32.to_le_bytes()); // F2
code.extend_from_slice(&0u32.to_le_bytes()); // cap=0
code.push(OpCode::CallClosure as u8); code.push(0x00);
code.extend_from_slice(&0u32.to_le_bytes()); // argc=0
code.push(OpCode::Ret as u8); code.push(0x00);
let f1_len = (code.len() as u32) - f0_len;
// F2 @ f0_len + f1_len
code.push(OpCode::PushI32 as u8); code.push(0x00);
code.extend_from_slice(&5u32.to_le_bytes());
code.push(OpCode::Ret as u8); code.push(0x00);
let f2_len = (code.len() as u32) - f0_len - f1_len;
let functions = vec![
FunctionMeta { code_offset: 0, code_len: f0_len, return_slots: 0, ..Default::default() },
FunctionMeta { code_offset: f0_len, code_len: f1_len, param_slots: 1, return_slots: 1, ..Default::default() },
FunctionMeta { code_offset: f0_len + f1_len, code_len: f2_len, param_slots: 1, return_slots: 1, ..Default::default() },
];
let res = Verifier::verify(&code, &functions).unwrap();
assert!(res[0] >= 1);
}
}