From ff1abe01313d82f9e9e158e5218933e804b76023 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 23 Sep 2026 10:06:47 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20TA=20=E8=87=AA=E8=BA=AB=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E6=8F=8F=E8=BF=B0=E7=AC=A6=E4=B8=8E=20hasOwn=20?= =?UTF-8?q?=E5=8F=B6=E6=8E=A5=E7=BB=9F=E4=B8=80=E6=95=B0=E5=80=BC=E9=94=AE?= =?UTF-8?q?=E9=97=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/object.rs | 65 +++++ .../tests/builtins_typed_array_tests.rs | 230 +++++++++++++++++- 2 files changed, 286 insertions(+), 9 deletions(-) diff --git a/crates/oxide_builtins/src/object.rs b/crates/oxide_builtins/src/object.rs index ab13c418..8c64b08c 100644 --- a/crates/oxide_builtins/src/object.rs +++ b/crates/oxide_builtins/src/object.rs @@ -994,10 +994,34 @@ pub fn object_define_property(vm: &mut H, args: &[u8]) -> NativeResul /// # 边界与前提 /// - 键不在对象自身返回 `Ok(None)` /// - 模块命名空间未初始化导出返回 `Err`(ReferenceError,原值传播) +/// - TypedArray 界内整数键返回常量四字段描述符(writable/enumerable/ +/// configurable 恒真,不读 buffer 状态);数字无效与非数字串键落槽位路径 /// /// # 副作用 /// - 在 epoch 分配一个描述符对象 fn own_descriptor_of(vm: &mut H, obj: &JsObject, key_si: u32) -> Result, JsValue> { + // TA 数值键:界内索引返回元素值 + 常量四字段描述符(writable/enumerable/ + // configurable 恒真);数字无效与非数字串键落下方槽位路径(真实 own 属性 + // 原样返回,无属性则 None)。 + if obj.is_typed_array_obj() { + if let crate::typed_array::TaIndexGate::NumericValid(index) = crate::typed_array::ta_index_gate(vm, obj, key_si) + { + let value = crate::typed_array::typed_array_element_get(vm, obj, index).unwrap_or(JsValue::undefined()); + let desc = alloc_desc_object(vm); + let sh_ptr = vm.kernel_core().shape_forge().as_ref() as *const ShapeForge; + let sf_ptr = vm.kernel_core().perm_interner().as_ref() as *const PermInterner; + // SAFETY: desc 为本函数刚分配的 epoch 对象;sh/sf 为 kernel 永久引用。 + let d: &mut JsObject = unsafe { &mut *desc }; + let sh = unsafe { &*sh_ptr }; + let sf = unsafe { &*sf_ptr }; + push_desc_prop(d, sh, sf.intern("value").0, value); + push_desc_prop(d, sh, sf.intern("writable").0, JsValue::bool(true)); + push_desc_prop(d, sh, sf.intern("enumerable").0, JsValue::bool(true)); + push_desc_prop(d, sh, sf.intern("configurable").0, JsValue::bool(true)); + return Ok(Some(JsValue::from_js_object(desc))); + } + } + // 数组 length 是虚拟属性(无 shape 槽,ordinary_get 直接返回逻辑长度): // 描述符 {value: len, writable: !frozen && 非显式收窄, enumerable: false, // configurable: false}。冻结数组与经 defineProperty 收窄的数组 writable=false。 @@ -1525,6 +1549,9 @@ pub fn object_set_prototype_of(vm: &mut H, args: &[u8]) -> NativeResu } /// `Object.hasOwn(obj, key)`:对象是否有指定自身属性。 +/// +/// # 边界与前提 +/// - TypedArray 界内整数键经统一数值键门预支恒 true,不查形状槽。 pub fn object_has_own(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 3 { return NativeResult::Ok(JsValue::bool(false)); @@ -1532,6 +1559,16 @@ pub fn object_has_own(vm: &mut H, args: &[u8]) -> NativeResult { let obj_ptr = native_try!(require_obj_arg(vm, args, "hasOwn")); let key_si = vm.property_key_si(vm.reg(args[2])); let obj = unsafe { &*obj_ptr }; + // 统一数值键门(exotic [[HasOwnProperty]]):界内整数键恒存在;数字无效 + // 与非数字串键落下方槽位判定。 + if obj.is_typed_array_obj() + && matches!( + crate::typed_array::ta_index_gate(vm, obj, key_si), + crate::typed_array::TaIndexGate::NumericValid(_) + ) + { + return NativeResult::Ok(JsValue::bool(true)); + } NativeResult::Ok(JsValue::bool(vm.get_own_property_slot(obj, key_si).is_some())) } @@ -1849,6 +1886,9 @@ pub fn object_proto_to_string(vm: &mut H, args: &[u8]) -> NativeResul /// # 步骤 /// 1. 先 ToPropertyKey 求键(spec 顺序:键先于 ToObject)。 /// 2. ToObject 装箱 this(null/undefined 抛 TypeError,原始值装箱后查 own 槽)。 +/// +/// # 边界与前提 +/// - TypedArray 界内整数键经统一数值键门预支恒 true,不查形状槽。 pub fn object_proto_has_own_property(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::bool(false)); @@ -1868,6 +1908,17 @@ pub fn object_proto_has_own_property(vm: &mut H, args: &[u8]) -> Nati Err(msg) => return NativeResult::Err(crate::error::create_type_error(vm, &msg)), }; let obj = unsafe { &*obj_val.as_js_object_ptr() }; + // 统一数值键门(exotic [[HasOwnProperty]]):界内整数键恒存在;数字无效 + // 与非数字串键落下方槽位判定(命名空间检查在门臂之后,TA 非命名空间 + // 不触达)。 + if obj.is_typed_array_obj() + && matches!( + crate::typed_array::ta_index_gate(vm, obj, key_si), + crate::typed_array::TaIndexGate::NumericValid(_) + ) + { + return NativeResult::Ok(JsValue::bool(true)); + } if vm.get_own_property_slot(obj, key_si).is_none() { return NativeResult::Ok(JsValue::bool(false)); } @@ -1883,6 +1934,10 @@ pub fn object_proto_has_own_property(vm: &mut H, args: &[u8]) -> Nati /// # 步骤 /// 1. 先 ToPropertyKey 求键。 /// 2. ToObject 装箱 this(null/undefined 抛 TypeError)。 +/// +/// # 边界与前提 +/// - TypedArray 界内整数键经统一数值键门预支恒 true(enumerable 恒真), +/// 不读 meta。 pub fn object_proto_property_is_enumerable(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::bool(false)); @@ -1902,6 +1957,16 @@ pub fn object_proto_property_is_enumerable(vm: &mut H, args: &[u8]) - Err(msg) => return NativeResult::Err(crate::error::create_type_error(vm, &msg)), }; let obj = unsafe { &*obj_val.as_js_object_ptr() }; + // 统一数值键门(exotic PropertyIsEnumerable):界内整数键恒可枚举; + // 数字无效与非数字串键落下方 meta 读取。 + if obj.is_typed_array_obj() + && matches!( + crate::typed_array::ta_index_gate(vm, obj, key_si), + crate::typed_array::TaIndexGate::NumericValid(_) + ) + { + return NativeResult::Ok(JsValue::bool(true)); + } let Some(pos) = vm.get_own_property_slot(obj, key_si) else { return NativeResult::Ok(JsValue::bool(false)); }; diff --git a/crates/oxide_vm/tests/builtins_typed_array_tests.rs b/crates/oxide_vm/tests/builtins_typed_array_tests.rs index 37d3a21d..8f117d54 100644 --- a/crates/oxide_vm/tests/builtins_typed_array_tests.rs +++ b/crates/oxide_vm/tests/builtins_typed_array_tests.rs @@ -1442,15 +1442,227 @@ fn ta_delete_live_zero_all_true() { let result = eval( &mut vm, "(function () { var ab = new ArrayBuffer(8); var ta = new Int8Array(ab); \ - ta[0] = 7; \ - ab.transfer(); \ - var r1 = delete ta[0]; \ - var r2 = delete ta['-0']; \ - var r3 = delete ta['1.1']; \ - var r4 = delete ta['1']; \ - var r5 = Reflect.deleteProperty(ta, 0); \ - return r1 === true && r2 === true && r3 === true && r4 === true && r5 === true \ - && !Object.prototype.hasOwnProperty.call(ta, '0'); })()", + ta[0] = 7; \ + ab.transfer(); \ + var r1 = delete ta[0]; \ + var r2 = delete ta['-0']; \ + var r3 = delete ta['1.1']; \ + var r4 = delete ta['1']; \ + var r5 = Reflect.deleteProperty(ta, 0); \ + return r1 === true && r2 === true && r3 === true && r4 === true && r5 === true \ + && !Object.prototype.hasOwnProperty.call(ta, '0'); })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn ta_gopd_index_desc() { + // gOPD 界内整数键:元素值 + 常量四字段(writable/enumerable/configurable + // 恒真,不读 buffer 状态);BigInt 族同形。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var ta = new Int8Array([42, 7]); \ + var d = Object.getOwnPropertyDescriptor(ta, '0'); \ + var di = Object.getOwnPropertyDescriptor(ta, 0); \ + if (!d || d.value !== 42 || d.writable !== true \ + || d.enumerable !== true || d.configurable !== true) return false; \ + if (!di || di.value !== 42 || di.writable !== true \ + || di.enumerable !== true || di.configurable !== true) return false; \ + var b = new BigInt64Array([42n, 43n]); \ + var db = Object.getOwnPropertyDescriptor(b, 1); \ + return !!db && db.value === 43n && db.writable === true \ + && db.enumerable === true && db.configurable === true; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn ta_gopd_oob_numeric_undefined() { + // 数字无效键(越界/负/"-0"/分数)gOPD 恒 undefined:不建属性、不查形状。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var ta = new Int8Array([42]); \ + var keys = ['-1', '-42', '1', '42', '-0', '1.1', '0.1']; \ + for (var i = 0; i < keys.length; i++) { \ + if (Object.getOwnPropertyDescriptor(ta, keys[i]) !== undefined) return false; } \ + return true; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn ta_gopd_ordinary_real_prop() { + // 非规范数字串键落真实 own 属性路径:完整描述符原样返回;无定义键 + // undefined(非 fall-through 吞属性)。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var ta = new Int8Array([42]); \ + Object.defineProperty(ta, '1.0', { value: 7 }); \ + var d = Object.getOwnPropertyDescriptor(ta, '1.0'); \ + if (!d || d.value !== 7 || d.writable !== false \ + || d.enumerable !== false || d.configurable !== false) return false; \ + return Object.getOwnPropertyDescriptor(ta, 'undef') === undefined; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn ta_gopd_symbol_real_prop() { + // symbol 键真实 own 属性:定义后 gOPD 返回其描述符,无定义 undefined。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var ta = new Int8Array([42]); \ + var s = Symbol('s'); \ + if (Object.getOwnPropertyDescriptor(ta, s) !== undefined) return false; \ + Object.defineProperty(ta, s, { value: 'x', enumerable: true, configurable: false }); \ + var d = Object.getOwnPropertyDescriptor(ta, s); \ + return !!d && d.value === 'x' && d.writable === false \ + && d.enumerable === true && d.configurable === false; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn ta_gopd_detached_undefined() { + // detach 后 live 长 0:全数值键 gOPD 恒 undefined(元素臂不触达)。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var ab = new ArrayBuffer(8); var ta = new Int8Array(ab); \ + ta[0] = 9; \ + ab.transfer(); \ + return Object.getOwnPropertyDescriptor(ta, 0) === undefined \ + && Object.getOwnPropertyDescriptor(ta, '0') === undefined \ + && Object.getOwnPropertyDescriptor(ta, '-1') === undefined; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn ta_gopd_subarray_view_offset() { + // subarray 视图偏移:视图下标 0 的元素是底层偏移后第 2 个元素(与 + // 元素读同口径)。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var base = new Int8Array([11, 22, 33]); \ + var view = base.subarray(1); \ + var d0 = Object.getOwnPropertyDescriptor(view, 0); \ + var d1 = Object.getOwnPropertyDescriptor(view, 1); \ + return !!d0 && d0.value === 22 && !!d1 && d1.value === 33; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn ta_gopd_length_undefined_unchanged() { + // TA length 非 own 属性(经原型访问器读):gOPD('length') 恒 undefined, + // 防 TA 臂误吞 length 键。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var ta = new Int8Array([42]); \ + return Object.getOwnPropertyDescriptor(ta, 'length') === undefined; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn ta_gopd_int_key_same_arm() { + // int 键与规范串键同臂:描述符值一致(ToKey 后同路)。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var ta = new Int8Array([11, 22]); \ + var di = Object.getOwnPropertyDescriptor(ta, 1); \ + var ds = Object.getOwnPropertyDescriptor(ta, '1'); \ + return !!di && !!ds && di.value === 22 && ds.value === 22 \ + && di.value === ds.value; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn ta_hasown_index_true() { + // hasOwn 面:界内整数键(int 与规范串)恒 true;数字无效键("-0"/分数) + // 无属性 false。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var ta = new Int8Array([42, 7]); \ + if (ta.hasOwnProperty(0) !== true) return false; \ + if (ta.hasOwnProperty('1') !== true) return false; \ + if (Object.hasOwn(ta, 0) !== true) return false; \ + if (ta.hasOwnProperty('-0') !== false) return false; \ + if (ta.hasOwnProperty('1.1') !== false) return false; \ + return ta.hasOwnProperty(2) === false; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn ta_pie_index_true() { + // propertyIsEnumerable 面:界内整数键恒可枚举;越界与数字无效键 false。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var ta = new Int8Array([42]); \ + if (ta.propertyIsEnumerable(0) !== true) return false; \ + if (ta.propertyIsEnumerable('0') !== true) return false; \ + if (ta.propertyIsEnumerable('1.1') !== false) return false; \ + return ta.propertyIsEnumerable(5) === false; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn ta_reflect_gopd() { + // Reflect.getOwnPropertyDescriptor 经共享核消费 TA 臂:界内键元素值, + // 越界与数字无效键 undefined。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var ta = new Int8Array([42]); \ + var d = Reflect.getOwnPropertyDescriptor(ta, 0); \ + if (!d || d.value !== 42 || d.writable !== true \ + || d.enumerable !== true || d.configurable !== true) return false; \ + return Reflect.getOwnPropertyDescriptor(ta, 9) === undefined \ + && Reflect.getOwnPropertyDescriptor(ta, '-1') === undefined; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn ta_gopd_real_prop_attrs_passthrough() { + // Ordinary 键真属性四字段原样返回(非常量四真):钉 fall-through 槽位路径 + // 不被 TA 臂覆盖——configurable 为 false 即非常量描述符。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var ta = new Int8Array([42]); \ + Object.defineProperty(ta, '1.0', { \ + value: 7, writable: true, enumerable: true, configurable: false }); \ + var d = Object.getOwnPropertyDescriptor(ta, '1.0'); \ + if (!d || d.value !== 7) return false; \ + if (d.writable !== true || d.enumerable !== true || d.configurable !== false) return false; \ + return ta.hasOwnProperty('1.0') === true \ + && ta.propertyIsEnumerable('1.0') === true; })()", ) .unwrap(); assert!(result.as_bool());