diff --git a/crates/oxide_vm/src/vm/props.rs b/crates/oxide_vm/src/vm/props.rs index 25ae1f09..4843d16b 100644 --- a/crates/oxide_vm/src/vm/props.rs +++ b/crates/oxide_vm/src/vm/props.rs @@ -249,9 +249,10 @@ impl Vm { false } - /// 查找自身属性槽下标:数组 length 虚拟属性返回 `None`,元素区返回下标, - /// shape 槽按数组(元素区之后偏移)与普通对象(槽位即下标)各自定位; - /// 未命中的槽返回 `None`。 + /// 查找自身属性槽下标:数组 length 虚拟属性返回 `None`,元素区返回下标 + /// (hole 视缺失),shape 槽命中即属性在场并返回存储下标(数组加元素区 + /// 偏移,普通对象槽位即下标);值可为显式 undefined,不参与存在性判定; + /// 未命中返回 `None`。 pub(crate) fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option { let length_si = self.length_si; if obj.is_array() && prop_name_si == length_si { @@ -271,13 +272,9 @@ impl Vm { .and_then(|pos| { if obj.is_array() { // 数组属性存储索引 = array_prop_count + shape 槽位(与元素区分)。 - let idx = obj.array_prop_count as usize + pos as usize; - let val = obj.get_prop_at(idx); - if !val.is_undefined() || obj.prop_vec_len() > idx { - Some(idx as u32) - } else { - None - } + // shape 槽命中即属性在场:删除重建保证槽随属性移除, + // 显式 undefined 值同样在场(规范存在性只看自身槽)。 + Some(obj.array_prop_count + pos) } else { let val = obj.get_prop_at(pos); if !val.is_undefined() || obj.prop_vec_len() > pos as usize { diff --git a/crates/oxide_vm/tests/builtins_typed_array_tests.rs b/crates/oxide_vm/tests/builtins_typed_array_tests.rs index bf5dfd8e..ea242872 100644 --- a/crates/oxide_vm/tests/builtins_typed_array_tests.rs +++ b/crates/oxide_vm/tests/builtins_typed_array_tests.rs @@ -352,6 +352,35 @@ fn in_operator_ordinary_object_zero_drift() { assert!(result.as_bool()); } +#[test] +fn in_operator_array_explicit_undefined_named_prop_present() { + // 数组显式 undefined 自有命名属性判在场:HasProperty 按自身槽存在性 + // 判定,值语义(显式 undefined)不参与;HasOwnProperty 同面。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var a = [1]; a.x = undefined; \ + ('x' in a) && a.hasOwnProperty('x') && Object.hasOwn(a, 'x') && \ + !('y' in a) && !a.hasOwnProperty('y')", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn in_operator_ordinary_explicit_undefined_named_prop_zero_drift() { + // 普通对象显式 undefined 命名槽零漂移守卫:`in`/hasOwnProperty 判在, + // 读值仍为 undefined。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var o = {}; o.x = undefined; \ + ('x' in o) && o.hasOwnProperty('x') && o.x === undefined", + ) + .unwrap(); + assert!(result.as_bool()); +} + #[test] fn typed_array_bigint_methods_receive_bigint_values() { let mut vm = Vm::new();