javascript 0.1.13

A JavaScript engine implementation in Rust
Documentation
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::JSWeakMap;

/// Handle WeakMap constructor calls
pub(crate) fn handle_weakmap_constructor(args: &[Expr], env: &JSObjectDataPtr) -> Result<Value, JSError> {
    let weakmap = Rc::new(RefCell::new(JSWeakMap { entries: Vec::new() }));

    if !args.is_empty() {
        if args.len() == 1 {
            // WeakMap(iterable)
            initialize_weakmap_from_iterable(&weakmap, args, env)?;
        } else {
            return Err(raise_eval_error!("WeakMap constructor takes at most one argument"));
        }
    }

    Ok(Value::WeakMap(weakmap))
}

/// Initialize WeakMap from an iterable
fn initialize_weakmap_from_iterable(weakmap: &Rc<RefCell<JSWeakMap>>, 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(entry_val) = obj_get_key_value(&obj, &key.into())? {
                    let entry = entry_val.borrow().clone();
                    if let Value::Object(entry_obj) = entry
                        && let (Some(key_val), Some(value_val)) = (
                            obj_get_key_value(&entry_obj, &"0".into())?,
                            obj_get_key_value(&entry_obj, &"1".into())?,
                        )
                    {
                        let key_obj = key_val.borrow().clone();
                        let value_obj = value_val.borrow().clone();

                        // Check if key is an object
                        if let Value::Object(ref obj) = key_obj {
                            let weak_key = Rc::downgrade(obj);
                            weakmap.borrow_mut().entries.push((weak_key, value_obj));
                        } else {
                            return Err(raise_eval_error!("WeakMap keys must be objects"));
                        }
                    }
                } else {
                    break;
                }
                i += 1;
            }
        }
        _ => {
            return Err(raise_eval_error!("WeakMap constructor requires an iterable"));
        }
    }
    Ok(())
}

/// Check if WeakMap has a key and clean up dead entries
fn weakmap_has_key(weakmap: &Rc<RefCell<JSWeakMap>>, key_obj_rc: &JSObjectDataPtr) -> bool {
    let mut found = false;
    weakmap.borrow_mut().entries.retain(|(k, _)| {
        if let Some(strong_k) = k.upgrade() {
            if Rc::ptr_eq(key_obj_rc, &strong_k) {
                found = true;
            }
            true // Keep alive entries
        } else {
            false // Remove dead entries
        }
    });
    found
}

/// Delete a key from WeakMap and clean up dead entries
fn weakmap_delete_key(weakmap: &Rc<RefCell<JSWeakMap>>, key_obj_rc: &JSObjectDataPtr) -> bool {
    let mut deleted = false;
    weakmap.borrow_mut().entries.retain(|(k, _)| {
        if let Some(strong_k) = k.upgrade() {
            if Rc::ptr_eq(key_obj_rc, &strong_k) {
                deleted = true;
                false // Remove this entry
            } else {
                true // Keep other alive entries
            }
        } else {
            false // Remove dead entries
        }
    });
    deleted
}

/// Handle WeakMap instance method calls
pub(crate) fn handle_weakmap_instance_method(
    weakmap: &Rc<RefCell<JSWeakMap>>,
    method: &str,
    args: &[Expr],
    env: &JSObjectDataPtr,
) -> Result<Value, JSError> {
    match method {
        "set" => {
            if args.len() != 2 {
                return Err(raise_eval_error!("WeakMap.prototype.set requires exactly two arguments"));
            }
            let key = evaluate_expr(env, &args[0])?;
            let value = evaluate_expr(env, &args[1])?;

            // Check if key is an object
            let key_obj_rc = match key {
                Value::Object(ref obj) => obj.clone(),
                _ => return Err(raise_eval_error!("WeakMap keys must be objects")),
            };

            let weak_key = Rc::downgrade(&key_obj_rc);

            // Remove existing entry with same key (if still alive)
            weakmap.borrow_mut().entries.retain(|(k, _)| {
                if let Some(strong_k) = k.upgrade() {
                    !Rc::ptr_eq(&key_obj_rc, &strong_k)
                } else {
                    false // Remove dead entries
                }
            });

            // Add new entry
            weakmap.borrow_mut().entries.push((weak_key, value));

            Ok(Value::WeakMap(weakmap.clone()))
        }
        "get" => {
            if args.len() != 1 {
                return Err(raise_eval_error!("WeakMap.prototype.get requires exactly one argument"));
            }
            let key = evaluate_expr(env, &args[0])?;

            let key_obj_rc = match key {
                Value::Object(ref obj) => obj,
                _ => return Ok(Value::Undefined),
            };

            // Clean up dead entries and find the key
            let mut result = None;
            weakmap.borrow_mut().entries.retain(|(k, v)| {
                if let Some(strong_k) = k.upgrade() {
                    if Rc::ptr_eq(key_obj_rc, &strong_k) {
                        result = Some(v.clone());
                    }
                    true // Keep alive entries
                } else {
                    false // Remove dead entries
                }
            });

            Ok(result.unwrap_or(Value::Undefined))
        }
        "has" => {
            if args.len() != 1 {
                return Err(raise_eval_error!("WeakMap.prototype.has requires exactly one argument"));
            }
            let key = evaluate_expr(env, &args[0])?;

            let key_obj_rc = match key {
                Value::Object(ref obj) => obj,
                _ => return Ok(Value::Boolean(false)),
            };

            Ok(Value::Boolean(weakmap_has_key(weakmap, key_obj_rc)))
        }
        "delete" => {
            if args.len() != 1 {
                return Err(raise_eval_error!("WeakMap.prototype.delete requires exactly one argument"));
            }
            let key = evaluate_expr(env, &args[0])?;

            let key_obj_rc = match key {
                Value::Object(ref obj) => obj,
                _ => return Ok(Value::Boolean(false)),
            };

            Ok(Value::Boolean(weakmap_delete_key(weakmap, key_obj_rc)))
        }
        "toString" => {
            if !args.is_empty() {
                return Err(raise_eval_error!("WeakMap.prototype.toString takes no arguments"));
            }
            Ok(Value::String(utf8_to_utf16("[object WeakMap]")))
        }
        _ => Err(raise_eval_error!(format!("WeakMap.prototype.{} is not implemented", method))),
    }
}