use crate::bytecode::OpCode;
use crate::executor::tests::test_utils::{compile, eval};
#[test]
fn test_auto_drop_at_scope_exit() {
let bytecode = compile(
r#"
function test_fn() {
{
let x = 42
}
return 1
}
test_fn()
"#,
);
let has_drop = bytecode
.instructions
.iter()
.any(|i| i.opcode == OpCode::DropCall);
assert!(
has_drop,
"block with let binding should emit DropCall at scope exit"
);
let result = eval(
r#"
function test_fn() {
{
let x = 42
}
return 1
}
test_fn()
"#,
);
assert_eq!(result.as_i64(), Some(1));
}
#[test]
fn test_auto_drop_reverse_order() {
let bytecode = compile(
r#"
function test_fn() {
let a = 1
let b = 2
let c = 3
return a + b + c
}
test_fn()
"#,
);
let drop_count = bytecode
.instructions
.iter()
.filter(|i| i.opcode == OpCode::DropCall)
.count();
assert!(
drop_count >= 3,
"3 let bindings should emit at least 3 DropCall instructions, got {}",
drop_count
);
}
#[test]
fn test_auto_drop_on_early_return() {
let bytecode = compile(
r#"
function test_fn() {
let x = 10
if true {
return x
}
return 0
}
test_fn()
"#,
);
let has_drop = bytecode
.instructions
.iter()
.any(|i| i.opcode == OpCode::DropCall);
assert!(has_drop, "early return should emit DropCall");
let result = crate::test_utils::eval_typed_i64(
r#"
function test_fn() {
let x = 10
if true {
return x
}
return 0
}
test_fn()
"#,
);
assert_eq!(result, 10);
}
#[test]
fn test_auto_drop_nested_scopes() {
let bytecode = compile(
r#"
function test_fn() {
let outer = 1
{
let inner = 2
}
return outer
}
test_fn()
"#,
);
let drop_count = bytecode
.instructions
.iter()
.filter(|i| i.opcode == OpCode::DropCall)
.count();
assert!(
drop_count >= 2,
"nested scopes should emit at least 2 DropCall instructions, got {}",
drop_count
);
let result = eval(
r#"
function test_fn() {
let outer = 1
{
let inner = 2
}
return outer
}
test_fn()
"#,
);
assert_eq!(result.as_i64(), Some(1));
}
#[test]
fn test_auto_drop_error_does_not_propagate() {
let result = eval(
r#"
function test_fn() {
{
let x = 42
}
return 99
}
test_fn()
"#,
);
assert_eq!(result.as_i64(), Some(99));
}
#[test]
fn test_async_drop_in_async_scope() {
let bytecode = compile(
r#"
type Conn { id: int }
impl Drop for Conn {
async method drop() { }
}
async function test_fn() {
let c: Conn = Conn { id: 1 }
return c.id
}
"#,
);
let has_async_drop = bytecode
.instructions
.iter()
.any(|i| i.opcode == OpCode::DropCallAsync);
assert!(
has_async_drop,
"async function with async-drop type should emit DropCallAsync"
);
}
#[test]
fn test_sync_function_uses_sync_drop() {
let bytecode = compile(
r#"
function test_fn() {
let x = 42
return x
}
test_fn()
"#,
);
let has_sync_drop = bytecode
.instructions
.iter()
.any(|i| i.opcode == OpCode::DropCall);
let has_async_drop = bytecode
.instructions
.iter()
.any(|i| i.opcode == OpCode::DropCallAsync);
assert!(has_sync_drop, "sync function should emit DropCall");
assert!(
!has_async_drop,
"sync function should NOT emit DropCallAsync"
);
}
#[test]
fn test_drop_top_level_block_self_field_access() {
use crate::VMConfig;
use crate::executor::VirtualMachine;
let src = r#"
type FileHandle { path: string }
impl Drop for FileHandle {
method drop() {
print(f"Closed file: {self.path}")
}
}
{
let f = FileHandle { path: "/tmp/a.txt" }
print(f"opened {f.path}")
}
print("after block")
"#;
let bc = crate::executor::tests::test_utils::compile(src);
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(bc);
let result = vm.execute(None);
assert!(
result.is_ok(),
"Drop body should not surface; got {:?}",
result.err()
);
}
#[test]
fn test_drop_fn_main_reverse_order() {
use crate::VMConfig;
use crate::executor::VirtualMachine;
let src = r#"
type R { name: string }
impl Drop for R {
method drop() { print(f"drop: {self.name}") }
}
fn main() {
let a = R { name: "a" }
let b = R { name: "b" }
let c = R { name: "c" }
print("body running")
}
main()
print("done")
"#;
let bc = crate::executor::tests::test_utils::compile(src);
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(bc);
let result = vm.execute(None);
assert!(
result.is_ok(),
"Drop in fn main should not surface; got {:?}",
result.err()
);
}