mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: TA 自身属性描述符与 hasOwn 叶接统一数值键门
This commit is contained in:
@@ -994,10 +994,34 @@ pub fn object_define_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
|
||||
/// # 边界与前提
|
||||
/// - 键不在对象自身返回 `Ok(None)`
|
||||
/// - 模块命名空间未初始化导出返回 `Err`(ReferenceError,原值传播)
|
||||
/// - TypedArray 界内整数键返回常量四字段描述符(writable/enumerable/
|
||||
/// configurable 恒真,不读 buffer 状态);数字无效与非数字串键落槽位路径
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 在 epoch 分配一个描述符对象
|
||||
fn own_descriptor_of<H: VmHost>(vm: &mut H, obj: &JsObject, key_si: u32) -> Result<Option<JsValue>, 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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResu
|
||||
}
|
||||
|
||||
/// `Object.hasOwn(obj, key)`:对象是否有指定自身属性。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - TypedArray 界内整数键经统一数值键门预支恒 true,不查形状槽。
|
||||
pub fn object_has_own<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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));
|
||||
};
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user