use std::collections::HashMap;
use crate::error::{StatorError, StatorResult};
use crate::objects::heap_object::HeapObject;
use crate::objects::value::JsValue;
#[derive(Debug, Default)]
pub struct JsWeakMap {
entries: HashMap<usize, JsValue>,
}
pub fn weak_map_new() -> JsWeakMap {
JsWeakMap::default()
}
pub fn weak_map_from_iterable(entries: Vec<(*mut HeapObject, JsValue)>) -> StatorResult<JsWeakMap> {
let mut map = weak_map_new();
for (key, value) in entries {
weak_map_set(&mut map, key, value)?;
}
Ok(map)
}
pub fn weak_map_set(map: &mut JsWeakMap, key: *mut HeapObject, value: JsValue) -> StatorResult<()> {
if key.is_null() {
return Err(StatorError::TypeError(
"WeakMap key must be an object".into(),
));
}
map.entries.insert(key as usize, value);
Ok(())
}
pub fn weak_map_get(map: &JsWeakMap, key: *mut HeapObject) -> JsValue {
if key.is_null() {
return JsValue::Undefined;
}
map.entries
.get(&(key as usize))
.cloned()
.unwrap_or(JsValue::Undefined)
}
pub fn weak_map_has(map: &JsWeakMap, key: *mut HeapObject) -> bool {
if key.is_null() {
return false;
}
map.entries.contains_key(&(key as usize))
}
pub fn weak_map_delete(map: &mut JsWeakMap, key: *mut HeapObject) -> bool {
if key.is_null() {
return false;
}
map.entries.remove(&(key as usize)).is_some()
}
pub fn weak_map_remove_object(map: &mut JsWeakMap, key: *mut HeapObject) {
if !key.is_null() {
map.entries.remove(&(key as usize));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_weak_map_set_and_get() {
let mut wm = weak_map_new();
let mut obj = HeapObject::new_null();
let key = &raw mut obj;
weak_map_set(&mut wm, key, JsValue::Smi(99)).unwrap();
assert_eq!(weak_map_get(&wm, key), JsValue::Smi(99));
}
#[test]
fn test_weak_map_get_missing_returns_undefined() {
let wm = weak_map_new();
let mut obj = HeapObject::new_null();
assert_eq!(weak_map_get(&wm, &raw mut obj), JsValue::Undefined);
}
#[test]
fn test_weak_map_has() {
let mut wm = weak_map_new();
let mut obj = HeapObject::new_null();
let key = &raw mut obj;
assert!(!weak_map_has(&wm, key));
weak_map_set(&mut wm, key, JsValue::Undefined).unwrap();
assert!(weak_map_has(&wm, key));
}
#[test]
fn test_weak_map_delete_existing() {
let mut wm = weak_map_new();
let mut obj = HeapObject::new_null();
let key = &raw mut obj;
weak_map_set(&mut wm, key, JsValue::Smi(1)).unwrap();
assert!(weak_map_delete(&mut wm, key));
assert!(!weak_map_has(&wm, key));
}
#[test]
fn test_weak_map_delete_missing_returns_false() {
let mut wm = weak_map_new();
let mut obj = HeapObject::new_null();
assert!(!weak_map_delete(&mut wm, &raw mut obj));
}
#[test]
fn test_weak_map_null_key_returns_error() {
let mut wm = weak_map_new();
assert!(weak_map_set(&mut wm, std::ptr::null_mut(), JsValue::Smi(1)).is_err());
}
#[test]
fn test_weak_map_null_key_get_returns_undefined() {
let wm = weak_map_new();
assert_eq!(weak_map_get(&wm, std::ptr::null_mut()), JsValue::Undefined);
}
#[test]
fn test_weak_map_null_key_has_returns_false() {
let wm = weak_map_new();
assert!(!weak_map_has(&wm, std::ptr::null_mut()));
}
#[test]
fn test_weak_map_null_key_delete_returns_false() {
let mut wm = weak_map_new();
assert!(!weak_map_delete(&mut wm, std::ptr::null_mut()));
}
#[test]
fn test_weak_map_from_iterable_populates_entries() {
let mut a = HeapObject::new_null();
let mut b = HeapObject::new_null();
let wm = weak_map_from_iterable(vec![
(&raw mut a, JsValue::Smi(1)),
(&raw mut b, JsValue::Smi(2)),
])
.unwrap();
assert_eq!(weak_map_get(&wm, &raw mut a), JsValue::Smi(1));
assert_eq!(weak_map_get(&wm, &raw mut b), JsValue::Smi(2));
}
#[test]
fn test_weak_map_from_iterable_rejects_null_key() {
let err =
weak_map_from_iterable(vec![(std::ptr::null_mut(), JsValue::Smi(1))]).unwrap_err();
assert!(matches!(err, StatorError::TypeError(_)));
}
#[test]
fn test_weak_map_keys_are_distinct_by_address() {
let mut wm = weak_map_new();
let mut a = HeapObject::new_null();
let mut b = HeapObject::new_null();
let ka = &raw mut a;
let kb = &raw mut b;
weak_map_set(&mut wm, ka, JsValue::Smi(1)).unwrap();
weak_map_set(&mut wm, kb, JsValue::Smi(2)).unwrap();
assert_eq!(weak_map_get(&wm, ka), JsValue::Smi(1));
assert_eq!(weak_map_get(&wm, kb), JsValue::Smi(2));
}
#[test]
fn test_weak_map_remove_object_clears_entry() {
let mut wm = weak_map_new();
let mut obj = HeapObject::new_null();
let key = &raw mut obj;
weak_map_set(&mut wm, key, JsValue::Boolean(true)).unwrap();
weak_map_remove_object(&mut wm, key);
assert!(!weak_map_has(&wm, key));
assert_eq!(weak_map_get(&wm, key), JsValue::Undefined);
}
#[test]
fn test_weak_map_remove_object_null_is_noop() {
let mut wm = weak_map_new();
weak_map_remove_object(&mut wm, std::ptr::null_mut());
}
#[test]
fn test_weak_map_overwrite_existing_key() {
let mut wm = weak_map_new();
let mut obj = HeapObject::new_null();
let key = &raw mut obj;
assert!(weak_map_set(&mut wm, key, JsValue::Smi(1)).is_ok());
assert!(weak_map_set(&mut wm, key, JsValue::Smi(2)).is_ok());
assert_eq!(weak_map_get(&wm, key), JsValue::Smi(2));
}
}