mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
feat: TA 删除自身属性接统一数值键门三态裁决
This commit is contained in:
@@ -313,23 +313,42 @@ pub enum DeleteOutcome {
|
||||
/// 参与删除判定,原型链属性不影响结果。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 数组 length 虚拟属性:不可配置,返回 `NonConfigurable`
|
||||
/// 2. 数组下标元素:检查 configurable,`mark_hole_at` 标记为 hole
|
||||
/// 3. 命名属性:walk_own_keys 定位槽位,不可配置返回 `NonConfigurable`
|
||||
/// 4. 数组先保存元素区(值 + meta),重建命名属性后恢复元素区
|
||||
/// 1. TA 数值键:界内索引返回 `NonConfigurable`(元素零修改);数字无效键
|
||||
/// 返回 `Missing`;非数字串 / symbol 键落下方普通路径
|
||||
/// 2. 数组 length 虚拟属性:不可配置,返回 `NonConfigurable`
|
||||
/// 3. 数组下标元素:检查 configurable,`mark_hole_at` 标记为 hole
|
||||
/// 4. 命名属性:walk_own_keys 定位槽位,不可配置返回 `NonConfigurable`
|
||||
/// 5. 数组先保存元素区(值 + meta),重建命名属性后恢复元素区
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 键不在对象自身(含原型链属性)返回 `Missing`
|
||||
/// - 非 configurable 属性返回 `NonConfigurable`
|
||||
/// - TA 键 live 长 0(detach / 收缩越界)时全数值键归数字无效,删除成功
|
||||
/// - `key_si` 须已 intern
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 修改 obj 的 shape_id、属性表与 generation;数组元素区内容不变
|
||||
/// - TA 数字无效臂同步全局内置镜像槽为 undefined(非全局内置 TA 上为 no-op)
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 数组元素存在性以 `prop_meta_at` 的 hole 标记判定,删除后重新写入元素
|
||||
/// 会自动清除 hole 标记恢复存在
|
||||
pub fn delete_own_property_outcome<H: VmHost>(vm: &mut H, obj: &mut JsObject, key_si: u32) -> DeleteOutcome {
|
||||
// TA 数值键:界内索引不可配置(删除失败,元素零修改、镜像槽不同步);
|
||||
// 数字无效键视为缺失(删除成功,镜像槽同步 undefined,对非全局内置 TA
|
||||
// 为 no-op);非数字串 / symbol 键落下方普通属性路径。live 长 0
|
||||
// (detach / 收缩越界)时全数值键自然归数字无效,与读 / 写 / 定义面同口径。
|
||||
if obj.is_typed_array_obj() {
|
||||
match crate::typed_array::ta_index_gate(vm, obj, key_si) {
|
||||
crate::typed_array::TaIndexGate::NumericValid(_) => return DeleteOutcome::NonConfigurable,
|
||||
crate::typed_array::TaIndexGate::NumericInvalid => {
|
||||
vm.sync_global_builtin_mirror(obj, key_si, JsValue::undefined());
|
||||
return DeleteOutcome::Missing;
|
||||
}
|
||||
crate::typed_array::TaIndexGate::Ordinary => {}
|
||||
}
|
||||
}
|
||||
|
||||
// 数组下标元素在元素区,不参与 shape 链,单独删除(保持 length 不变)。
|
||||
if obj.is_array() {
|
||||
if let Some(index) = array_index_of(vm, key_si) {
|
||||
|
||||
@@ -1296,3 +1296,162 @@ fn ta_dop_get_dir_arm4() {
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ta_delete_valid_in_range_false() {
|
||||
// 界内索引删除失败(sloppy false):元素与 length 不变,原型同键抛 getter
|
||||
// 不被触达(界内臂不查自身命名属性也不走原型链)。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"(function () { var ta = new Int8Array([42, 43]); \
|
||||
Object.defineProperty(Int8Array.prototype, '0', { \
|
||||
get: function () { throw new Error('proto getter must not fire'); }, \
|
||||
configurable: true }); \
|
||||
var r0 = delete ta[0]; \
|
||||
var r1 = delete ta['0']; \
|
||||
var r2 = delete ta[1]; \
|
||||
return r0 === false && r1 === false && r2 === false \
|
||||
&& ta[0] === 42 && ta[1] === 43 && ta.length === 2; })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ta_delete_valid_strict_typeerror() {
|
||||
// 严格模式界内索引删除抛 TypeError(构造器断言);元素不动、length 不变。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"'use strict'; (function () { var ta = new Int8Array([42, 43]); \
|
||||
var thrown = false; \
|
||||
try { delete ta[1]; } \
|
||||
catch (e) { if (!(e instanceof TypeError)) return false; thrown = true; } \
|
||||
return thrown && ta[0] === 42 && ta[1] === 43 && ta.length === 2; })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ta_delete_int_minus_zero() {
|
||||
// -0 两相分臂:int 相(ta[-0] → 键 "0" 界内)删除失败;串相("-0" 数字
|
||||
// 无效)删除成功且不建属性;元素不动。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"(function () { var ta = new Int8Array([42]); \
|
||||
var rInt = delete ta[-0]; \
|
||||
var rStr = delete ta['-0']; \
|
||||
return rInt === false && rStr === true \
|
||||
&& ta[0] === 42 \
|
||||
&& !Object.prototype.hasOwnProperty.call(ta, '-0'); })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ta_delete_oob_true() {
|
||||
// 越界 / 负索引删除成功(true):元素不动、无自身属性、严格模式不抛。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"'use strict'; (function () { var ta = new Int8Array([42]); \
|
||||
var ok = delete ta[-1] && delete ta[1] && delete ta['-1'] && delete ta['1'] && delete ta['4294967295']; \
|
||||
var keys = ['-1', '1', '4294967295']; \
|
||||
for (var i = 0; i < keys.length; i++) { \
|
||||
if (Object.prototype.hasOwnProperty.call(ta, keys[i])) return false; } \
|
||||
return ok && ta[0] === 42 && ta.length === 1; })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ta_delete_numeric_invalid_strings() {
|
||||
// 数字无效串(非整数 / NaN / ±Infinity / "-0" 特例)删除恒成功:不建自身
|
||||
// 属性,原型同键抛 getter 不被触达(数字无效臂不走原型链)。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"(function () { var ta = new Int8Array([42]); \
|
||||
var keys = ['1.1', '0.000001', '-0', 'Infinity', 'NaN']; \
|
||||
for (var i = 0; i < keys.length; i++) { \
|
||||
Object.defineProperty(Int8Array.prototype, keys[i], { \
|
||||
get: function () { throw new Error('OrdinaryDelete was called'); }, \
|
||||
configurable: true }); } \
|
||||
for (var i = 0; i < keys.length; i++) { \
|
||||
if (!delete ta[keys[i]]) return false; \
|
||||
if (Object.prototype.hasOwnProperty.call(ta, keys[i])) return false; } \
|
||||
return ta[0] === 42; })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ta_delete_ordinary_own_property() {
|
||||
// 非 round-trip 串(Ordinary 臂)落真实自身属性路径:不可配置 accessor
|
||||
// 删除失败且 getter 保留可读;可配置数据属性删除成功、hasOwn 转假。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"(function () { var ta = new Int8Array(2); \
|
||||
var rDef = Object.defineProperty(ta, '1.0', { \
|
||||
get: function () { return 'g'; }, configurable: false }); \
|
||||
var d1 = delete ta['1.0']; \
|
||||
var d2 = delete ta['+1']; \
|
||||
Object.defineProperty(ta, '+1', { value: 9, configurable: true }); \
|
||||
var d3 = delete ta['+1']; \
|
||||
return rDef === ta && d1 === false && ta['1.0'] === 'g' \
|
||||
&& d2 === true && d3 === true \
|
||||
&& !Object.prototype.hasOwnProperty.call(ta, '+1'); })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ta_delete_symbol_and_reflect() {
|
||||
// symbol 键缺失删除成功、建自身属性后删除成功;Reflect.deleteProperty
|
||||
// 界内索引 false 不抛、越界 true(恒不抛契约)。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"(function () { var ta = new Int8Array([42]); \
|
||||
var s = Symbol('s'); \
|
||||
var r1 = delete ta[s]; \
|
||||
ta[s] = 'x'; \
|
||||
var r2 = delete ta[s]; \
|
||||
var r3 = Reflect.deleteProperty(ta, 0); \
|
||||
var r4 = Reflect.deleteProperty(ta, 9); \
|
||||
return r1 === true && r2 === true && r3 === false && r4 === true \
|
||||
&& ta[0] === 42; })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ta_delete_live_zero_all_true() {
|
||||
// detach 后 live 长 0:全数值键归数字无效,成员形与 Reflect 删除恒成功、
|
||||
// 无自身属性、不抛。
|
||||
let mut vm = Vm::new();
|
||||
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'); })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user