Files
OxideJS-New/crates/oxide_builtins/src/reflect.rs
T

287 lines
12 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::mem::P;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::object::{delete_own_property, key_si_to_js_value, own_symbol_key_values, walk_own_keys};
use oxide_runtime_api::{NativeResult, VmHost};
/// `Reflect.apply(target, thisArgument, argumentsList)`:以指定 this 与参数数组调用函数。
pub fn reflect_apply<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target = arg(vm, args, 1);
let this_arg = arg(vm, args, 2);
let arg_list = arg(vm, args, 3);
if !is_callable(target) {
return type_error(vm, "Reflect.apply target is not callable");
}
let Some(call_args) = array_like_elements(arg_list) else {
return type_error(vm, "Reflect.apply argumentsList must be an array-like object");
};
match vm.call_function_sync(target, this_arg, &call_args) {
Ok(value) => NativeResult::Ok(value),
Err(err) => type_error(vm, &err),
}
}
/// `Reflect.construct(target, argumentsList, newTarget)`:以 newTarget 的 prototype
/// 分配 this 后调用 target;返回值非对象时回退到新建的 this。
pub fn reflect_construct<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target = arg(vm, args, 1);
let arg_list = arg(vm, args, 2);
let new_target = if args.len() > 3 { arg(vm, args, 3) } else { target };
if !is_callable(target) {
return type_error(vm, "Reflect.construct target is not callable");
}
let new_target_ptr = if new_target.is_object() {
new_target.as_js_object_ptr()
} else {
std::ptr::null_mut()
};
if new_target_ptr.is_null() || !unsafe { &*new_target_ptr }.is_function() {
return type_error(vm, "Reflect.construct newTarget is not a constructor");
}
// 不可构造:箭头函数、native 方法(非构造器,OBJ_TYPE_CONSTRUCTOR 标记的除外)。
let nt = unsafe { &*new_target_ptr };
if nt.is_arrow() || (nt.native_fn().is_some() && nt.type_tag != oxide_types::object::JsObject::OBJ_TYPE_CONSTRUCTOR)
{
return type_error(vm, "Reflect.construct newTarget is not a constructor");
}
let proto_si = vm.kernel_core().perm_interner().intern("prototype").0;
let proto_val = match vm.resolve_property(unsafe { &*new_target_ptr }, proto_si) {
Some(p) if p.is_object() => p,
_ => JsValue::from_js_object(P::as_ptr(&vm.session().builtin_world().object_proto) as *mut JsObject),
};
let this_ptr = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
let this_val = JsValue::from_js_object(this_ptr);
let call_args = array_like_elements(arg_list).unwrap_or_default();
// ponytail: construct by binding the freshly allocated `this` to a plain call;
// new.target is not propagated into target. Upgrade path: expose the VM's
// [[Construct]] frame (constructed_this/new_target in vm.rs) through VmHost.
match vm.call_function_sync(target, this_val, &call_args) {
Ok(ret) if ret.is_object() => NativeResult::Ok(ret),
Ok(_) => NativeResult::Ok(this_val),
Err(err) => NativeResult::Err(crate::error::create_type_error(vm, &err)),
}
}
/// `Reflect.defineProperty(target, key, descriptor)`:按 descriptor 定义属性,
/// 数据/访问器属性混用或非法 getter/setter 返回 false。
///
/// # 注意事项
/// - 与 `Object.defineProperty` 共用 `define_from_descriptor`:缺失字段回填现有
/// 属性、模块命名空间 exotic 收窄语义一处生效;普通失败投影为布尔 false。
/// - 数组 length 的非法值(`"RangeError: "` 前缀)与强转失败(`"TypeError: "`
/// 前缀)按规范抛对应错误,不投影为 false。
/// - 强转期用户代码抛出的原始异常经 VM 专用槽原值重抛。
pub fn reflect_define_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.defineProperty target is not an object");
};
let desc_val = arg(vm, args, 3);
let Some(_) = object_ptr(desc_val) else {
return type_error(vm, "Reflect.defineProperty descriptor is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
match crate::object::define_from_descriptor(vm, target_ptr, key_si, desc_val) {
Ok(()) => NativeResult::Ok(JsValue::bool(true)),
Err(msg) => {
// 强转期用户代码(valueOf / Symbol.toPrimitive)抛出的异常值转存专用槽,
// 须原值重抛,不得投影为 false 或改写成引擎错误。
if let Some(exc) = vm.take_pending_length_exception() {
return NativeResult::Err(exc);
}
match crate::error::split_kinded(&msg) {
Some((kind, rest)) => NativeResult::Err(crate::error::create_kind_error(vm, kind, rest)),
None => NativeResult::Ok(JsValue::bool(false)),
}
}
}
}
/// `Reflect.deleteProperty(target, key)`:删除自身属性;
/// 属性不可配置时返回 false,否则重构属性表并返回 true。
pub fn reflect_delete_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.deleteProperty target is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
let target = unsafe { &mut *target_ptr };
NativeResult::Ok(JsValue::bool(delete_own_property(vm, target, key_si)))
}
/// `Reflect.get(target, key, receiver)`:读取属性(含原型链与 accessor)。
pub fn reflect_get<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.get target is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
let receiver = if args.len() > 3 { vm.reg(args[3]) } else { target_val };
match vm.ordinary_get(unsafe { &*target_ptr }, key_si, receiver) {
Ok(value) => NativeResult::Ok(value),
Err(err) => type_error(vm, &err),
}
}
/// `Reflect.getOwnPropertyDescriptor(target, key)`:复用 Object 同名实现。
pub fn reflect_get_own_property_descriptor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(_) = object_ptr(target_val) else {
return type_error(vm, "Reflect.getOwnPropertyDescriptor target is not an object");
};
crate::object::object_get_own_property_descriptor(vm, args)
}
/// `Reflect.getPrototypeOf(target)`:返回对象的 prototype。
pub fn reflect_get_prototype_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.getPrototypeOf target is not an object");
};
NativeResult::Ok(unsafe { &*target_ptr }.proto())
}
/// `Reflect.has(target, key)`:属性是否存在(含原型链)。
pub fn reflect_has<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.has target is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
NativeResult::Ok(JsValue::bool(vm.resolve_property(unsafe { &*target_ptr }, key_si).is_some()))
}
/// `Reflect.isExtensible(target)`:对象是否可扩展。
pub fn reflect_is_extensible<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.isExtensible target is not an object");
};
NativeResult::Ok(JsValue::bool(unsafe { &*target_ptr }.is_extensible()))
}
/// `Reflect.ownKeys(target)`:返回对象全部自身属性键,字符串键在前、Symbol 键在后。
///
/// # 步骤
/// 1. 收集字符串键与整数键(整数升序,其余插入序)。
/// 2. 追加自身 Symbol 键(保持插入序),符合 OrdinaryOwnPropertyKeys 的排序。
///
/// # 副作用
/// - 结果数组分配在当前 epoch。
pub fn reflect_own_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.ownKeys target is not an object");
};
let target = unsafe { &*target_ptr };
// Symbol 键分流在消费端追加:底层字符串枚举源(Object.keys / JSON / for-in)
// 语义不变,不泄漏 Symbol 键。
let keys = walk_own_keys(vm, target);
let symbols = own_symbol_key_values(vm, target);
let n = keys.len() + symbols.len();
let array_proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
let arr = vm.alloc_object(JsObject::new_array(
EMPTY_SHAPE_ID,
JsValue::from_js_object(array_proto),
n,
vm.epoch().bump(),
));
for (i, (si, _)) in keys.iter().enumerate() {
let key_val = key_si_to_js_value(vm, *si);
unsafe {
(*arr).set_prop_at(i, key_val);
}
}
let base = keys.len();
for (j, sym) in symbols.iter().enumerate() {
unsafe {
(*arr).set_prop_at(base + j, *sym);
}
}
unsafe {
(*arr).set_prop_count(n);
}
NativeResult::Ok(JsValue::from_js_object(arr))
}
/// `Reflect.preventExtensions(target)`:阻止扩展,返回 true。
pub fn reflect_prevent_extensions<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.preventExtensions target is not an object");
};
unsafe { &mut *target_ptr }.set_extensible(false);
NativeResult::Ok(JsValue::bool(true))
}
/// `Reflect.set(target, key, value, receiver)`:写入属性;
/// 不可扩展且属性不存在时返回 false。
pub fn reflect_set<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.set target is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
let value = arg(vm, args, 3);
let receiver = if args.len() > 4 { vm.reg(args[4]) } else { target_val };
let target = unsafe { &mut *target_ptr };
if !target.is_extensible() && vm.get_own_property_slot(target, key_si).is_none() {
return NativeResult::Ok(JsValue::bool(false));
}
NativeResult::Ok(JsValue::bool(vm.ordinary_set(target, key_si, value, receiver, true).is_ok()))
}
/// `Reflect.setPrototypeOf(target, proto)`:设置 prototype(对象或 null)。
/// 不可扩展且新旧原型不同时返回 false;新旧相同返回 true。
pub fn reflect_set_prototype_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.setPrototypeOf target is not an object");
};
let proto = arg(vm, args, 2);
if !proto.is_object() && !proto.is_null() {
return type_error(vm, "Reflect.setPrototypeOf prototype must be an object or null");
}
let target = unsafe { &mut *target_ptr };
// OrdinarySetPrototypeOf 步 2-4:新旧相同先于可扩展判定,直接成功。
if !target.is_extensible() && target.proto() != proto {
return NativeResult::Ok(JsValue::bool(false));
}
NativeResult::Ok(JsValue::bool(target.set_proto(proto).is_ok()))
}
fn arg<H: VmHost>(vm: &H, args: &[u8], idx: usize) -> JsValue {
args.get(idx).map(|reg| vm.reg(*reg)).unwrap_or_else(JsValue::undefined)
}
fn object_ptr(value: JsValue) -> Option<*mut JsObject> {
if !value.is_object() {
return None;
}
let ptr = value.as_js_object_ptr();
(!ptr.is_null()).then_some(ptr)
}
fn is_callable(value: JsValue) -> bool {
object_ptr(value).is_some_and(|ptr| unsafe { &*ptr }.is_function())
}
fn type_error<H: VmHost>(vm: &mut H, message: &str) -> NativeResult {
NativeResult::Err(crate::error::create_type_error(vm, message))
}
fn array_like_elements(value: JsValue) -> Option<Vec<JsValue>> {
let ptr = object_ptr(value)?;
let obj = unsafe { &*ptr };
Some((0..obj.prop_count() as usize).map(|idx| obj.get_prop_at(idx)).collect())
}