use super::*;
use shape_value::{ValueWord, ValueWordExt};
use std::sync::Arc;
fn nb_str(s: &str) -> ValueWord {
ValueWord::from_string(Arc::new(s.to_string()))
}
fn test_hashmap() -> ValueWord {
let keys = vec![nb_str("a"), nb_str("b"), nb_str("c")];
let values = vec![
ValueWord::from_i64(1),
ValueWord::from_i64(2),
ValueWord::from_i64(3),
];
ValueWord::from_hashmap_pairs(keys, values)
}
#[test]
fn test_hashmap_get_existing_key() {
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::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("b".to_string()),
Constant::String("get".to_string()),
Constant::Number(1.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_i64(), Some(2));
}
#[test]
fn test_hashmap_get_missing_key() {
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::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("z".to_string()),
Constant::String("get".to_string()),
Constant::Number(1.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert!(result.is_none());
}
#[test]
fn test_hashmap_set_new_key() {
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::simple(OpCode::CallMethod),
Instruction::new(OpCode::PushConst, Some(Operand::Const(5))), Instruction::new(OpCode::PushConst, Some(Operand::Const(6))), Instruction::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("d".to_string()),
Constant::Number(4.0),
Constant::String("set".to_string()),
Constant::Number(2.0),
Constant::String("len".to_string()),
Constant::Number(0.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_i64(), Some(4));
}
#[test]
fn test_hashmap_set_existing_key() {
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::simple(OpCode::CallMethod),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))), Instruction::new(OpCode::PushConst, Some(Operand::Const(5))), Instruction::new(OpCode::PushConst, Some(Operand::Const(6))), Instruction::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("b".to_string()),
Constant::Number(99.0),
Constant::String("set".to_string()),
Constant::Number(2.0),
Constant::String("get".to_string()),
Constant::Number(1.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.to_number().unwrap(), 99.0);
}
#[test]
fn test_hashmap_has_existing() {
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::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("a".to_string()),
Constant::String("has".to_string()),
Constant::Number(1.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_bool(), Some(true));
}
#[test]
fn test_hashmap_has_missing() {
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::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("z".to_string()),
Constant::String("has".to_string()),
Constant::Number(1.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_bool(), Some(false));
}
#[test]
fn test_hashmap_delete() {
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::simple(OpCode::CallMethod),
Instruction::new(OpCode::PushConst, Some(Operand::Const(4))), Instruction::new(OpCode::PushConst, Some(Operand::Const(5))), Instruction::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("b".to_string()),
Constant::String("delete".to_string()),
Constant::Number(1.0),
Constant::String("len".to_string()),
Constant::Number(0.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_i64(), Some(2));
}
#[test]
fn test_hashmap_delete_missing_key() {
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::simple(OpCode::CallMethod),
Instruction::new(OpCode::PushConst, Some(Operand::Const(4))),
Instruction::new(OpCode::PushConst, Some(Operand::Const(5))),
Instruction::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("z".to_string()),
Constant::String("delete".to_string()),
Constant::Number(1.0),
Constant::String("len".to_string()),
Constant::Number(0.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_i64(), Some(3));
}
#[test]
fn test_hashmap_keys() {
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::simple(OpCode::CallMethod),
Instruction::new(OpCode::PushConst, Some(Operand::Const(3))), Instruction::new(OpCode::PushConst, Some(Operand::Const(2))), Instruction::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("keys".to_string()),
Constant::Number(0.0),
Constant::String("length".to_string()),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_i64(), Some(3));
}
#[test]
fn test_hashmap_values() {
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::simple(OpCode::CallMethod),
Instruction::new(OpCode::PushConst, Some(Operand::Const(3))), Instruction::new(OpCode::PushConst, Some(Operand::Const(2))), Instruction::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("values".to_string()),
Constant::Number(0.0),
Constant::String("length".to_string()),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_i64(), Some(3));
}
#[test]
fn test_hashmap_entries() {
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::simple(OpCode::CallMethod),
Instruction::new(OpCode::PushConst, Some(Operand::Const(3))), Instruction::new(OpCode::PushConst, Some(Operand::Const(2))), Instruction::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("entries".to_string()),
Constant::Number(0.0),
Constant::String("length".to_string()),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_i64(), Some(3));
}
#[test]
fn test_hashmap_len() {
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::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("len".to_string()),
Constant::Number(0.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_i64(), Some(3));
}
#[test]
fn test_hashmap_len_empty() {
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::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(ValueWord::empty_hashmap()),
Constant::String("len".to_string()),
Constant::Number(0.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_i64(), Some(0));
}
#[test]
fn test_hashmap_is_empty_true() {
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::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(ValueWord::empty_hashmap()),
Constant::String("isEmpty".to_string()),
Constant::Number(0.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_bool(), Some(true));
}
#[test]
fn test_hashmap_is_empty_false() {
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::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("isEmpty".to_string()),
Constant::Number(0.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_bool(), Some(false));
}
#[test]
fn test_hashmap_set_then_get() {
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::simple(OpCode::CallMethod),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))), Instruction::new(OpCode::PushConst, Some(Operand::Const(5))), Instruction::new(OpCode::PushConst, Some(Operand::Const(6))), Instruction::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(ValueWord::empty_hashmap()),
Constant::String("x".to_string()),
Constant::Number(42.0),
Constant::String("set".to_string()),
Constant::Number(2.0),
Constant::String("get".to_string()),
Constant::Number(1.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.to_number().unwrap(), 42.0);
}
#[test]
fn test_hashmap_delete_then_has() {
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::simple(OpCode::CallMethod),
Instruction::new(OpCode::PushConst, Some(Operand::Const(1))), Instruction::new(OpCode::PushConst, Some(Operand::Const(4))), Instruction::new(OpCode::PushConst, Some(Operand::Const(3))), Instruction::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("a".to_string()),
Constant::String("delete".to_string()),
Constant::Number(1.0),
Constant::String("has".to_string()),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_bool(), Some(false));
}
#[test]
fn test_hashmap_integer_keys() {
let keys = vec![ValueWord::from_i64(1), ValueWord::from_i64(2)];
let values = vec![nb_str("one"), nb_str("two")];
let hm = ValueWord::from_hashmap_pairs(keys, values);
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::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(hm),
Constant::Int(2),
Constant::String("get".to_string()),
Constant::Number(1.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_str().unwrap(), "two");
}
#[test]
fn test_hashmap_shape_table_is_per_vm() {
use crate::VMConfig;
use crate::executor::VirtualMachine;
use shape_value::{SyncShapeTableScope, hash_property_name, shape_transition};
let vm_a = VirtualMachine::new(VMConfig::default());
let vm_b = VirtualMachine::new(VMConfig::default());
assert_eq!(vm_a.shape_table.table().lock().unwrap().shape_count(), 1);
assert_eq!(vm_b.shape_table.table().lock().unwrap().shape_count(), 1);
{
let _scope = SyncShapeTableScope::enter(vm_a.shape_table.clone());
let root = shape_value::ShapeTransitionTable::root();
let _ = shape_transition(root, hash_property_name("x"));
let _ = shape_transition(root, hash_property_name("y"));
}
let a_count = vm_a.shape_table.table().lock().unwrap().shape_count();
let b_count = vm_b.shape_table.table().lock().unwrap().shape_count();
assert!(
a_count >= 3, "vm_a shape table should have grown via the ambient handle (count={a_count})",
);
assert_eq!(
b_count, 1,
"vm_b shape table should be unaffected by transitions made under \
vm_a's scope (count={b_count})",
);
}
#[test]
fn test_hashmap_set_is_immutable() {
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::simple(OpCode::CallMethod),
Instruction::simple(OpCode::Pop), Instruction::new(OpCode::PushConst, Some(Operand::Const(0))), Instruction::new(OpCode::PushConst, Some(Operand::Const(5))), Instruction::new(OpCode::PushConst, Some(Operand::Const(6))), Instruction::simple(OpCode::CallMethod),
];
let constants = vec![
Constant::Value(test_hashmap()),
Constant::String("d".to_string()),
Constant::Number(4.0),
Constant::String("set".to_string()),
Constant::Number(2.0),
Constant::String("len".to_string()),
Constant::Number(0.0),
];
let result = execute_bytecode(instructions, constants).unwrap();
assert_eq!(result.as_i64(), Some(3));
}