use crate::bytecode::*;
use crate::executor::{VMConfig, VirtualMachine};
use crate::type_tracking::{FrameDescriptor, NativeKind};
use shape_value::FunctionId;
use shape_value::VMError;
fn execute_program(program: BytecodeProgram) -> Result<u64, VMError> {
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
vm.execute_raw(None)
}
fn execute_program_typed(
mut program: BytecodeProgram,
return_kind: NativeKind,
) -> Result<u64, VMError> {
let mut frame = program.top_level_frame.unwrap_or_else(FrameDescriptor::new);
frame.return_kind = Some(return_kind);
program.top_level_frame = Some(frame);
execute_program(program)
}
#[inline]
fn bits_as_f64(bits: u64) -> f64 {
f64::from_bits(bits)
}
#[inline]
fn bits_as_i64(bits: u64) -> i64 {
bits as i64
}
#[inline]
fn bits_as_bool(bits: u64) -> bool {
bits != 0
}
#[test]
fn v2_stack_raw_f64_push_pop_roundtrip() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
],
constants: vec![Constant::Number(3.14)],
..Default::default()
};
let bits = execute_program(program).unwrap();
assert_eq!(bits, 3.14f64.to_bits(), "f64 bits must match exactly");
}
#[test]
fn v2_stack_raw_f64_special_values() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
],
constants: vec![Constant::Number(f64::INFINITY)],
..Default::default()
};
let bits = execute_program(program).unwrap();
assert_eq!(bits_as_f64(bits), f64::INFINITY);
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
],
constants: vec![Constant::Number(-0.0f64)],
..Default::default()
};
let bits = execute_program(program).unwrap();
let f = bits_as_f64(bits);
assert!(f.is_sign_negative() && f == 0.0, "negative zero must be preserved");
}
#[test]
fn v2_stack_raw_i64_push_pop_roundtrip() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
],
constants: vec![Constant::Int(-42)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Int64).unwrap();
assert_eq!(bits_as_i64(bits), -42, "i64 value must round-trip exactly");
}
#[test]
fn v2_stack_raw_i64_zero() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
],
constants: vec![Constant::Int(0)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Int64).unwrap();
assert_eq!(bits_as_i64(bits), 0);
}
#[test]
fn v2_stack_raw_i64_large_positive() {
let val = (1i64 << 47) - 1;
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
],
constants: vec![Constant::Int(val)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Int64).unwrap();
assert_eq!(bits_as_i64(bits), val);
}
#[test]
fn v2_stack_raw_i32_push_pop_roundtrip() {
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(BytecodeProgram::default());
let val: i32 = 12345;
vm.push_kinded(val as u32 as u64, NativeKind::Int32).unwrap();
let (bits, kind) = vm.pop_kinded().unwrap();
assert_eq!(kind, NativeKind::Int32, "kind must be Int32");
assert_eq!(bits as u32 as i32, val, "i32 value must round-trip exactly");
}
#[test]
fn v2_stack_raw_i32_negative() {
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(BytecodeProgram::default());
let val: i32 = -42;
vm.push_kinded(val as u32 as u64, NativeKind::Int32).unwrap();
let (bits, kind) = vm.pop_kinded().unwrap();
assert_eq!(kind, NativeKind::Int32);
assert_eq!(bits as u32 as i32, val, "negative i32 round-trip");
}
#[test]
fn v2_stack_raw_bool_push_pop_true() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
],
constants: vec![Constant::Bool(true)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Bool).unwrap();
assert!(bits_as_bool(bits));
}
#[test]
fn v2_stack_raw_bool_push_pop_false() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
],
constants: vec![Constant::Bool(false)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Bool).unwrap();
assert!(!bits_as_bool(bits));
}
#[test]
fn v2_stack_raw_pointer_push_pop() {
use shape_value::HeapKind;
use shape_value::heap_value::HeapValue;
use std::sync::Arc;
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(BytecodeProgram::default());
let arc = Arc::new(HeapValue::String(Arc::new("hello".to_string())));
let bits_in = Arc::into_raw(arc) as u64;
vm.push_kinded(bits_in, NativeKind::Ptr(HeapKind::String))
.unwrap();
let (bits, kind) = vm.pop_kinded().unwrap();
assert_eq!(kind, NativeKind::Ptr(HeapKind::String));
assert_eq!(bits, bits_in, "raw pointer bits must round-trip");
unsafe {
let _ = Arc::from_raw(bits as *const HeapValue);
}
}
#[test]
fn v2_stack_raw_pointer_null() {
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(BytecodeProgram::default());
vm.push_kinded(0u64, NativeKind::Null).unwrap();
let (bits, kind) = vm.pop_kinded().unwrap();
assert_eq!(kind, NativeKind::Null);
assert_eq!(bits, 0);
}
#[test]
fn v2_stack_mixed_types_push_pop_order() {
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(BytecodeProgram::default());
vm.push_kinded(2.718f64.to_bits(), NativeKind::Float64).unwrap();
vm.push_kinded(42i64 as u64, NativeKind::Int64).unwrap();
vm.push_kinded(1u64, NativeKind::Bool).unwrap();
let (b_bits, b_kind) = vm.pop_kinded().unwrap();
let (i_bits, i_kind) = vm.pop_kinded().unwrap();
let (f_bits, f_kind) = vm.pop_kinded().unwrap();
assert_eq!(b_kind, NativeKind::Bool, "first pop must be Bool");
assert!(bits_as_bool(b_bits), "bool must be popped first");
assert_eq!(i_kind, NativeKind::Int64, "second pop must be Int64");
assert_eq!(bits_as_i64(i_bits), 42, "i64 must be popped second");
assert_eq!(f_kind, NativeKind::Float64, "third pop must be Float64");
assert_eq!(
f_bits,
2.718f64.to_bits(),
"f64 must be popped third"
);
}
#[test]
fn v2_stack_mixed_types_with_native_scalars() {
use shape_value::HeapKind;
use shape_value::heap_value::HeapValue;
use std::sync::Arc;
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(BytecodeProgram::default());
let arc = Arc::new(HeapValue::String(Arc::new("hi".to_string())));
let ptr_bits = Arc::into_raw(arc) as u64;
vm.push_kinded(1.5f64.to_bits(), NativeKind::Float64).unwrap();
vm.push_kinded(99i64 as u64, NativeKind::Int64).unwrap();
vm.push_kinded(1u64, NativeKind::Bool).unwrap();
vm.push_kinded((-7i32) as u32 as u64, NativeKind::Int32).unwrap();
vm.push_kinded(ptr_bits, NativeKind::Ptr(HeapKind::String))
.unwrap();
let (top_bits, top_kind) = vm.pop_kinded().unwrap();
assert_eq!(top_kind, NativeKind::Ptr(HeapKind::String));
assert_eq!(top_bits, ptr_bits);
unsafe {
let _ = Arc::from_raw(top_bits as *const HeapValue);
}
let (i32_bits, i32_kind) = vm.pop_kinded().unwrap();
assert_eq!(i32_kind, NativeKind::Int32);
assert_eq!(i32_bits as u32 as i32, -7);
let (b_bits, b_kind) = vm.pop_kinded().unwrap();
assert_eq!(b_kind, NativeKind::Bool);
assert!(bits_as_bool(b_bits));
let (i_bits, i_kind) = vm.pop_kinded().unwrap();
assert_eq!(i_kind, NativeKind::Int64);
assert_eq!(bits_as_i64(i_bits), 99);
let (f_bits, f_kind) = vm.pop_kinded().unwrap();
assert_eq!(f_kind, NativeKind::Float64);
assert_eq!(bits_as_f64(f_bits), 1.5);
}
#[test]
fn v2_stack_typed_add_int() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::AddInt),
],
constants: vec![Constant::Int(10), Constant::Int(20)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Int64).unwrap();
assert_eq!(bits_as_i64(bits), 30, "AddInt must produce raw i64 result");
}
#[test]
fn v2_stack_typed_sub_int() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::SubInt),
],
constants: vec![Constant::Int(100), Constant::Int(37)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Int64).unwrap();
assert_eq!(bits_as_i64(bits), 63);
}
#[test]
fn v2_stack_typed_mul_int() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::MulInt),
],
constants: vec![Constant::Int(6), Constant::Int(7)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Int64).unwrap();
assert_eq!(bits_as_i64(bits), 42);
}
#[test]
fn v2_stack_typed_add_number() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::AddNumber),
],
constants: vec![Constant::Number(1.5), Constant::Number(2.5)],
..Default::default()
};
let bits = execute_program(program).unwrap();
assert_eq!(bits_as_f64(bits), 4.0);
}
#[test]
fn v2_stack_typed_mul_number() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::MulNumber),
],
constants: vec![Constant::Number(3.0), Constant::Number(4.0)],
..Default::default()
};
let bits = execute_program(program).unwrap();
assert_eq!(bits_as_f64(bits), 12.0);
}
#[test]
fn v2_stack_typed_gt_number_true() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::GtNumber),
],
constants: vec![Constant::Number(5.0), Constant::Number(3.0)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Bool).unwrap();
assert!(bits_as_bool(bits), "GtNumber 5.0>3.0 must produce raw bool true");
}
#[test]
fn v2_stack_typed_gt_number_false() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::GtNumber),
],
constants: vec![Constant::Number(2.0), Constant::Number(3.0)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Bool).unwrap();
assert!(!bits_as_bool(bits));
}
#[test]
fn v2_stack_typed_gt_int() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::GtInt),
],
constants: vec![Constant::Int(10), Constant::Int(5)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Bool).unwrap();
assert!(bits_as_bool(bits));
}
#[test]
fn v2_stack_typed_lt_int() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::LtInt),
],
constants: vec![Constant::Int(3), Constant::Int(7)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Bool).unwrap();
assert!(bits_as_bool(bits));
}
#[test]
fn v2_stack_typed_eq_int() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::EqInt),
],
constants: vec![Constant::Int(42), Constant::Int(42)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Bool).unwrap();
assert!(bits_as_bool(bits));
}
#[test]
fn v2_stack_typed_field_load_f64() {
use crate::executor::typed_object_ops::FIELD_TAG_F64;
let mut program = BytecodeProgram::default();
let schema_id = program.type_schema_registry.register_type(
"__v2_test_point_x",
vec![("x".to_string(), shape_runtime::type_schema::FieldType::F64)],
);
let schema_u16 = u16::try_from(schema_id).expect("schema id fits u16");
program.instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::NewTypedObject,
Some(Operand::TypedObjectAlloc {
schema_id: schema_u16,
field_count: 1,
}),
),
Instruction::new(
OpCode::GetFieldTyped,
Some(Operand::TypedField {
type_id: schema_u16,
field_idx: 0,
field_type_tag: FIELD_TAG_F64,
}),
),
];
program.constants = vec![Constant::Number(99.99)];
let bits = execute_program(program).unwrap();
assert_eq!(
bits_as_f64(bits),
99.99,
"GetFieldTyped must extract f64 field value"
);
}
#[test]
fn v2_stack_typed_field_load_i64() {
use crate::executor::typed_object_ops::FIELD_TAG_I64;
let mut program = BytecodeProgram::default();
let schema_id = program.type_schema_registry.register_type(
"__v2_test_counter",
vec![("count".to_string(), shape_runtime::type_schema::FieldType::I64)],
);
let schema_u16 = u16::try_from(schema_id).expect("schema id fits u16");
program.instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::NewTypedObject,
Some(Operand::TypedObjectAlloc {
schema_id: schema_u16,
field_count: 1,
}),
),
Instruction::new(
OpCode::GetFieldTyped,
Some(Operand::TypedField {
type_id: schema_u16,
field_idx: 0,
field_type_tag: FIELD_TAG_I64,
}),
),
];
program.constants = vec![Constant::Int(12345)];
let bits = execute_program_typed(program, NativeKind::Int64).unwrap();
assert_eq!(bits_as_i64(bits), 12345);
}
#[test]
fn v2_stack_typed_field_load_bool() {
use crate::executor::typed_object_ops::FIELD_TAG_BOOL;
let mut program = BytecodeProgram::default();
let schema_id = program.type_schema_registry.register_type(
"__v2_test_flag",
vec![("active".to_string(), shape_runtime::type_schema::FieldType::Bool)],
);
let schema_u16 = u16::try_from(schema_id).expect("schema id fits u16");
program.instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::NewTypedObject,
Some(Operand::TypedObjectAlloc {
schema_id: schema_u16,
field_count: 1,
}),
),
Instruction::new(
OpCode::GetFieldTyped,
Some(Operand::TypedField {
type_id: schema_u16,
field_idx: 0,
field_type_tag: FIELD_TAG_BOOL,
}),
),
];
program.constants = vec![Constant::Bool(true)];
let bits = execute_program_typed(program, NativeKind::Bool).unwrap();
assert!(bits_as_bool(bits));
}
#[test]
fn v2_stack_typed_field_load_multi_field() {
use crate::executor::typed_object_ops::{FIELD_TAG_F64, FIELD_TAG_I64};
let mut program = BytecodeProgram::default();
let schema_id = program.type_schema_registry.register_type(
"__v2_test_point2d",
vec![
("x".to_string(), shape_runtime::type_schema::FieldType::F64),
("y".to_string(), shape_runtime::type_schema::FieldType::I64),
],
);
let schema_u16 = u16::try_from(schema_id).expect("schema id fits u16");
program.instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))), Instruction::new(OpCode::PushConst, Some(Operand::Const(1))), Instruction::new(
OpCode::NewTypedObject,
Some(Operand::TypedObjectAlloc {
schema_id: schema_u16,
field_count: 2,
}),
),
Instruction::simple(OpCode::Dup),
Instruction::new(
OpCode::GetFieldTyped,
Some(Operand::TypedField {
type_id: schema_u16,
field_idx: 0,
field_type_tag: FIELD_TAG_F64,
}),
),
Instruction::simple(OpCode::Swap),
Instruction::new(
OpCode::GetFieldTyped,
Some(Operand::TypedField {
type_id: schema_u16,
field_idx: 1,
field_type_tag: FIELD_TAG_I64,
}),
),
Instruction::simple(OpCode::IntToNumber),
Instruction::simple(OpCode::AddNumber),
];
program.constants = vec![Constant::Number(3.14), Constant::Int(7)];
let bits = execute_program(program).unwrap();
let got = bits_as_f64(bits);
let expected = 3.14 + 7.0;
assert!(
(got - expected).abs() < 1e-10,
"expected {}, got {}",
expected,
got,
);
}
#[test]
fn v2_stack_int_to_number_conversion() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::simple(OpCode::IntToNumber),
],
constants: vec![Constant::Int(42)],
..Default::default()
};
let bits = execute_program(program).unwrap();
assert_eq!(bits_as_f64(bits), 42.0, "IntToNumber must convert i64 to f64");
}
#[test]
fn v2_stack_int_to_number_negative() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::simple(OpCode::IntToNumber),
],
constants: vec![Constant::Int(-1000)],
..Default::default()
};
let bits = execute_program(program).unwrap();
assert_eq!(bits_as_f64(bits), -1000.0);
}
#[test]
fn v2_stack_number_to_int_conversion() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::simple(OpCode::NumberToInt),
],
constants: vec![Constant::Number(7.9)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Int64).unwrap();
assert_eq!(bits_as_i64(bits), 7, "NumberToInt must truncate 7.9 to 7");
}
#[test]
fn v2_stack_large_push_pop_1000() {
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(BytecodeProgram::default());
let count = 1000usize;
for i in 0..count {
let f = i as f64 * 1.1;
vm.push_kinded(f.to_bits(), NativeKind::Float64).unwrap();
}
for i in (0..count).rev() {
let (bits, kind) = vm.pop_kinded().unwrap();
assert_eq!(kind, NativeKind::Float64, "slot {} kind mismatch", i);
let expected = i as f64 * 1.1;
assert_eq!(
bits,
expected.to_bits(),
"stack slot {} mismatch: expected {}, got {}",
i,
expected,
bits_as_f64(bits),
);
}
}
#[test]
fn v2_stack_large_mixed_types_500() {
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(BytecodeProgram::default());
for i in 0..500usize {
if i % 2 == 0 {
vm.push_kinded((i as f64).to_bits(), NativeKind::Float64).unwrap();
} else {
vm.push_kinded(i as u64, NativeKind::Int64).unwrap();
}
}
for i in (0..500usize).rev() {
let (bits, kind) = vm.pop_kinded().unwrap();
if i % 2 == 0 {
assert_eq!(kind, NativeKind::Float64, "slot {} expected Float64", i);
assert_eq!(bits_as_f64(bits), i as f64, "f64 mismatch at index {}", i);
} else {
assert_eq!(kind, NativeKind::Int64, "slot {} expected Int64", i);
assert_eq!(bits_as_i64(bits), i as i64, "i64 mismatch at index {}", i);
}
}
}
#[test]
fn v2_stack_frame_with_typed_locals() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(3))), Instruction::new(OpCode::Call, Some(Operand::Function(FunctionId(0)))), Instruction::new(OpCode::Jump, Some(Operand::Offset(11))), Instruction::new(OpCode::PushConst, Some(Operand::Const(0))), Instruction::new(OpCode::StoreLocal, Some(Operand::Local(0))), Instruction::new(OpCode::PushConst, Some(Operand::Const(1))), Instruction::new(OpCode::StoreLocal, Some(Operand::Local(1))), Instruction::new(OpCode::PushConst, Some(Operand::Const(2))), Instruction::new(OpCode::StoreLocal, Some(Operand::Local(2))), Instruction::new(OpCode::LoadLocal, Some(Operand::Local(0))), Instruction::new(OpCode::LoadLocal, Some(Operand::Local(1))), Instruction::simple(OpCode::IntToNumber), Instruction::simple(OpCode::AddNumber), Instruction::simple(OpCode::ReturnValue), ],
constants: vec![
Constant::Number(3.14), Constant::Int(42), Constant::Bool(true), Constant::Number(0.0), ],
functions: vec![Function {
name: "__v2_test_locals".to_string(),
arity: 0,
param_names: vec![],
locals_count: 3,
entry_point: 3,
body_length: 11,
is_closure: false,
captures_count: 0,
is_async: false,
ref_params: vec![],
ref_mutates: vec![],
mutable_captures: vec![],
frame_descriptor: None,
osr_entry_points: vec![],
mir_data: None,
}],
..Default::default()
};
let bits = execute_program(program).unwrap();
let got = bits_as_f64(bits);
let expected = 3.14 + 42.0;
assert!(
(got - expected).abs() < 1e-10,
"function with typed locals: expected {}, got {}",
expected,
got,
);
}
#[test]
fn v2_stack_frame_locals_isolation() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))), Instruction::new(OpCode::PushConst, Some(Operand::Const(3))), Instruction::new(OpCode::Call, Some(Operand::Function(FunctionId(0)))), Instruction::simple(OpCode::Pop), Instruction::new(OpCode::Jump, Some(Operand::Offset(6))), Instruction::new(OpCode::PushConst, Some(Operand::Const(1))), Instruction::new(OpCode::StoreLocal, Some(Operand::Local(0))), Instruction::new(OpCode::PushConst, Some(Operand::Const(2))), Instruction::new(OpCode::StoreLocal, Some(Operand::Local(1))), Instruction::new(OpCode::LoadLocal, Some(Operand::Local(0))), Instruction::simple(OpCode::ReturnValue), ],
constants: vec![
Constant::Int(999), Constant::Int(77), Constant::Int(-1), Constant::Number(0.0), ],
functions: vec![Function {
name: "__v2_test_isolation".to_string(),
arity: 0,
param_names: vec![],
locals_count: 2,
entry_point: 5,
body_length: 6,
is_closure: false,
captures_count: 0,
is_async: false,
ref_params: vec![],
ref_mutates: vec![],
mutable_captures: vec![],
frame_descriptor: None,
osr_entry_points: vec![],
mir_data: None,
}],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Int64).unwrap();
assert_eq!(
bits_as_i64(bits),
999,
"sentinel value must survive function call frame"
);
}
#[test]
fn v2_stack_underflow_on_empty() {
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(BytecodeProgram::default());
let err = vm.pop_kinded().unwrap_err();
assert!(
matches!(err, VMError::StackUnderflow),
"pop on empty stack must return StackUnderflow, got {:?}",
err
);
}
#[test]
fn v2_stack_dup_preserves_type() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::simple(OpCode::Dup),
Instruction::simple(OpCode::AddInt),
],
constants: vec![Constant::Int(42)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Int64).unwrap();
assert_eq!(bits_as_i64(bits), 84, "Dup + AddInt: 42 + 42 = 84");
}
#[test]
fn v2_stack_swap_preserves_types() {
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(BytecodeProgram::default());
vm.push_kinded(10i64 as u64, NativeKind::Int64).unwrap();
vm.push_kinded(2.5f64.to_bits(), NativeKind::Float64).unwrap();
let (b_bits, b_kind) = vm.pop_kinded().unwrap(); let (a_bits, a_kind) = vm.pop_kinded().unwrap(); vm.push_kinded(b_bits, b_kind).unwrap();
vm.push_kinded(a_bits, a_kind).unwrap();
let (top_bits, top_kind) = vm.pop_kinded().unwrap();
assert_eq!(top_kind, NativeKind::Int64);
assert_eq!(bits_as_i64(top_bits), 10);
let (bot_bits, bot_kind) = vm.pop_kinded().unwrap();
assert_eq!(bot_kind, NativeKind::Float64);
assert_eq!(bits_as_f64(bot_bits), 2.5);
}
#[test]
fn v2_stack_chained_typed_arithmetic() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))), Instruction::new(OpCode::PushConst, Some(Operand::Const(1))), Instruction::simple(OpCode::AddInt), Instruction::new(OpCode::PushConst, Some(Operand::Const(2))), Instruction::simple(OpCode::MulInt), ],
constants: vec![Constant::Int(10), Constant::Int(20), Constant::Int(3)],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Int64).unwrap();
assert_eq!(bits_as_i64(bits), 90);
}
#[test]
fn v2_stack_typed_comparison_chain() {
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))), Instruction::new(OpCode::PushConst, Some(Operand::Const(1))), Instruction::simple(OpCode::GtNumber),
Instruction::new(OpCode::PushConst, Some(Operand::Const(2))), Instruction::new(OpCode::PushConst, Some(Operand::Const(3))), Instruction::simple(OpCode::LtInt),
Instruction::simple(OpCode::And),
],
constants: vec![
Constant::Number(5.0),
Constant::Number(3.0),
Constant::Int(1),
Constant::Int(2),
],
..Default::default()
};
let bits = execute_program_typed(program, NativeKind::Bool).unwrap();
assert!(bits_as_bool(bits), "chain: (5>3) AND (1<2) must be true");
}