mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: Reflect ownKeys 族收敛规范枚举与原值传播
This commit is contained in:
@@ -11,12 +11,13 @@ use oxide_runtime_api::{NativeResult, VmHost};
|
||||
|
||||
use crate::builtins_debug;
|
||||
|
||||
/// 判断字符串是否为规范整数索引:非空、无前导零、全数字且值 < 2^32。
|
||||
/// 判断字符串是否为规范整数索引:非空、无前导零、全数字且值 <= 2^32-2
|
||||
/// (array index 上限,2^32-1 落字符串键组)。
|
||||
fn is_integer_index(key: &str) -> bool {
|
||||
if key.is_empty() || key.len() > 1 && key.as_bytes()[0] == b'0' {
|
||||
return false;
|
||||
}
|
||||
key.bytes().all(|b| b.is_ascii_digit()) && key.parse::<u64>().unwrap_or(u64::MAX) < (1u64 << 32)
|
||||
key.bytes().all(|b| b.is_ascii_digit()) && key.parse::<u64>().unwrap_or(u64::MAX) <= (1u64 << 32) - 2
|
||||
}
|
||||
|
||||
/// 收集对象全部自身属性(数组元素区 + shape 链),按规范顺序排列:整数索引在前
|
||||
@@ -635,12 +636,17 @@ pub fn object_create<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if proto_val.is_null() {
|
||||
let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||
if args.len() >= 3 && !vm.reg(args[2]).is_undefined() {
|
||||
if let Err(msg) = define_all_from_properties(vm, obj, vm.reg(args[2])) {
|
||||
// 强转期用户异常原值重抛;define 失败文本按 kind 前缀恢复原类型。
|
||||
if let Some(exc) = vm.take_pending_length_exception() {
|
||||
return NativeResult::Err(exc);
|
||||
match define_all_from_properties(vm, obj, vm.reg(args[2])) {
|
||||
Ok(()) => {}
|
||||
// 描述符字段读取期用户异常原值重抛。
|
||||
Err(DefineDescFailure::User(exc)) => return NativeResult::Err(exc),
|
||||
Err(DefineDescFailure::Engine(msg)) => {
|
||||
// 强转期用户异常原值重抛;define 失败文本按 kind 前缀恢复原类型。
|
||||
if let Some(exc) = vm.take_pending_length_exception() {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
return NativeResult::Err(crate::error::create_define_failure(vm, &msg));
|
||||
}
|
||||
return NativeResult::Err(crate::error::create_define_failure(vm, &msg));
|
||||
}
|
||||
}
|
||||
return NativeResult::Ok(JsValue::from_js_object(obj));
|
||||
@@ -653,12 +659,17 @@ pub fn object_create<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||
if args.len() >= 3 && !vm.reg(args[2]).is_undefined() {
|
||||
if let Err(msg) = define_all_from_properties(vm, obj, vm.reg(args[2])) {
|
||||
// 强转期用户异常原值重抛;define 失败文本按 kind 前缀恢复原类型。
|
||||
if let Some(exc) = vm.take_pending_length_exception() {
|
||||
return NativeResult::Err(exc);
|
||||
match define_all_from_properties(vm, obj, vm.reg(args[2])) {
|
||||
Ok(()) => {}
|
||||
// 描述符字段读取期用户异常原值重抛。
|
||||
Err(DefineDescFailure::User(exc)) => return NativeResult::Err(exc),
|
||||
Err(DefineDescFailure::Engine(msg)) => {
|
||||
// 强转期用户异常原值重抛;define 失败文本按 kind 前缀恢复原类型。
|
||||
if let Some(exc) = vm.take_pending_length_exception() {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
return NativeResult::Err(crate::error::create_define_failure(vm, &msg));
|
||||
}
|
||||
return NativeResult::Err(crate::error::create_define_failure(vm, &msg));
|
||||
}
|
||||
}
|
||||
NativeResult::Ok(JsValue::from_js_object(obj))
|
||||
@@ -738,6 +749,20 @@ pub fn object_is<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::bool(oxide_runtime_api::same_value(lhs, rhs)))
|
||||
}
|
||||
|
||||
/// 描述符定义通道的失败原因:`Engine` 为 DefineOwnProperty 失败文本
|
||||
/// (kind 前缀约定不变,调用方按既有口径恢复);`User` 为描述符字段读取期
|
||||
/// 用户代码抛出的原始异常值,原值重抛。
|
||||
pub(crate) enum DefineDescFailure {
|
||||
Engine(String),
|
||||
User(JsValue),
|
||||
}
|
||||
|
||||
impl From<JsValue> for DefineDescFailure {
|
||||
fn from(exc: JsValue) -> Self {
|
||||
DefineDescFailure::User(exc)
|
||||
}
|
||||
}
|
||||
|
||||
/// 按 ToPropertyDescriptor 语义把描述符定义/修改到对象的自身属性上。
|
||||
///
|
||||
/// 处理数据(value/writable)与访问器(get/set)两类描述符;描述符字段沿原型
|
||||
@@ -755,11 +780,15 @@ pub fn object_is<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 修改 obj 的 shape 链、属性表与 generation
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 描述符字段读取期用户代码(accessor getter)抛出的异常经
|
||||
/// `DefineDescFailure::User` 原值传播,调用方原值重抛。
|
||||
pub(crate) fn define_from_descriptor<H: VmHost>(
|
||||
vm: &mut H, obj_ptr: *mut JsObject, key_si: u32, desc_val: JsValue,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<(), DefineDescFailure> {
|
||||
if !desc_val.is_object() {
|
||||
return Err("Property description must be an object".to_string());
|
||||
return Err(DefineDescFailure::Engine("Property description must be an object".to_string()));
|
||||
}
|
||||
|
||||
let value_si = vm.kernel_core().perm_interner().intern("value").0;
|
||||
@@ -769,12 +798,13 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
|
||||
let enumerable_si = vm.kernel_core().perm_interner().intern("enumerable").0;
|
||||
let configurable_si = vm.kernel_core().perm_interner().intern("configurable").0;
|
||||
|
||||
let value_field = own_field(vm, desc_val, value_si);
|
||||
let get_field = own_field(vm, desc_val, get_si);
|
||||
let set_field = own_field(vm, desc_val, set_si);
|
||||
let writable_field = own_field(vm, desc_val, writable_si);
|
||||
let enumerable_field = own_field(vm, desc_val, enumerable_si);
|
||||
let configurable_field = own_field(vm, desc_val, configurable_si);
|
||||
// 六字段按规范序逐个 GetV,读取期异常原值传播(ReturnIfAbrupt)。
|
||||
let value_field = own_field(vm, desc_val, value_si)?;
|
||||
let get_field = own_field(vm, desc_val, get_si)?;
|
||||
let set_field = own_field(vm, desc_val, set_si)?;
|
||||
let writable_field = own_field(vm, desc_val, writable_si)?;
|
||||
let enumerable_field = own_field(vm, desc_val, enumerable_si)?;
|
||||
let configurable_field = own_field(vm, desc_val, configurable_si)?;
|
||||
|
||||
// 数组 length 是无 shape 槽的虚拟数据属性:需与普通已有属性一样参与描述符
|
||||
// 缺省回填(writable 保持当前值、value 保持当前长度),其当前描述符由元素
|
||||
@@ -809,15 +839,17 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
|
||||
// 与 accessor 的收窄由 define_* 的不可配置守卫承担。
|
||||
if unsafe { &*obj_ptr }.is_module_namespace() && !is_symbol_key(key_si) {
|
||||
let Some(pos) = existing_pos else {
|
||||
return Err("Cannot define property on module namespace: not an export".to_string());
|
||||
return Err(DefineDescFailure::Engine(
|
||||
"Cannot define property on module namespace: not an export".to_string(),
|
||||
));
|
||||
};
|
||||
if writable_field.is_some_and(|w| !oxide_runtime_api::to_boolean(w)) {
|
||||
return Err("Cannot redefine module namespace export".to_string());
|
||||
return Err(DefineDescFailure::Engine("Cannot redefine module namespace export".to_string()));
|
||||
}
|
||||
if let Some(v) = value_field {
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
if !oxide_runtime_api::same_value(v, obj.get_prop_at(pos)) {
|
||||
return Err("Cannot redefine module namespace export".to_string());
|
||||
return Err(DefineDescFailure::Engine("Cannot redefine module namespace export".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -833,7 +865,9 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
|
||||
let has_data = value_field.is_some() || writable_field.is_some();
|
||||
let has_accessor = get_field.is_some() || set_field.is_some();
|
||||
if has_data && has_accessor {
|
||||
return Err("Invalid property descriptor: cannot mix data and accessor fields".to_string());
|
||||
return Err(DefineDescFailure::Engine(
|
||||
"Invalid property descriptor: cannot mix data and accessor fields".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let existing_value = if is_array_length {
|
||||
@@ -861,7 +895,9 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
|
||||
.unwrap_or(JsValue::undefined())
|
||||
});
|
||||
if (!get.is_undefined() && !is_callable(get)) || (!set.is_undefined() && !is_callable(set)) {
|
||||
return Err("accessor descriptor get/set must be callable or undefined".to_string());
|
||||
return Err(DefineDescFailure::Engine(
|
||||
"accessor descriptor get/set must be callable or undefined".to_string(),
|
||||
));
|
||||
}
|
||||
(get, set)
|
||||
} else {
|
||||
@@ -880,19 +916,22 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
|
||||
|| enumerable_field.is_some_and(|e| !oxide_runtime_api::to_boolean(e))
|
||||
|| configurable_field.is_some_and(|c| !oxide_runtime_api::to_boolean(c))
|
||||
{
|
||||
return Err(
|
||||
return Err(DefineDescFailure::Engine(
|
||||
"cannot define property: TypedArray index only accepts a writable, enumerable, configurable data descriptor"
|
||||
.to_string(),
|
||||
);
|
||||
));
|
||||
}
|
||||
let value = value_field.unwrap_or_else(|| {
|
||||
crate::typed_array::typed_array_element_get(vm, unsafe { &*obj_ptr }, index)
|
||||
.unwrap_or(JsValue::undefined())
|
||||
});
|
||||
return crate::typed_array::typed_array_element_define(vm, unsafe { &mut *obj_ptr }, index, value);
|
||||
return crate::typed_array::typed_array_element_define(vm, unsafe { &mut *obj_ptr }, index, value)
|
||||
.map_err(DefineDescFailure::Engine);
|
||||
}
|
||||
crate::typed_array::TaIndexGate::NumericInvalid => {
|
||||
return Err("cannot define property: TypedArray index out of range".to_string());
|
||||
return Err(DefineDescFailure::Engine(
|
||||
"cannot define property: TypedArray index out of range".to_string(),
|
||||
));
|
||||
}
|
||||
crate::typed_array::TaIndexGate::Ordinary => {}
|
||||
}
|
||||
@@ -900,7 +939,8 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
|
||||
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
if has_accessor {
|
||||
vm.define_accessor_property(obj, key_si, get, set, PropAttributes::new(false, enumerable, configurable))?;
|
||||
vm.define_accessor_property(obj, key_si, get, set, PropAttributes::new(false, enumerable, configurable))
|
||||
.map_err(DefineDescFailure::Engine)?;
|
||||
} else if has_data {
|
||||
let value = if has_existing {
|
||||
value_field.unwrap_or(existing_value)
|
||||
@@ -914,7 +954,8 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
|
||||
} else {
|
||||
writable_field.map(oxide_runtime_api::to_boolean).unwrap_or(false)
|
||||
};
|
||||
vm.define_data_property(obj, key_si, value, PropAttributes::new(writable, enumerable, configurable))?;
|
||||
vm.define_data_property(obj, key_si, value, PropAttributes::new(writable, enumerable, configurable))
|
||||
.map_err(DefineDescFailure::Engine)?;
|
||||
} else {
|
||||
if !has_existing {
|
||||
vm.define_data_property(
|
||||
@@ -922,7 +963,8 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
|
||||
key_si,
|
||||
JsValue::undefined(),
|
||||
PropAttributes::new(false, enumerable, configurable),
|
||||
)?;
|
||||
)
|
||||
.map_err(DefineDescFailure::Engine)?;
|
||||
return Ok(());
|
||||
}
|
||||
let is_accessor = existing_meta.map(|m| m.is_accessor).unwrap_or(false);
|
||||
@@ -931,9 +973,11 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
|
||||
if is_accessor {
|
||||
let get = existing_meta.map(|m| m.get).unwrap_or(JsValue::undefined());
|
||||
let set = existing_meta.map(|m| m.set).unwrap_or(JsValue::undefined());
|
||||
vm.define_accessor_property(obj, key_si, get, set, attrs)?;
|
||||
vm.define_accessor_property(obj, key_si, get, set, attrs)
|
||||
.map_err(DefineDescFailure::Engine)?;
|
||||
} else {
|
||||
vm.define_data_property(obj, key_si, existing_value, attrs)?;
|
||||
vm.define_data_property(obj, key_si, existing_value, attrs)
|
||||
.map_err(DefineDescFailure::Engine)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -956,12 +1000,16 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 修改 target 的 shape 链、属性表与 generation
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 描述符值读取期(getter)与字段读取期用户代码抛出的异常经
|
||||
/// `DefineDescFailure::User` 原值传播,调用方原值重抛。
|
||||
fn define_all_from_properties<H: VmHost>(
|
||||
vm: &mut H, target_ptr: *mut JsObject, props_val: JsValue,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<(), DefineDescFailure> {
|
||||
// ToObject:原始值装箱;null/undefined 的 TypeError 文本经调用方 kind
|
||||
// 前缀恢复后原类型抛出。
|
||||
let props_obj = oxide_runtime_api::to_object(props_val, vm)?;
|
||||
let props_obj = oxide_runtime_api::to_object(props_val, vm).map_err(DefineDescFailure::Engine)?;
|
||||
let props_ptr = props_obj.as_js_object_ptr();
|
||||
let descriptors: Vec<(u32, JsValue)> = {
|
||||
let props = unsafe { &*props_ptr };
|
||||
@@ -973,7 +1021,9 @@ fn define_all_from_properties<H: VmHost>(
|
||||
let props = unsafe { &*props_ptr };
|
||||
vm.ordinary_get(props, key_si, props_obj).map(|desc_val| (key_si, desc_val))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
// 描述符值 getter 抛出的异常原值传播。
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|msg| DefineDescFailure::User(crate::array::from_engine_error(vm, &msg)))?
|
||||
};
|
||||
for (key_si, desc_val) in descriptors {
|
||||
define_from_descriptor(vm, target_ptr, key_si, desc_val)?;
|
||||
@@ -1000,13 +1050,18 @@ pub fn object_define_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
|
||||
// 保证与计算属性访问、Reflect.defineProperty 等读键路径一致。
|
||||
let si = vm.property_key_si(vm.reg(args[2]));
|
||||
|
||||
if let Err(msg) = define_from_descriptor(vm, obj_ptr, si, vm.reg(args[3])) {
|
||||
// 强转期用户代码(valueOf / Symbol.toPrimitive)抛出的异常值转存专用槽,
|
||||
// 须原值重抛而非改写成引擎 TypeError。
|
||||
if let Some(exc) = vm.take_pending_length_exception() {
|
||||
return NativeResult::Err(exc);
|
||||
match define_from_descriptor(vm, obj_ptr, si, vm.reg(args[3])) {
|
||||
Ok(()) => {}
|
||||
// 描述符字段读取期用户代码(accessor getter)抛出的异常原值重抛。
|
||||
Err(DefineDescFailure::User(exc)) => return NativeResult::Err(exc),
|
||||
Err(DefineDescFailure::Engine(msg)) => {
|
||||
// 强转期用户代码(valueOf / Symbol.toPrimitive)抛出的异常值转存专用槽,
|
||||
// 须原值重抛而非改写成引擎 TypeError。
|
||||
if let Some(exc) = vm.take_pending_length_exception() {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
return NativeResult::Err(crate::error::create_define_failure(vm, &msg));
|
||||
}
|
||||
return NativeResult::Err(crate::error::create_define_failure(vm, &msg));
|
||||
}
|
||||
NativeResult::Ok(obj_val)
|
||||
}
|
||||
@@ -1184,12 +1239,18 @@ pub fn object_get_own_property_descriptors<H: VmHost>(vm: &mut H, args: &[u8]) -
|
||||
|
||||
/// 按 ToPropertyDescriptor 语义取描述符字段:沿原型链判存在性,存在时经
|
||||
/// ordinary_get 取值(触发 accessor getter,receiver 为描述符对象本身)。
|
||||
fn own_field<H: VmHost>(vm: &mut H, desc: JsValue, prop_si: u32) -> Option<JsValue> {
|
||||
/// 用户代码(accessor getter)抛出的异常原值传播。
|
||||
fn own_field<H: VmHost>(vm: &mut H, desc: JsValue, prop_si: u32) -> Result<Option<JsValue>, JsValue> {
|
||||
// ordinary_get 对缺失属性返回 undefined,无法区分"不存在"与"值为
|
||||
// undefined",故先用 resolve_property 沿原型链判 HasProperty。
|
||||
let obj = unsafe { &*desc.as_js_object_ptr() };
|
||||
vm.resolve_property(obj, prop_si)?;
|
||||
vm.ordinary_get(obj, prop_si, desc).ok()
|
||||
let Some(_) = vm.resolve_property(obj, prop_si) else {
|
||||
return Ok(None);
|
||||
};
|
||||
match vm.ordinary_get(obj, prop_si, desc) {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
Err(msg) => Err(crate::array::from_engine_error(vm, &msg)),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_callable(value: JsValue) -> bool {
|
||||
@@ -1492,13 +1553,18 @@ pub fn object_define_properties<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeRes
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "Object.defineProperties called on non-object"));
|
||||
}
|
||||
let target_ptr = target_val.as_js_object_ptr();
|
||||
if let Err(msg) = define_all_from_properties(vm, target_ptr, vm.reg(args[2])) {
|
||||
// 与单属性入口一致:强转期用户异常原值重抛,非法 length 以 RangeError
|
||||
// kind 穿透,其余 define 失败投影为 TypeError。
|
||||
if let Some(exc) = vm.take_pending_length_exception() {
|
||||
return NativeResult::Err(exc);
|
||||
match define_all_from_properties(vm, target_ptr, vm.reg(args[2])) {
|
||||
Ok(()) => {}
|
||||
// 与单属性入口一致:描述符字段读取期用户异常原值重抛。
|
||||
Err(DefineDescFailure::User(exc)) => return NativeResult::Err(exc),
|
||||
Err(DefineDescFailure::Engine(msg)) => {
|
||||
// 强转期用户异常原值重抛,非法 length 以 RangeError kind 穿透,
|
||||
// 其余 define 失败投影为 TypeError。
|
||||
if let Some(exc) = vm.take_pending_length_exception() {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
return NativeResult::Err(crate::error::create_define_failure(vm, &msg));
|
||||
}
|
||||
return NativeResult::Err(crate::error::create_define_failure(vm, &msg));
|
||||
}
|
||||
NativeResult::Ok(target_val)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
use oxide_types::object::{JsObject, PropMetaEntry};
|
||||
use oxide_types::private_key::is_int_key;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use crate::array::{arraylike_get, from_engine_error, is_constructor_value};
|
||||
@@ -11,6 +12,12 @@ use oxide_runtime_api::{to_length, NativeResult, VmHost};
|
||||
const MAX_PROTO_CHAIN_DEPTH: usize = 1024;
|
||||
|
||||
/// `Reflect.apply(target, thisArgument, argumentsList)`:以指定 this 与参数数组调用函数。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. IsCallable(target) 为 false → TypeError;argumentsList 非对象 → TypeError。
|
||||
/// 2. CreateListFromArrayLike:规范读 length(访问器异常原值传播)、ToLength、
|
||||
/// 逐索引规范 Get。
|
||||
/// 3. Call(target, thisArgument, args);调用异常原值传播。
|
||||
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);
|
||||
@@ -18,12 +25,30 @@ pub fn reflect_apply<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if !is_callable(target) {
|
||||
return type_error(vm, "Reflect.apply target is not callable");
|
||||
}
|
||||
let Some(call_args) = array_like_elements(arg_list) else {
|
||||
let Some(arg_list_ptr) = object_ptr(arg_list) else {
|
||||
return type_error(vm, "Reflect.apply argumentsList must be an array-like object");
|
||||
};
|
||||
// SAFETY: object_ptr 保证指针非空且指向存活对象。
|
||||
let arg_list_obj = unsafe { &*arg_list_ptr };
|
||||
|
||||
// CreateListFromArrayLike:规范读 length(访问器异常原值传播),ToLength 纯函数不抛。
|
||||
let length_si = vm.kernel_core().perm_interner().intern("length").0;
|
||||
let len_val = match vm.ordinary_get(arg_list_obj, length_si, arg_list) {
|
||||
Ok(v) => v,
|
||||
Err(msg) => return NativeResult::Err(from_engine_error(vm, &msg)),
|
||||
};
|
||||
let len = to_length(len_val) as usize;
|
||||
let mut call_args = Vec::with_capacity(len);
|
||||
for i in 0..len {
|
||||
match arraylike_get(vm, arg_list_ptr, i) {
|
||||
Ok(v) => call_args.push(v),
|
||||
Err(exc) => return NativeResult::Err(exc),
|
||||
}
|
||||
}
|
||||
|
||||
match vm.call_function_sync(target, this_arg, &call_args) {
|
||||
Ok(value) => NativeResult::Ok(value),
|
||||
Err(err) => type_error(vm, &err),
|
||||
Err(err) => NativeResult::Err(from_engine_error(vm, &err)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +128,9 @@ pub fn reflect_define_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResu
|
||||
|
||||
match crate::object::define_from_descriptor(vm, target_ptr, key_si, desc_val) {
|
||||
Ok(()) => NativeResult::Ok(JsValue::bool(true)),
|
||||
Err(msg) => {
|
||||
// 描述符字段读取期用户代码(accessor getter)抛出的异常原值传播。
|
||||
Err(crate::object::DefineDescFailure::User(exc)) => NativeResult::Err(exc),
|
||||
Err(crate::object::DefineDescFailure::Engine(msg)) => {
|
||||
// 强转期用户代码(valueOf / Symbol.toPrimitive)抛出的异常值转存专用槽,
|
||||
// 须原值重抛,不得投影为 false 或改写成引擎错误。
|
||||
if let Some(exc) = vm.take_pending_length_exception() {
|
||||
@@ -147,7 +174,8 @@ pub fn reflect_get<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
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),
|
||||
// 访问器 getter 抛出的异常原值传播(ReturnIfAbrupt)。
|
||||
Err(err) => NativeResult::Err(from_engine_error(vm, &err)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +228,8 @@ pub fn reflect_is_extensible<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
/// `Reflect.ownKeys(target)`:返回对象全部自身属性键,字符串键在前、Symbol 键在后。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 收集字符串键与整数键(整数升序,其余插入序)。
|
||||
/// 1. 收集字符串键与整数键(整数升序,其余插入序);数组目标注入虚拟
|
||||
/// `length`(字符串组首)。
|
||||
/// 2. 追加自身 Symbol 键(保持插入序),符合 OrdinaryOwnPropertyKeys 的排序。
|
||||
///
|
||||
/// # 副作用
|
||||
@@ -214,7 +243,15 @@ pub fn reflect_own_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
|
||||
// Symbol 键分流在消费端追加:底层字符串枚举源(Object.keys / JSON / for-in)
|
||||
// 语义不变,不泄漏 Symbol 键。
|
||||
let keys = walk_own_keys(vm, target);
|
||||
let mut keys = walk_own_keys(vm, target);
|
||||
// 数组 length 是虚拟自身属性(无 shape 槽、不在形状链):注入字符串组首
|
||||
// (创建序第一枚字符串键),存储下标哨兵 u32::MAX = 无槽位(消费端只读
|
||||
// si,不读存储下标)。
|
||||
if target.is_array() {
|
||||
let length_si = vm.kernel_core().perm_interner().intern("length").0;
|
||||
let insert_at = keys.iter().position(|(si, _)| !is_int_key(*si)).unwrap_or(keys.len());
|
||||
keys.insert(insert_at, (length_si, u32::MAX));
|
||||
}
|
||||
let symbols = own_symbol_key_values(vm, target);
|
||||
let n = keys.len() + symbols.len();
|
||||
|
||||
@@ -453,9 +490,3 @@ fn is_callable(value: JsValue) -> bool {
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -367,3 +367,106 @@ fn construct_direct_new_derived_array_guard() {
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
// 数组 length 是虚拟自身属性:ownKeys 注入字符串组首(创建序第一枚字符串键)。
|
||||
#[test]
|
||||
fn reflect_own_keys_array_includes_virtual_length() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var k1 = Reflect.ownKeys([]); \
|
||||
var k2 = Reflect.ownKeys([,,2]); \
|
||||
var a = []; a.x = 1; var k3 = Reflect.ownKeys(a); \
|
||||
k1.join(',') === 'length' && k2.join(',') === '2,length' && k3.join(',') === 'length,x'",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
// array index 上限 2^32-2:4294967294 落索引组(整数升序在前),
|
||||
// 4294967295 落字符串组(创建序)。
|
||||
#[test]
|
||||
fn reflect_own_keys_large_index_boundary() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var o = { b: 1, a: 2, 4294967294: 3, 4294967295: 4 }; \
|
||||
Reflect.ownKeys(o).join(',')",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(to_str(&vm, result), "4294967294,b,a,4294967295");
|
||||
}
|
||||
|
||||
// Reflect.get 的 own 与 inherited 访问器异常两臂均原值传播。
|
||||
#[test]
|
||||
fn reflect_get_propagates_accessor_exception_original_value() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var boom = { value: 1 }; \
|
||||
var own = { get p() { throw boom; } }; \
|
||||
var caughtOwn; \
|
||||
try { Reflect.get(own, 'p'); caughtOwn = 'none'; } catch (e) { caughtOwn = e; } \
|
||||
var inherited = { q: 1 }; \
|
||||
Object.defineProperty(inherited, 'q', { get() { throw boom; }, configurable: true }); \
|
||||
var sub = Object.create(inherited); \
|
||||
var caughtInh; \
|
||||
try { Reflect.get(sub, 'q'); caughtInh = 'none'; } catch (e) { caughtInh = e; } \
|
||||
caughtOwn === boom && caughtInh === boom",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
// apply 的 argumentsList 按 CreateListFromArrayLike 读取:length 经 getter,
|
||||
// 逐索引规范 Get;函数对象命名槽(prototype/length/name)不被误读。
|
||||
#[test]
|
||||
fn reflect_apply_arguments_list_array_like() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"function fn() { return [arguments.length, arguments[0]]; } \
|
||||
var list = Object.create(null); \
|
||||
Object.defineProperty(list, 'length', { get: function() { return 1; } }); \
|
||||
var r = Reflect.apply(fn, null, list); \
|
||||
r[0] === 1 && r[1] === undefined",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
// apply 的 length getter 异常原值传播;非对象 argumentsList 抛 TypeError。
|
||||
#[test]
|
||||
fn reflect_apply_propagates_length_getter_original_value() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var boom = 'X'; \
|
||||
var list = { get length() { throw boom; } }; \
|
||||
var caught; \
|
||||
try { Reflect.apply(function(){}, null, list); caught = 'none'; } catch (e) { caught = e; } \
|
||||
var thrown; \
|
||||
try { Reflect.apply(function(){}, null, 42); thrown = 'none'; } catch (e) { thrown = (e instanceof TypeError); } \
|
||||
caught === boom && thrown === true",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
// defineProperty 描述符字段 getter 抛出的异常原值传播(Reflect 与 Object 两侧同通道)。
|
||||
#[test]
|
||||
fn define_property_propagates_attribute_getter_original_value() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var boom = 'X'; \
|
||||
var desc = { get enumerable() { throw boom; } }; \
|
||||
var caught; \
|
||||
try { Reflect.defineProperty({}, 'a', desc); caught = 'none'; } catch (e) { caught = e; } \
|
||||
var caughtObj; \
|
||||
try { Object.defineProperty({}, 'a', desc); caughtObj = 'none'; } catch (e) { caughtObj = e; } \
|
||||
caught === boom && caughtObj === boom",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
@@ -2073,12 +2073,13 @@ fn ta_reflect_has_subarray_view() {
|
||||
|
||||
#[test]
|
||||
fn ta_keys_int_index_2pow32_boundary() {
|
||||
// 2^32 边界:2^32-1 为规范数组索引(升序前置),2^32 起为串键(插入序)。
|
||||
// 2^32 边界:2^32-2 为规范数组索引上限(升序前置),2^32-1 起为串键
|
||||
// (创建序)。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"(function () { var ks1 = Object.keys({ a: 1, '4294967295': 2 }); \
|
||||
if (ks1.join(',') !== '4294967295,a') return false; \
|
||||
"(function () { var ks1 = Object.keys({ a: 1, '4294967294': 2, '4294967295': 3 }); \
|
||||
if (ks1.join(',') !== '4294967294,a,4294967295') return false; \
|
||||
var ks2 = Object.keys({ b: 1, '4294967296': 2 }); \
|
||||
return ks2.join(',') === 'b,4294967296'; })()",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user