use super::*;
use crate::bytecode::*;
use shape_value::VMError;
pub(crate) mod test_utils;
mod auto_drop;
mod channel_ops;
mod decimal_ops;
mod deque_ops;
mod io_integration;
mod jit_abi_tests;
mod matrix_ops;
mod mutation_writeback;
mod pop_mutation;
mod priority_queue_ops;
mod set_ops;
mod soak_tests;
mod table_iteration;
mod try_operator;
mod type_system_integration;
mod typed_array_ops;
mod v2_opcode_tests;
mod v2_struct_integration;
#[cfg(feature = "deep-tests")]
mod differential_trusted;
#[cfg(feature = "deep-tests")]
mod drop_deep_tests;
#[cfg(feature = "deep-tests")]
mod extend_blocks;
#[cfg(feature = "deep-tests")]
mod hashmap_ops;
#[cfg(feature = "deep-tests")]
mod iterator_ops;
#[cfg(feature = "deep-tests")]
mod module_deep_tests;
#[cfg(feature = "deep-tests")]
mod operator_overload;
#[cfg(feature = "deep-tests")]
mod trusted_edge_cases;
mod trait_object_thunks;
#[allow(dead_code)]
fn execute_bytecode(
instructions: Vec<Instruction>,
constants: Vec<Constant>,
) -> Result<u64, VMError> {
let program = BytecodeProgram {
instructions,
constants,
..Default::default()
};
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
vm.execute_raw(None)
}
#[allow(dead_code)]
fn execute_bytecode_typed(
instructions: Vec<Instruction>,
constants: Vec<Constant>,
return_kind: crate::type_tracking::NativeKind,
) -> Result<u64, VMError> {
use crate::type_tracking::FrameDescriptor;
let mut frame = FrameDescriptor::new();
frame.return_kind = Some(return_kind);
let program = BytecodeProgram {
instructions,
constants,
top_level_frame: Some(frame),
..Default::default()
};
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
vm.execute_raw(None)
}
#[test]
fn test_basic_arithmetic() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))), Instruction::new(OpCode::PushConst, Some(Operand::Const(1))), Instruction::simple(OpCode::AddNumber), ];
let constants = vec![Constant::Number(2.0), Constant::Number(3.0)];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(f64::from_bits(result), 5.0);
}
#[test]
fn test_subtraction() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::SubNumber),
];
let constants = vec![Constant::Number(10.0), Constant::Number(4.0)];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(f64::from_bits(result), 6.0);
}
#[test]
fn test_multiplication() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::MulNumber),
];
let constants = vec![Constant::Number(3.0), Constant::Number(4.0)];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(f64::from_bits(result), 12.0);
}
#[test]
fn test_division() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::DivNumber),
];
let constants = vec![Constant::Number(15.0), Constant::Number(3.0)];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(f64::from_bits(result), 5.0);
}
#[test]
fn test_integer_add_overflow_wraps_two_complement() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::AddInt),
];
let constants = vec![Constant::Int(i64::MAX), Constant::Int(1)];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result as i64, i64::MIN);
}
#[test]
fn test_integer_mul_overflow_wraps_two_complement() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::MulInt),
];
let constants = vec![Constant::Int(3_037_000_500), Constant::Int(3_037_000_500)];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result as i64, 3_037_000_500i64.wrapping_mul(3_037_000_500));
assert_eq!(result as i64, -9_223_372_036_709_301_616);
}
#[test]
fn test_integer_sub_overflow_wraps_two_complement() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::SubInt),
];
let constants = vec![Constant::Int(i64::MIN), Constant::Int(1)];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result as i64, i64::MAX);
}
#[test]
fn test_integer_arithmetic_no_overflow_stays_int() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::AddInt),
];
let constants = vec![Constant::Int(100), Constant::Int(200)];
let result = execute_bytecode_typed(
instructions,
constants,
crate::type_tracking::NativeKind::Int64,
)
.unwrap();
assert_eq!(Some(result as i64), Some(300));
}
#[test]
fn test_comparisons() {
let bool_kind = crate::type_tracking::NativeKind::Bool;
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::GtNumber),
];
let constants = vec![Constant::Number(5.0), Constant::Number(3.0)];
let result = execute_bytecode_typed(instructions, constants, bool_kind).unwrap();
assert_eq!(Some(result != 0), Some(true));
let instructions2 = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::simple(OpCode::GtNumber),
];
let constants2 = vec![Constant::Number(5.0), Constant::Number(3.0)];
let result2 = execute_bytecode_typed(instructions2, constants2, bool_kind).unwrap();
assert_eq!(Some(result2 != 0), Some(false));
}
#[test]
fn test_logical_and() {
let bool_kind = crate::type_tracking::NativeKind::Bool;
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::simple(OpCode::And),
];
let constants = vec![Constant::Bool(true)];
let result = execute_bytecode_typed(instructions, constants, bool_kind).unwrap();
assert_eq!(Some(result != 0), Some(true));
let instructions2 = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::And),
];
let constants2 = vec![Constant::Bool(true), Constant::Bool(false)];
let result2 = execute_bytecode_typed(instructions2, constants2, bool_kind).unwrap();
assert_eq!(Some(result2 != 0), Some(false));
}
#[test]
fn test_local_variables() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))), Instruction::new(OpCode::StoreLocal, Some(Operand::Local(0))), Instruction::new(OpCode::LoadLocal, Some(Operand::Local(0))), ];
let constants = vec![Constant::Number(10.0)];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(f64::from_bits(result), 10.0);
}
#[test]
fn test_arrays() {
use test_utils::eval;
let v = eval("[1, 2, 3].len()");
assert_eq!(v.as_i64(), Some(3));
}
#[test]
fn test_array_indexing() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(2))),
Instruction::new(OpCode::NewArray, Some(Operand::Count(3))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(3))), Instruction::simple(OpCode::GetProp),
];
let constants = vec![
Constant::Number(10.0),
Constant::Number(20.0),
Constant::Number(30.0),
Constant::Int(1),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(f64::from_bits(result), 20.0);
}
#[test]
fn test_stack_operations() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::simple(OpCode::Dup),
Instruction::simple(OpCode::AddNumber),
];
let constants = vec![Constant::Number(5.0)];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(f64::from_bits(result), 10.0);
}
#[test]
fn test_null_value() {
let instructions = vec![Instruction::simple(OpCode::PushNull)];
let constants = vec![];
let result = execute_bytecode(instructions, constants).unwrap();
assert!((result == 0));
}
#[test]
fn test_while_loop_simple() {
use crate::bytecode::*;
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::StoreModuleBinding, Some(Operand::ModuleBinding(0))),
Instruction::new(OpCode::LoadModuleBinding, Some(Operand::ModuleBinding(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::LtNumber),
Instruction::new(OpCode::JumpIfFalse, Some(Operand::Offset(5))), Instruction::new(OpCode::LoadModuleBinding, Some(Operand::ModuleBinding(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(2))),
Instruction::simple(OpCode::AddNumber),
Instruction::new(OpCode::StoreModuleBinding, Some(Operand::ModuleBinding(0))),
Instruction::new(OpCode::Jump, Some(Operand::Offset(-9))),
Instruction::new(OpCode::LoadModuleBinding, Some(Operand::ModuleBinding(0))),
];
let constants = vec![
Constant::Number(0.0), Constant::Number(3.0), Constant::Number(1.0), ];
let program = BytecodeProgram {
instructions,
constants,
..Default::default()
};
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None).unwrap().clone();
assert_eq!(
result.slot().as_f64(),
3.0,
"Loop should increment from 0 to 3"
);
}
#[test]
fn test_conditional_jump() {
use crate::bytecode::*;
let 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::JumpIfFalse, Some(Operand::Offset(2))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(2))),
Instruction::new(OpCode::Jump, Some(Operand::Offset(1))), Instruction::new(OpCode::PushConst, Some(Operand::Const(3))),
];
let constants = vec![
Constant::Number(5.0),
Constant::Number(3.0),
Constant::Number(10.0), Constant::Number(20.0), ];
let program = BytecodeProgram {
instructions,
constants,
..Default::default()
};
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None).unwrap();
assert_eq!(
result.clone().as_f64().unwrap(),
10.0,
"Should take then branch since 5 > 3"
);
}
#[test]
fn test_comparison_operators_complete() {
use crate::bytecode::*;
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::simple(OpCode::GteNumber),
],
constants: vec![Constant::Number(5.0)],
top_level_frame: Some({
let mut f = crate::type_tracking::FrameDescriptor::new();
f.return_kind = Some(crate::type_tracking::NativeKind::Bool);
f
}),
..Default::default()
});
assert_eq!(
vm.execute(None).unwrap().clone().as_bool(),
Some(true),
"5 >= 5"
);
vm.load_program(BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::LteNumber),
],
constants: vec![Constant::Number(3.0), Constant::Number(5.0)],
top_level_frame: Some({
let mut f = crate::type_tracking::FrameDescriptor::new();
f.return_kind = Some(crate::type_tracking::NativeKind::Bool);
f
}),
..Default::default()
});
assert_eq!(
vm.execute(None).unwrap().clone().as_bool(),
Some(true),
"3 <= 5"
);
vm.load_program(BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::simple(OpCode::EqNumber),
],
constants: vec![Constant::Number(7.0)],
top_level_frame: Some({
let mut f = crate::type_tracking::FrameDescriptor::new();
f.return_kind = Some(crate::type_tracking::NativeKind::Bool);
f
}),
..Default::default()
});
assert_eq!(
vm.execute(None).unwrap().clone().as_bool(),
Some(true),
"7 == 7"
);
vm.load_program(BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::NeqNumber),
],
constants: vec![Constant::Number(5.0), Constant::Number(3.0)],
top_level_frame: Some({
let mut f = crate::type_tracking::FrameDescriptor::new();
f.return_kind = Some(crate::type_tracking::NativeKind::Bool);
f
}),
..Default::default()
});
assert_eq!(
vm.execute(None).unwrap().clone().as_bool(),
Some(true),
"5 != 3"
);
vm.load_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)],
top_level_frame: Some({
let mut f = crate::type_tracking::FrameDescriptor::new();
f.return_kind = Some(crate::type_tracking::NativeKind::Bool);
f
}),
..Default::default()
});
assert_eq!(
vm.execute(None).unwrap().clone().as_bool(),
Some(true),
"42 == 42 (typed int)"
);
vm.load_program(BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::NeqNumber),
],
constants: vec![Constant::Number(1.5), Constant::Number(2.5)],
top_level_frame: Some({
let mut f = crate::type_tracking::FrameDescriptor::new();
f.return_kind = Some(crate::type_tracking::NativeKind::Bool);
f
}),
..Default::default()
});
assert_eq!(
vm.execute(None).unwrap().clone().as_bool(),
Some(true),
"1.5 != 2.5 (typed number)"
);
}
#[test]
fn test_logical_or_not() {
use crate::bytecode::*;
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::Or),
],
constants: vec![Constant::Bool(false), Constant::Bool(true)],
top_level_frame: Some({
let mut f = crate::type_tracking::FrameDescriptor::new();
f.return_kind = Some(crate::type_tracking::NativeKind::Bool);
f
}),
..Default::default()
});
assert_eq!(
vm.execute(None).unwrap().clone().as_bool(),
Some(true),
"false || true"
);
vm.load_program(BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::simple(OpCode::Not),
],
constants: vec![Constant::Bool(false)],
top_level_frame: Some({
let mut f = crate::type_tracking::FrameDescriptor::new();
f.return_kind = Some(crate::type_tracking::NativeKind::Bool);
f
}),
..Default::default()
});
assert_eq!(
vm.execute(None).unwrap().clone().as_bool(),
Some(true),
"!false"
);
}
#[test]
fn test_mod_pow_neg_opcodes() {
use crate::bytecode::*;
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::ModNumber),
],
constants: vec![Constant::Number(10.0), Constant::Number(3.0)],
..Default::default()
});
assert_eq!(
vm.execute(None).unwrap().clone().as_f64().unwrap(),
1.0,
"10 % 3"
);
vm.load_program(BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::PowNumber),
],
constants: vec![Constant::Number(2.0), Constant::Number(3.0)],
..Default::default()
});
assert_eq!(
vm.execute(None).unwrap().clone().as_f64().unwrap(),
8.0,
"2 ^ 3"
);
vm.load_program(BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::simple(OpCode::NegNumber),
],
constants: vec![Constant::Number(5.0)],
..Default::default()
});
assert_eq!(
vm.execute(None).unwrap().clone().as_f64().unwrap(),
-5.0,
"-5"
);
}
#[test]
fn test_swap_opcode_verify() {
use crate::bytecode::*;
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::simple(OpCode::Swap),
Instruction::simple(OpCode::Pop),
],
constants: vec![Constant::Number(5.0), Constant::Number(10.0)],
..Default::default()
};
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
assert_eq!(
vm.execute(None).unwrap().clone().as_f64().unwrap(),
10.0,
"Swap opcode"
);
}
#[test]
fn test_object_operations() {
use crate::bytecode::*;
let mut program = BytecodeProgram::default();
let schema_id = program.type_schema_registry.register_type(
"__test_obj_x",
vec![("x".to_string(), shape_runtime::type_schema::FieldType::Any)],
);
let schema_u16 = u16::try_from(schema_id).expect("schema id fits in u16 for test");
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: crate::executor::typed_object_ops::FIELD_TAG_ANY,
}),
),
];
program.constants = vec![Constant::Number(10.0)];
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
assert_eq!(
vm.execute(None).unwrap().clone().as_f64().unwrap(),
10.0,
"Object access"
);
}
#[test]
#[ignore = "V3-S5 ckpt-5 SURFACE: typed-annotation HeapValue accessor pending; T1 class-shift surface (ADR-006 §2.7.4)"]
fn test_wrap_type_annotation_opcode() {
todo!("V3-S5 ckpt-5 — typed-annotation HeapValue accessor pending; out of T1 scope")
}
#[test]
#[ignore = "V3-S5 ckpt-5 SURFACE: typed-annotation HeapValue accessor pending; T1 class-shift surface (ADR-006 §2.7.4)"]
fn test_wrap_type_annotation_with_string() {
todo!("V3-S5 ckpt-5 — typed-annotation HeapValue accessor pending; out of T1 scope")
}
#[test]
#[ignore = "V3-S5 ckpt-5 SURFACE: typed-annotation HeapValue accessor pending; T1 class-shift surface (ADR-006 §2.7.4)"]
fn test_type_annotated_value_in_variable() {
todo!("V3-S5 ckpt-5 — typed-annotation HeapValue accessor pending; out of T1 scope")
}
#[test]
#[ignore = "V3-S5 ckpt-5 SURFACE: typed-annotation HeapValue accessor pending; T1 class-shift surface (ADR-006 §2.7.4)"]
fn test_type_annotated_value_type_name() {
todo!("V3-S5 ckpt-5 — typed-annotation HeapValue accessor pending; out of T1 scope")
}
#[test]
#[ignore = "V3-S5 ckpt-5 SURFACE: typed-annotation HeapValue accessor pending; T1 class-shift surface (ADR-006 §2.7.4)"]
fn test_type_annotated_value_to_string() {
todo!("V3-S5 ckpt-5 — typed-annotation HeapValue accessor pending; out of T1 scope")
}
#[test]
fn test_wrap_type_annotation_preserves_operations() {
use crate::bytecode::*;
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))), Instruction::new(OpCode::WrapTypeAnnotation, Some(Operand::Property(0))), Instruction::new(OpCode::PushConst, Some(Operand::Const(1))), Instruction::simple(OpCode::AddNumber), ],
constants: vec![Constant::Number(10.0), Constant::Number(5.0)],
strings: vec!["Currency".to_string()],
..Default::default()
};
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None);
match result {
Ok(val) => {
println!("Result: {:?}", val);
}
Err(e) => {
println!("Error (expected): {:?}", e);
}
}
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_multiple_type_annotations() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted host-tier carriers)")
}
#[allow(dead_code)]
fn typed_table_from_slot(
slot: &shape_value::KindedSlot,
) -> Option<(u64, std::sync::Arc<shape_value::DataTable>)> {
use shape_value::heap_value::TableViewData;
use shape_value::{HeapKind, NativeKind};
match slot.kind() {
NativeKind::Ptr(HeapKind::TableView) => {
let bits = slot.raw();
if bits == 0 {
return None;
}
let tv = unsafe { &*(bits as *const TableViewData) };
match tv {
TableViewData::TypedTable { schema_id, table } => {
Some((*schema_id, std::sync::Arc::clone(table)))
}
_ => None,
}
}
_ => None,
}
}
#[test]
fn test_load_col_f64() {
use arrow_array::{Float64Array, RecordBatch};
use arrow_schema::{DataType, Field, Schema};
use shape_value::DataTable;
let schema = std::sync::Arc::new(Schema::new(vec![Field::new(
"price",
DataType::Float64,
false,
)]));
let batch = RecordBatch::try_new(
schema,
vec![std::sync::Arc::new(Float64Array::from(vec![42.5, 99.0]))],
)
.unwrap();
let table = std::sync::Arc::new(DataTable::new(batch));
let row_view = KindedConstant::from_row_view(0, table, 0);
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColF64,
Some(Operand::ColumnAccess { col_id: 0 }),
),
Instruction::simple(OpCode::Halt),
];
let constants = vec![Constant::Value(row_view)];
let bits = execute_bytecode_typed(instructions, constants, crate::type_tracking::NativeKind::Float64).unwrap();
let v = f64::from_bits(bits);
assert_eq!(v, 42.5, "Expected 42.5, got {}", v);
}
#[test]
fn test_load_col_i64() {
use arrow_array::{Int64Array, RecordBatch};
use arrow_schema::{DataType, Field, Schema};
use shape_value::DataTable;
let schema = std::sync::Arc::new(Schema::new(vec![Field::new(
"volume",
DataType::Int64,
false,
)]));
let batch = RecordBatch::try_new(
schema,
vec![std::sync::Arc::new(Int64Array::from(vec![1000, 2000]))],
)
.unwrap();
let table = std::sync::Arc::new(DataTable::new(batch));
let row_view = KindedConstant::from_row_view(0, table, 1);
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColI64,
Some(Operand::ColumnAccess { col_id: 0 }),
),
Instruction::simple(OpCode::Halt),
];
let constants = vec![Constant::Value(row_view)];
let bits = execute_bytecode_typed(instructions, constants, crate::type_tracking::NativeKind::Int64).unwrap();
assert_eq!(bits as i64, 2000, "Expected 2000, got {}", bits as i64);
}
#[test]
fn test_load_col_str() {
use arrow_array::{RecordBatch, StringArray};
use arrow_schema::{DataType, Field, Schema};
use shape_value::DataTable;
let schema = std::sync::Arc::new(Schema::new(vec![Field::new(
"symbol",
DataType::Utf8,
false,
)]));
let batch = RecordBatch::try_new(
schema,
vec![std::sync::Arc::new(StringArray::from(vec!["AAPL", "GOOG"]))],
)
.unwrap();
let table = std::sync::Arc::new(DataTable::new(batch));
let row_view = KindedConstant::from_row_view(0, table, 0);
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColStr,
Some(Operand::ColumnAccess { col_id: 0 }),
),
Instruction::simple(OpCode::Halt),
];
let constants = vec![Constant::Value(row_view)];
let program = BytecodeProgram {
instructions,
constants,
..Default::default()
};
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None).unwrap();
assert_eq!(
result.as_str().expect("Expected String"),
"AAPL"
);
}
#[test]
fn test_bind_schema_success() {
use arrow_array::{Float64Array, RecordBatch, StringArray};
use arrow_schema::{DataType, Field, Schema as ArrowSchema};
use shape_runtime::type_schema::TypeSchemaBuilder;
use shape_value::datatable::DataTable;
use std::sync::Arc;
let schema = Arc::new(ArrowSchema::new(vec![
Field::new("price", DataType::Float64, false),
Field::new("symbol", DataType::Utf8, false),
]));
let batch = RecordBatch::try_new(
schema,
vec![
Arc::new(Float64Array::from(vec![100.0, 200.0])),
Arc::new(StringArray::from(vec!["AAPL", "GOOG"])),
],
)
.unwrap();
let table = DataTable::new(batch);
let mut registry = shape_runtime::type_schema::TypeSchemaRegistry::new();
let schema_id = TypeSchemaBuilder::new("TestTrade")
.f64_field("price")
.string_field("symbol")
.register(&mut registry);
let datatable_val = KindedConstant::from_datatable(Arc::new(table));
let mut program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::BindSchema, Some(Operand::Count(schema_id as u16))),
Instruction::simple(OpCode::Halt),
],
constants: vec![Constant::Value(datatable_val)],
..Default::default()
};
program.type_schema_registry = registry;
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None).unwrap();
let (sid, table) =
typed_table_from_slot(&result).expect("Expected TypedTable result");
assert_eq!(sid, schema_id as u64);
assert_eq!(table.row_count(), 2);
}
#[test]
fn test_bind_schema_missing_column() {
use arrow_array::{Float64Array, RecordBatch};
use arrow_schema::{DataType, Field, Schema as ArrowSchema};
use shape_runtime::type_schema::TypeSchemaBuilder;
use shape_value::datatable::DataTable;
use std::sync::Arc;
let schema = Arc::new(ArrowSchema::new(vec![Field::new(
"price",
DataType::Float64,
false,
)]));
let batch =
RecordBatch::try_new(schema, vec![Arc::new(Float64Array::from(vec![100.0]))]).unwrap();
let table = DataTable::new(batch);
let mut registry = shape_runtime::type_schema::TypeSchemaRegistry::new();
let schema_id = TypeSchemaBuilder::new("TestTrade2")
.f64_field("price")
.f64_field("volume")
.register(&mut registry);
let datatable_val = KindedConstant::from_datatable(Arc::new(table));
let mut program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::BindSchema, Some(Operand::Count(schema_id as u16))),
Instruction::simple(OpCode::Halt),
],
constants: vec![Constant::Value(datatable_val)],
..Default::default()
};
program.type_schema_registry = registry;
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None);
assert!(result.is_err(), "BindSchema should fail for missing column");
let err = format!("{}", result.unwrap_err());
assert!(
err.contains("volume"),
"Error should mention missing column 'volume': {}",
err
);
}
fn make_test_pipeline_table() -> KindedConstant {
use arrow_array::{
BooleanArray, Float64Array, Int64Array, RecordBatch, StringArray, TimestampMillisecondArray,
};
use arrow_schema::{DataType, Field, Schema as ArrowSchema, TimeUnit};
use shape_value::datatable::DataTable;
use std::sync::Arc;
let symbols = ["AAPL", "GOOG", "MSFT", "TSLA", "AMZN"];
let timestamp_values: Vec<i64> = (0..100)
.map(|i| 1_704_067_200_000_i64 + (i as i64) * 60_000_i64)
.collect();
let symbol_values: Vec<&str> = (0..100).map(|i| symbols[i % symbols.len()]).collect();
let price_values: Vec<f64> = (0..100).map(|i| 100.0 + (i as f64) * 1.23).collect();
let volume_values: Vec<i64> = (0..100).map(|i| 1_000_000 + i as i64 * 12_345).collect();
let is_buy_values: Vec<bool> = (0..100).map(|i| i % 2 == 0).collect();
let schema = Arc::new(ArrowSchema::new(vec![
Field::new(
"timestamp",
DataType::Timestamp(TimeUnit::Millisecond, None),
false,
),
Field::new("symbol", DataType::Utf8, false),
Field::new("price", DataType::Float64, false),
Field::new("volume", DataType::Int64, false),
Field::new("is_buy", DataType::Boolean, false),
]));
let batch = RecordBatch::try_new(
schema,
vec![
Arc::new(TimestampMillisecondArray::from(timestamp_values)),
Arc::new(StringArray::from(symbol_values)),
Arc::new(Float64Array::from(price_values)),
Arc::new(Int64Array::from(volume_values)),
Arc::new(BooleanArray::from(is_buy_values)),
],
)
.unwrap();
KindedConstant::from_datatable(Arc::new(DataTable::new(batch)))
}
fn build_bind_schema_program(
value: KindedConstant,
registry: shape_runtime::type_schema::TypeSchemaRegistry,
schema_id: u32,
) -> BytecodeProgram {
let mut program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::BindSchema, Some(Operand::Count(schema_id as u16))),
Instruction::simple(OpCode::Halt),
],
constants: vec![Constant::Value(value)],
..Default::default()
};
program.type_schema_registry = registry;
program
}
#[test]
fn test_load_pipeline_correct_mapping() {
use shape_runtime::type_schema::{TypeSchemaBuilder, TypeSchemaRegistry};
let dt_val = make_test_pipeline_table();
let mut registry = TypeSchemaRegistry::new();
let schema_id = TypeSchemaBuilder::new("PipelineTrade")
.timestamp_field("timestamp")
.string_field("symbol")
.f64_field("price")
.i64_field("volume")
.bool_field("is_buy")
.register(&mut registry);
let program = build_bind_schema_program(dt_val, registry, schema_id);
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None).unwrap();
let (sid, table) =
typed_table_from_slot(&result).expect("Expected TypedTable result");
assert_eq!(sid, schema_id as u64);
assert_eq!(table.row_count(), 100);
}
#[test]
fn test_load_pipeline_f64_field_on_string_column() {
use shape_runtime::type_schema::{TypeSchemaBuilder, TypeSchemaRegistry};
let dt_val = make_test_pipeline_table();
let mut registry = TypeSchemaRegistry::new();
let schema_id = TypeSchemaBuilder::new("BadF64")
.f64_field("symbol") .register(&mut registry);
let program = build_bind_schema_program(dt_val, registry, schema_id);
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None);
assert!(result.is_err());
let err = format!("{}", result.unwrap_err());
assert!(
err.contains("symbol"),
"Error should mention 'symbol': {}",
err
);
assert!(
err.contains("type"),
"Error should mention type mismatch: {}",
err
);
}
#[test]
fn test_load_pipeline_string_field_on_number_column() {
use shape_runtime::type_schema::{TypeSchemaBuilder, TypeSchemaRegistry};
let dt_val = make_test_pipeline_table();
let mut registry = TypeSchemaRegistry::new();
let schema_id = TypeSchemaBuilder::new("BadStr")
.string_field("price") .register(&mut registry);
let program = build_bind_schema_program(dt_val, registry, schema_id);
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None);
assert!(result.is_err());
let err = format!("{}", result.unwrap_err());
assert!(
err.contains("price"),
"Error should mention 'price': {}",
err
);
assert!(
err.contains("type"),
"Error should mention type mismatch: {}",
err
);
}
#[test]
fn test_load_pipeline_missing_column() {
use shape_runtime::type_schema::{TypeSchemaBuilder, TypeSchemaRegistry};
let dt_val = make_test_pipeline_table();
let mut registry = TypeSchemaRegistry::new();
let schema_id = TypeSchemaBuilder::new("MissingCol")
.f64_field("nonexistent")
.register(&mut registry);
let program = build_bind_schema_program(dt_val, registry, schema_id);
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None);
assert!(result.is_err());
let err = format!("{}", result.unwrap_err());
assert!(
err.contains("nonexistent"),
"Error should mention 'nonexistent': {}",
err
);
assert!(
err.contains("column"),
"Error should mention missing column: {}",
err
);
}
#[test]
fn test_load_pipeline_subset_columns() {
use shape_runtime::type_schema::{TypeSchemaBuilder, TypeSchemaRegistry};
let dt_val = make_test_pipeline_table();
let mut registry = TypeSchemaRegistry::new();
let schema_id = TypeSchemaBuilder::new("SubsetTrade")
.f64_field("price")
.string_field("symbol")
.register(&mut registry);
let program = build_bind_schema_program(dt_val, registry, schema_id);
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None).unwrap();
let (sid, table) =
typed_table_from_slot(&result).expect("Expected TypedTable result");
assert_eq!(sid, schema_id as u64);
assert_eq!(table.row_count(), 100);
}
#[test]
fn test_load_pipeline_column_alias() {
use shape_runtime::type_schema::FieldType;
use shape_runtime::type_schema::{TypeSchemaBuilder, TypeSchemaRegistry};
let dt_val = make_test_pipeline_table();
let mut registry = TypeSchemaRegistry::new();
let schema_id = TypeSchemaBuilder::new("AliasTrade")
.field_with_meta(
"close",
FieldType::F64,
vec![shape_runtime::type_schema::FieldAnnotation {
name: "alias".to_string(),
args: vec!["price".to_string()],
}],
)
.register(&mut registry);
let program = build_bind_schema_program(dt_val, registry, schema_id);
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None).unwrap();
let (sid, table) =
typed_table_from_slot(&result).expect("Expected TypedTable result");
assert_eq!(sid, schema_id as u64);
assert_eq!(table.row_count(), 100);
}
#[test]
fn test_load_pipeline_wrong_alias() {
use shape_runtime::type_schema::FieldType;
use shape_runtime::type_schema::{TypeSchemaBuilder, TypeSchemaRegistry};
let dt_val = make_test_pipeline_table();
let mut registry = TypeSchemaRegistry::new();
let schema_id = TypeSchemaBuilder::new("WrongAlias")
.field_with_meta(
"close",
FieldType::F64,
vec![shape_runtime::type_schema::FieldAnnotation {
name: "alias".to_string(),
args: vec!["nonexistent".to_string()],
}],
)
.register(&mut registry);
let program = build_bind_schema_program(dt_val, registry, schema_id);
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None);
assert!(result.is_err());
let err = format!("{}", result.unwrap_err());
assert!(
err.contains("nonexistent"),
"Error should mention 'nonexistent': {}",
err
);
}
#[test]
fn test_load_pipeline_timestamp_field() {
use shape_runtime::type_schema::{TypeSchemaBuilder, TypeSchemaRegistry};
let dt_val = make_test_pipeline_table();
let mut registry = TypeSchemaRegistry::new();
let schema_id = TypeSchemaBuilder::new("TsTrade")
.timestamp_field("timestamp")
.register(&mut registry);
let program = build_bind_schema_program(dt_val, registry, schema_id);
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None).unwrap();
let (sid, table) =
typed_table_from_slot(&result).expect("Expected TypedTable result");
assert_eq!(sid, schema_id as u64);
assert_eq!(table.row_count(), 100);
}
#[test]
fn test_load_pipeline_numeric_promotion() {
use shape_runtime::type_schema::{TypeSchemaBuilder, TypeSchemaRegistry};
let dt_val = make_test_pipeline_table();
let mut registry = TypeSchemaRegistry::new();
let schema_id = TypeSchemaBuilder::new("PromoTrade")
.f64_field("volume")
.register(&mut registry);
let program = build_bind_schema_program(dt_val, registry, schema_id);
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None).unwrap();
let (sid, table) =
typed_table_from_slot(&result).expect("Expected TypedTable result");
assert_eq!(sid, schema_id as u64);
assert_eq!(table.row_count(), 100);
}
#[test]
fn test_load_pipeline_non_table_value() {
use shape_runtime::type_schema::{TypeSchemaBuilder, TypeSchemaRegistry};
let mut registry = shape_runtime::type_schema::TypeSchemaRegistry::new();
let schema_id = TypeSchemaBuilder::new("AnyType")
.f64_field("x")
.register(&mut registry);
let _ = TypeSchemaRegistry::new;
let mut program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::BindSchema, Some(Operand::Count(schema_id as u16))),
Instruction::simple(OpCode::Halt),
],
constants: vec![Constant::Number(42.0)],
..Default::default()
};
program.type_schema_registry = registry;
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None);
assert!(result.is_err());
let err = format!("{}", result.unwrap_err());
assert!(
err.contains("expected DataTable") || err.contains("got") || err.contains("DataTable"),
"Error should mention expected DataTable: {}",
err
);
}
#[test]
fn test_load_col_bool() {
use arrow_array::{BooleanArray, RecordBatch};
use arrow_schema::{DataType, Field, Schema};
use shape_value::DataTable;
let schema = std::sync::Arc::new(Schema::new(vec![Field::new(
"flag",
DataType::Boolean,
false,
)]));
let batch = RecordBatch::try_new(
schema,
vec![std::sync::Arc::new(BooleanArray::from(vec![
true, false, true,
]))],
)
.unwrap();
let table = std::sync::Arc::new(DataTable::new(batch));
let row_view = KindedConstant::from_row_view(0, table.clone(), 1);
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColBool,
Some(Operand::ColumnAccess { col_id: 0 }),
),
Instruction::simple(OpCode::Halt),
];
let constants = vec![Constant::Value(row_view)];
let bits = execute_bytecode_typed(instructions, constants, crate::type_tracking::NativeKind::Bool).unwrap();
assert_eq!(bits != 0, false, "Expected false, got {}", bits != 0);
let row_view2 = KindedConstant::from_row_view(0, table, 2);
let instructions2 = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColBool,
Some(Operand::ColumnAccess { col_id: 0 }),
),
Instruction::simple(OpCode::Halt),
];
let constants2 = vec![Constant::Value(row_view2)];
let bits2 = execute_bytecode_typed(instructions2, constants2, crate::type_tracking::NativeKind::Bool).unwrap();
assert_eq!(bits2 != 0, true, "Expected true, got {}", bits2 != 0);
}
#[test]
fn test_load_col_f64_from_float32() {
use arrow_array::{Float32Array, RecordBatch};
use arrow_schema::{DataType, Field, Schema};
use shape_value::DataTable;
let schema = std::sync::Arc::new(Schema::new(vec![Field::new(
"val",
DataType::Float32,
false,
)]));
let batch = RecordBatch::try_new(
schema,
vec![std::sync::Arc::new(Float32Array::from(vec![
3.14f32, 2.72f32,
]))],
)
.unwrap();
let table = std::sync::Arc::new(DataTable::new(batch));
let row_view = KindedConstant::from_row_view(0, table, 0);
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColF64,
Some(Operand::ColumnAccess { col_id: 0 }),
),
Instruction::simple(OpCode::Halt),
];
let constants = vec![Constant::Value(row_view)];
let bits = execute_bytecode_typed(instructions, constants, crate::type_tracking::NativeKind::Float64).unwrap();
let n = f64::from_bits(bits);
assert!((n - 3.14).abs() < 0.001, "Expected ~3.14, got {}", n);
}
#[test]
fn test_load_col_f64_from_int64() {
use arrow_array::{Int64Array, RecordBatch};
use arrow_schema::{DataType, Field, Schema};
use shape_value::DataTable;
let schema = std::sync::Arc::new(Schema::new(vec![Field::new(
"count",
DataType::Int64,
false,
)]));
let batch = RecordBatch::try_new(
schema,
vec![std::sync::Arc::new(Int64Array::from(vec![42, 100]))],
)
.unwrap();
let table = std::sync::Arc::new(DataTable::new(batch));
let row_view = KindedConstant::from_row_view(0, table, 0);
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColF64,
Some(Operand::ColumnAccess { col_id: 0 }),
),
Instruction::simple(OpCode::Halt),
];
let constants = vec![Constant::Value(row_view)];
let bits = execute_bytecode_typed(instructions, constants, crate::type_tracking::NativeKind::Float64).unwrap();
let v = f64::from_bits(bits);
assert_eq!(v, 42.0, "Expected 42.0, got {}", v);
}
#[test]
fn test_load_col_i64_from_int32() {
use arrow_array::{Int32Array, RecordBatch};
use arrow_schema::{DataType, Field, Schema};
use shape_value::DataTable;
let schema = std::sync::Arc::new(Schema::new(vec![Field::new(
"small",
DataType::Int32,
false,
)]));
let batch = RecordBatch::try_new(
schema,
vec![std::sync::Arc::new(Int32Array::from(vec![123, 456]))],
)
.unwrap();
let table = std::sync::Arc::new(DataTable::new(batch));
let row_view = KindedConstant::from_row_view(0, table, 1);
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColI64,
Some(Operand::ColumnAccess { col_id: 0 }),
),
Instruction::simple(OpCode::Halt),
];
let constants = vec![Constant::Value(row_view)];
let bits = execute_bytecode_typed(instructions, constants, crate::type_tracking::NativeKind::Int64).unwrap();
assert_eq!(bits as i64, 456, "Expected 456, got {}", bits as i64);
}
#[test]
fn test_load_col_str_row1() {
use arrow_array::{RecordBatch, StringArray};
use arrow_schema::{DataType, Field, Schema};
use shape_value::DataTable;
let schema = std::sync::Arc::new(Schema::new(vec![Field::new("name", DataType::Utf8, false)]));
let batch = RecordBatch::try_new(
schema,
vec![std::sync::Arc::new(StringArray::from(vec![
"alpha", "beta", "gamma",
]))],
)
.unwrap();
let table = std::sync::Arc::new(DataTable::new(batch));
let row_view = KindedConstant::from_row_view(0, table, 1);
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColStr,
Some(Operand::ColumnAccess { col_id: 0 }),
),
Instruction::simple(OpCode::Halt),
];
let constants = vec![Constant::Value(row_view)];
let program = BytecodeProgram {
instructions,
constants,
..Default::default()
};
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None).unwrap();
assert_eq!(result.as_str().expect("Expected String"), "beta");
}
#[test]
fn test_load_col_out_of_bounds_row() {
use arrow_array::{Float64Array, RecordBatch};
use arrow_schema::{DataType, Field, Schema};
use shape_value::DataTable;
let schema = std::sync::Arc::new(Schema::new(vec![Field::new("x", DataType::Float64, false)]));
let batch = RecordBatch::try_new(
schema,
vec![std::sync::Arc::new(Float64Array::from(vec![1.0, 2.0]))],
)
.unwrap();
let table = std::sync::Arc::new(DataTable::new(batch));
let row_view = KindedConstant::from_row_view(0, table, 5);
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColF64,
Some(Operand::ColumnAccess { col_id: 0 }),
),
Instruction::simple(OpCode::Halt),
];
let constants = vec![Constant::Value(row_view)];
let result = execute_bytecode(instructions, constants);
assert!(result.is_err(), "Should error on out-of-bounds row");
let err = format!("{}", result.unwrap_err());
assert!(
err.contains("Row index") || err.contains("out of bounds"),
"Error should mention row out of bounds: {}",
err
);
}
#[test]
fn test_load_col_out_of_bounds_col() {
use arrow_array::{Float64Array, RecordBatch};
use arrow_schema::{DataType, Field, Schema};
use shape_value::DataTable;
let schema = std::sync::Arc::new(Schema::new(vec![Field::new("x", DataType::Float64, false)]));
let batch = RecordBatch::try_new(
schema,
vec![std::sync::Arc::new(Float64Array::from(vec![1.0]))],
)
.unwrap();
let table = std::sync::Arc::new(DataTable::new(batch));
let row_view = KindedConstant::from_row_view(0, table, 0);
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColF64,
Some(Operand::ColumnAccess { col_id: 5 }),
),
Instruction::simple(OpCode::Halt),
];
let constants = vec![Constant::Value(row_view)];
let result = execute_bytecode(instructions, constants);
assert!(result.is_err(), "Should error on out-of-bounds column");
let err = format!("{}", result.unwrap_err());
assert!(
err.contains("Column index") || err.contains("out of bounds"),
"Error should mention column out of bounds: {}",
err
);
}
#[test]
fn test_load_col_wrong_value_type() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColF64,
Some(Operand::ColumnAccess { col_id: 0 }),
),
Instruction::simple(OpCode::Halt),
];
let constants = vec![Constant::Number(42.0)];
let result = execute_bytecode(instructions, constants);
assert!(
result.is_err(),
"Should error when LoadCol* gets non-RowView"
);
let err = format!("{}", result.unwrap_err());
assert!(
err.contains("RowView") || err.contains("expected"),
"Error should mention expected RowView: {}",
err
);
}
#[test]
fn test_load_col_multi_column() {
use arrow_array::{BooleanArray, Float64Array, RecordBatch, StringArray};
use arrow_schema::{DataType, Field, Schema};
use shape_value::DataTable;
let schema = std::sync::Arc::new(Schema::new(vec![
Field::new("price", DataType::Float64, false),
Field::new("active", DataType::Boolean, false),
Field::new("label", DataType::Utf8, false),
]));
let batch = RecordBatch::try_new(
schema,
vec![
std::sync::Arc::new(Float64Array::from(vec![10.5, 20.0])),
std::sync::Arc::new(BooleanArray::from(vec![true, false])),
std::sync::Arc::new(StringArray::from(vec!["buy", "sell"])),
],
)
.unwrap();
let table = std::sync::Arc::new(DataTable::new(batch));
let rv = KindedConstant::from_row_view(0, table.clone(), 1);
let bits = execute_bytecode_typed(
vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColF64,
Some(Operand::ColumnAccess { col_id: 0 }),
),
Instruction::simple(OpCode::Halt),
],
vec![Constant::Value(rv)],
crate::type_tracking::NativeKind::Float64,
)
.unwrap();
let v = f64::from_bits(bits);
assert_eq!(v, 20.0, "Expected 20.0, got {}", v);
let rv = KindedConstant::from_row_view(0, table.clone(), 0);
let bits = execute_bytecode_typed(
vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColBool,
Some(Operand::ColumnAccess { col_id: 1 }),
),
Instruction::simple(OpCode::Halt),
],
vec![Constant::Value(rv)],
crate::type_tracking::NativeKind::Bool,
)
.unwrap();
assert_eq!(bits != 0, true, "Expected true, got {}", bits != 0);
let rv = KindedConstant::from_row_view(0, table.clone(), 1);
let program = BytecodeProgram {
instructions: vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(
OpCode::LoadColStr,
Some(Operand::ColumnAccess { col_id: 2 }),
),
Instruction::simple(OpCode::Halt),
],
constants: vec![Constant::Value(rv)],
..Default::default()
};
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(program);
let result = vm.execute(None).unwrap();
assert_eq!(result.as_str().expect("Expected String"), "sell");
}
#[test]
fn test_dynamic_object_methods_are_rejected() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))), Instruction::new(OpCode::PushConst, Some(Operand::Const(1))), Instruction::new(OpCode::NewObject, Some(Operand::Count(1))), Instruction::new(OpCode::PushConst, Some(Operand::Const(0))), Instruction::new(OpCode::PushConst, Some(Operand::Const(2))), Instruction::new(OpCode::PushConst, Some(Operand::Const(3))), Instruction::simple(OpCode::CallMethod),
Instruction::simple(OpCode::Halt),
];
let constants = vec![
Constant::String("name".to_string()),
Constant::String("hello".to_string()),
Constant::String("get".to_string()),
Constant::Number(1.0),
];
let result = execute_bytecode(instructions, constants);
assert!(
result.is_err(),
"Typed object dynamic helper methods must be rejected"
);
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_extension_intrinsic_dispatch() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted host-tier carriers)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_extension_intrinsic_takes_priority_over_ufcs() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted host-tier carriers)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_extension_intrinsic_fallback_to_ufcs_when_no_match() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted host-tier carriers)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_hoisted_field_in_typed_object() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted helper)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_hoisted_field_stays_typed_object() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted helper)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_array_index_assignment_accepts_int_keys() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted helper)")
}
#[test]
#[ignore = "Wave B made array literals emit v2 typed opcodes unconditionally; v2 TypedArray uses refcounting (no Arc) so copy-on-write aliasing semantics differ from v1 VMArray. Test exercises v1 semantics; needs rewrite for v2 semantics."]
fn test_array_index_assignment_preserves_copy_on_write_aliasing() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted helper)")
}
#[test]
fn test_array_index_assignment_uses_local_fast_path_opcode() {
let program = shape_ast::parser::parse_program(
r#"
let mut a = [1, 2]
a[0] = 9
"#,
)
.expect("program should parse");
let compiler = crate::compiler::BytecodeCompiler::new();
let bytecode = compiler.compile(&program).expect("program should compile");
assert!(
bytecode.instructions.iter().any(|ins| {
matches!(
ins.opcode,
OpCode::SetLocalIndex
| OpCode::SetModuleBindingIndex
| OpCode::TypedArraySetI64
| OpCode::TypedArraySetI32
| OpCode::TypedArraySetF64
| OpCode::TypedArraySetBool
)
}),
"expected SetLocalIndex/SetModuleBindingIndex/TypedArraySet* opcode in compiled bytecode"
);
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_print_uses_default_display_impl() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted helper)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_to_string_uses_display_impl() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted helper)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_universal_type_method_returns_type_name() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted helper)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_type_method_to_string_returns_canonical_name() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted helper)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_print_uses_named_display_impl_with_using_selector() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted helper)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_print_named_display_impl_supports_dollar_formatted_json_strings() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted helper)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_print_supports_hash_formatted_strings() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted helper)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_print_without_default_display_impl_reports_ambiguity_for_named_impls() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted helper)")
}
#[test]
fn test_window_sum_builtin_executes() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))), Instruction::new(OpCode::PushConst, Some(Operand::Const(1))), Instruction::new(OpCode::PushConst, Some(Operand::Const(2))), Instruction::new(OpCode::NewArray, Some(Operand::Count(3))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(3))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(4))),
Instruction::new(
OpCode::BuiltinCall,
Some(Operand::Builtin(BuiltinFunction::WindowSum)),
),
];
let constants = vec![
Constant::Number(1.0),
Constant::Number(2.0),
Constant::Number(3.0),
Constant::String("".to_string()),
Constant::Number(2.0), ];
let result = execute_bytecode(instructions, constants);
assert!(
result.is_ok(),
"WindowSum should execute: {:?}",
result.err()
);
assert_eq!(
f64::from_bits(result.unwrap()),
6.0,
"sum([1,2,3]) = 6"
);
}
#[test]
fn test_window_avg_builtin_executes() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(2))),
Instruction::new(OpCode::NewArray, Some(Operand::Count(3))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(3))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(4))),
Instruction::new(
OpCode::BuiltinCall,
Some(Operand::Builtin(BuiltinFunction::WindowAvg)),
),
];
let constants = vec![
Constant::Number(10.0),
Constant::Number(20.0),
Constant::Number(30.0),
Constant::String("".to_string()),
Constant::Number(2.0),
];
let result = execute_bytecode(instructions, constants);
assert!(
result.is_ok(),
"WindowAvg should execute: {:?}",
result.err()
);
assert_eq!(
f64::from_bits(result.unwrap()),
20.0,
"avg([10,20,30]) = 20"
);
}
#[test]
fn test_window_count_builtin_executes() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::new(OpCode::NewArray, Some(Operand::Count(2))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(2))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(3))),
Instruction::new(
OpCode::BuiltinCall,
Some(Operand::Builtin(BuiltinFunction::WindowCount)),
),
];
let constants = vec![
Constant::Number(5.0),
Constant::Number(10.0),
Constant::String("".to_string()),
Constant::Number(2.0),
];
let result = execute_bytecode(instructions, constants);
assert!(
result.is_ok(),
"WindowCount should execute: {:?}",
result.err()
);
let _result_val = result.unwrap();
let n = f64::from_bits(_result_val);
assert_eq!(n, 2.0, "count([5,10]) = 2");
}
#[test]
fn test_window_min_max_builtin_executes() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(2))),
Instruction::new(OpCode::NewArray, Some(Operand::Count(3))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(3))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(4))),
Instruction::new(
OpCode::BuiltinCall,
Some(Operand::Builtin(BuiltinFunction::WindowMin)),
),
];
let constants = vec![
Constant::Number(7.0),
Constant::Number(3.0),
Constant::Number(9.0),
Constant::String("".to_string()),
Constant::Number(2.0),
];
let result = execute_bytecode(instructions, constants);
assert!(
result.is_ok(),
"WindowMin should execute: {:?}",
result.err()
);
assert_eq!(
f64::from_bits(result.unwrap()),
3.0,
"min([7,3,9]) = 3"
);
let instructions2 = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(2))),
Instruction::new(OpCode::NewArray, Some(Operand::Count(3))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(3))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(4))),
Instruction::new(
OpCode::BuiltinCall,
Some(Operand::Builtin(BuiltinFunction::WindowMax)),
),
];
let constants2 = vec![
Constant::Number(7.0),
Constant::Number(3.0),
Constant::Number(9.0),
Constant::String("".to_string()),
Constant::Number(2.0),
];
let result2 = execute_bytecode(instructions2, constants2);
assert!(
result2.is_ok(),
"WindowMax should execute: {:?}",
result2.err()
);
assert_eq!(
f64::from_bits(result2.unwrap()),
9.0,
"max([7,3,9]) = 9"
);
}
#[test]
fn test_window_row_number_builtin_executes() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))), Instruction::new(OpCode::PushConst, Some(Operand::Const(1))), Instruction::new(OpCode::PushConst, Some(Operand::Const(2))), Instruction::new(
OpCode::BuiltinCall,
Some(Operand::Builtin(BuiltinFunction::WindowRowNumber)),
),
];
let constants = vec![
Constant::Number(42.0),
Constant::String("".to_string()),
Constant::Number(2.0),
];
let result = execute_bytecode(instructions, constants);
assert!(
result.is_ok(),
"WindowRowNumber should execute: {:?}",
result.err()
);
}
#[test]
fn test_window_lag_lead_builtin_executes() {
let instructions = vec![
Instruction::new(OpCode::PushConst, Some(Operand::Const(0))), Instruction::new(OpCode::PushConst, Some(Operand::Const(1))), Instruction::new(OpCode::PushConst, Some(Operand::Const(2))), Instruction::new(OpCode::PushConst, Some(Operand::Const(3))), Instruction::new(OpCode::PushConst, Some(Operand::Const(4))), Instruction::new(
OpCode::BuiltinCall,
Some(Operand::Builtin(BuiltinFunction::WindowLag)),
),
];
let constants = vec![
Constant::Number(100.0),
Constant::Number(1.0),
Constant::Number(0.0),
Constant::String("".to_string()),
Constant::Number(4.0),
];
let result = execute_bytecode(instructions, constants);
assert!(
result.is_ok(),
"WindowLag should execute: {:?}",
result.err()
);
assert_eq!(
f64::from_bits(result.unwrap()),
0.0,
"lag with no history returns default"
);
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_cte_compiles_and_runs() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted helper)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_module_context_can_invoke_shape_callable() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted host-tier carriers)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_r5_4d_intrinsic_vec_add_i64_bytecode_dispatch() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted host-tier carriers)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_r5_4d_intrinsic_mat_add_bytecode_dispatch() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted host-tier carriers)")
}
#[test]
#[ignore = "T1 class-shift surface (ADR-006 §2.7.4) — depends on deleted host-tier helpers / typed-Arc accessors not in T1 scope"]
fn test_r5_4d_intrinsic_mat_sub_bytecode_dispatch() {
todo!("phase-2c — see ADR-006 §2.7.4 (host-tier eval/marshal API rebuild — deleted host-tier carriers)")
}