#[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); } }