use crate::{
core::{
Expr, JSObjectDataPtr, PropertyKey, Value, evaluate_expr, evaluate_statements, extract_closure_from_value, new_js_object_data,
obj_get_key_value, obj_set_key_value,
},
error::JSError,
unicode::utf8_to_utf16,
};
use std::cell::RefCell;
use std::rc::Rc;
use crate::core::JSProxy;
pub(crate) fn handle_proxy_constructor(args: &[Expr], env: &JSObjectDataPtr) -> Result<Value, JSError> {
if args.len() != 2 {
return Err(raise_eval_error!(
"Proxy constructor requires exactly two arguments: target and handler"
));
}
let target = evaluate_expr(env, &args[0])?;
let handler = evaluate_expr(env, &args[1])?;
let proxy = Rc::new(RefCell::new(JSProxy {
target,
handler,
revoked: false,
}));
let proxy_obj = new_js_object_data();
proxy_obj.borrow_mut().insert(
PropertyKey::String("__proxy__".to_string()),
Rc::new(RefCell::new(Value::Proxy(proxy))),
);
Ok(Value::Object(proxy_obj))
}
pub(crate) fn handle_proxy_revocable(args: &[Expr], env: &JSObjectDataPtr) -> Result<Value, JSError> {
if args.len() != 2 {
return Err(raise_eval_error!(
"Proxy.revocable requires exactly two arguments: target and handler"
));
}
let target = evaluate_expr(env, &args[0])?;
let handler = evaluate_expr(env, &args[1])?;
let proxy = Rc::new(RefCell::new(JSProxy {
target: target.clone(),
handler: handler.clone(),
revoked: false,
}));
let revoke_proxy_ref = proxy.clone();
let revoke_body = vec![
crate::core::Statement::from(crate::core::StatementKind::Expr(crate::core::Expr::Call(
Box::new(crate::core::Expr::Var("__revoke_proxy".to_string(), None, None)),
Vec::new(),
))),
];
let revoke_env = new_js_object_data();
revoke_env
.borrow_mut()
.insert("__revoke_proxy".into(), Rc::new(RefCell::new(Value::Proxy(revoke_proxy_ref))));
let revoke_func = Value::Closure(Rc::new(crate::core::ClosureData::new(&[], &revoke_body, &revoke_env, None)));
let proxy_wrapper = new_js_object_data();
proxy_wrapper.borrow_mut().insert(
PropertyKey::String("__proxy__".to_string()),
Rc::new(RefCell::new(Value::Proxy(proxy))),
);
let result_obj = new_js_object_data();
obj_set_key_value(&result_obj, &"proxy".into(), Value::Object(proxy_wrapper))?;
obj_set_key_value(&result_obj, &"revoke".into(), revoke_func)?;
Ok(Value::Object(result_obj))
}
pub(crate) fn apply_proxy_trap(
proxy: &Rc<RefCell<JSProxy>>,
trap_name: &str,
args: Vec<Value>,
default_fn: impl FnOnce() -> Result<Value, JSError>,
) -> Result<Value, JSError> {
let proxy_borrow = proxy.borrow();
if proxy_borrow.revoked {
return Err(raise_eval_error!("Cannot perform operation on a revoked proxy"));
}
if let Value::Object(handler_obj) = &proxy_borrow.handler
&& let Some(trap_val) = obj_get_key_value(handler_obj, &trap_name.into())?
{
let trap = trap_val.borrow().clone();
if let Some((params, body, captured_env)) = extract_closure_from_value(&trap) {
let trap_env = crate::core::prepare_function_call_env(Some(&captured_env), None, Some(¶ms), &args, None, None)?;
return evaluate_statements(&trap_env, &body);
} else if matches!(trap, Value::Function(_)) {
} else {
}
}
default_fn()
}
pub(crate) fn proxy_get_property(proxy: &Rc<RefCell<JSProxy>>, key: &PropertyKey) -> Result<Option<Rc<RefCell<Value>>>, JSError> {
let result = apply_proxy_trap(
proxy,
"get",
vec![proxy.borrow().target.clone(), property_key_to_value(key)],
|| {
match &proxy.borrow().target {
Value::Object(obj) => {
let val_opt = obj_get_key_value(obj, key)?;
match val_opt {
Some(val_rc) => Ok(val_rc.borrow().clone()),
None => Ok(Value::Undefined),
}
}
_ => Ok(Value::Undefined), }
},
)?;
match result {
Value::Undefined => Ok(None),
val => Ok(Some(Rc::new(RefCell::new(val)))),
}
}
pub(crate) fn proxy_set_property(proxy: &Rc<RefCell<JSProxy>>, key: &PropertyKey, value: Value) -> Result<bool, JSError> {
let result = apply_proxy_trap(
proxy,
"set",
vec![proxy.borrow().target.clone(), property_key_to_value(key), value.clone()],
|| {
match &proxy.borrow().target {
Value::Object(obj) => {
obj_set_key_value(obj, key, value)?;
Ok(Value::Boolean(true))
}
_ => Ok(Value::Boolean(false)), }
},
)?;
match result {
Value::Boolean(b) => Ok(b),
_ => Ok(true), }
}
pub(crate) fn _proxy_has_property(proxy: &Rc<RefCell<JSProxy>>, key: &PropertyKey) -> Result<bool, JSError> {
let result = apply_proxy_trap(
proxy,
"has",
vec![proxy.borrow().target.clone(), property_key_to_value(key)],
|| {
match &proxy.borrow().target {
Value::Object(obj) => Ok(Value::Boolean(obj_get_key_value(obj, key)?.is_some())),
_ => Ok(Value::Boolean(false)), }
},
)?;
match result {
Value::Boolean(b) => Ok(b),
_ => Ok(false), }
}
pub(crate) fn proxy_delete_property(proxy: &Rc<RefCell<JSProxy>>, key: &PropertyKey) -> Result<bool, JSError> {
let result = apply_proxy_trap(
proxy,
"deleteProperty",
vec![proxy.borrow().target.clone(), property_key_to_value(key)],
|| {
match &proxy.borrow().target {
Value::Object(obj) => {
let mut obj_borrow = obj.borrow_mut();
let existed = obj_borrow.properties.contains_key(key);
obj_borrow.properties.shift_remove(key);
Ok(Value::Boolean(existed))
}
_ => Ok(Value::Boolean(false)), }
},
)?;
match result {
Value::Boolean(b) => Ok(b),
_ => Ok(false), }
}
fn property_key_to_value(key: &PropertyKey) -> Value {
match key {
PropertyKey::String(s) => Value::String(utf8_to_utf16(s)),
PropertyKey::Symbol(sym_rc) => sym_rc.borrow().clone(),
}
}