use crate::{
core::{Expr, JSObjectDataPtr, Value, evaluate_expr, obj_get_key_value},
error::JSError,
unicode::utf8_to_utf16,
};
use std::cell::RefCell;
use std::rc::Rc;
use crate::core::JSWeakSet;
pub(crate) fn handle_weakset_constructor(args: &[Expr], env: &JSObjectDataPtr) -> Result<Value, JSError> {
let weakset = Rc::new(RefCell::new(JSWeakSet { values: Vec::new() }));
if !args.is_empty() {
if args.len() == 1 {
initialize_weakset_from_iterable(&weakset, args, env)?;
} else {
return Err(raise_eval_error!("WeakSet constructor takes at most one argument"));
}
}
Ok(Value::WeakSet(weakset))
}
fn initialize_weakset_from_iterable(weakset: &Rc<RefCell<JSWeakSet>>, args: &[Expr], env: &JSObjectDataPtr) -> Result<(), JSError> {
let iterable = evaluate_expr(env, &args[0])?;
match iterable {
Value::Object(obj) => {
let mut i = 0;
loop {
let key = format!("{}", i);
if let Some(value_val) = obj_get_key_value(&obj, &key.into())? {
let value = value_val.borrow().clone();
if let Value::Object(ref obj) = value {
let weak_value = Rc::downgrade(obj);
weakset.borrow_mut().values.push(weak_value);
} else {
return Err(raise_eval_error!("WeakSet values must be objects"));
}
} else {
break;
}
i += 1;
}
}
_ => {
return Err(raise_eval_error!("WeakSet constructor requires an iterable"));
}
}
Ok(())
}
fn weakset_has_value(weakset: &Rc<RefCell<JSWeakSet>>, value_obj_rc: &JSObjectDataPtr) -> bool {
let mut found = false;
weakset.borrow_mut().values.retain(|v| {
if let Some(strong_v) = v.upgrade() {
if Rc::ptr_eq(value_obj_rc, &strong_v) {
found = true;
}
true } else {
false }
});
found
}
fn weakset_delete_value(weakset: &Rc<RefCell<JSWeakSet>>, value_obj_rc: &JSObjectDataPtr) -> bool {
let mut deleted = false;
weakset.borrow_mut().values.retain(|v| {
if let Some(strong_v) = v.upgrade() {
if Rc::ptr_eq(value_obj_rc, &strong_v) {
deleted = true;
false } else {
true }
} else {
false }
});
deleted
}
pub(crate) fn handle_weakset_instance_method(
weakset: &Rc<RefCell<JSWeakSet>>,
method: &str,
args: &[Expr],
env: &JSObjectDataPtr,
) -> Result<Value, JSError> {
match method {
"add" => {
if args.len() != 1 {
return Err(raise_eval_error!("WeakSet.prototype.add requires exactly one argument"));
}
let value = evaluate_expr(env, &args[0])?;
let value_obj_rc = match value {
Value::Object(ref obj) => obj.clone(),
_ => return Err(raise_eval_error!("WeakSet values must be objects")),
};
let weak_value = Rc::downgrade(&value_obj_rc);
weakset.borrow_mut().values.retain(|v| {
if let Some(strong_v) = v.upgrade() {
!Rc::ptr_eq(&value_obj_rc, &strong_v)
} else {
false }
});
weakset.borrow_mut().values.push(weak_value);
Ok(Value::WeakSet(weakset.clone()))
}
"has" => {
if args.len() != 1 {
return Err(raise_eval_error!("WeakSet.prototype.has requires exactly one argument"));
}
let value = evaluate_expr(env, &args[0])?;
let value_obj_rc = match value {
Value::Object(ref obj) => obj,
_ => return Ok(Value::Boolean(false)),
};
Ok(Value::Boolean(weakset_has_value(weakset, value_obj_rc)))
}
"delete" => {
if args.len() != 1 {
return Err(raise_eval_error!("WeakSet.prototype.delete requires exactly one argument"));
}
let value = evaluate_expr(env, &args[0])?;
let value_obj_rc = match value {
Value::Object(ref obj) => obj,
_ => return Ok(Value::Boolean(false)),
};
Ok(Value::Boolean(weakset_delete_value(weakset, value_obj_rc)))
}
"toString" => {
if !args.is_empty() {
return Err(raise_eval_error!("WeakSet.prototype.toString takes no arguments"));
}
Ok(Value::String(utf8_to_utf16("[object WeakSet]")))
}
_ => Err(raise_eval_error!(format!("WeakSet.prototype.{} is not implemented", method))),
}
}