diff --git a/crates/oxide_vm/src/vm_props.rs b/crates/oxide_vm/src/vm_props.rs index cbfc892c..bce4908b 100644 --- a/crates/oxide_vm/src/vm_props.rs +++ b/crates/oxide_vm/src/vm_props.rs @@ -425,6 +425,10 @@ impl Vm { if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) { obj.set_prop_shape(pos, val); crate::ic_helper::write_ic_back(self.bytecode_mut(), ext_pc, obj.shape_id(), pos, 0); + } else if self.named_prop_create_needs_ordinary_set(obj, prop_name_si) { + // 数组 length / 整数索引键写新属性:分流回 ordinary_set(ArraySetLength / + // 元素区写),与 dispatch_ic_set_prop 对称,防快路径建影子槽破坏数组语义。 + self.ordinary_set(obj, prop_name_si, val, receiver)?; } else { self.create_named_prop_fast(obj, prop_name_si, val, receiver, ext_pc, false)?; } diff --git a/crates/oxide_vm/tests/member_write_ic_tests.rs b/crates/oxide_vm/tests/member_write_ic_tests.rs index 9f635718..2f6f4d19 100644 --- a/crates/oxide_vm/tests/member_write_ic_tests.rs +++ b/crates/oxide_vm/tests/member_write_ic_tests.rs @@ -186,3 +186,58 @@ fn proto_assignment_then_compound_shadow() { ); assert_eq!(r, "2:1", "__proto__ 赋值建链后复合写 shadow 到 o,p 不被污染"); } + +/// 数组 length 复合写分流 ordinary_set(ArraySetLength):`arr.length += 1` 更新逻辑 +/// 长度而非建影子槽,后续 push / 索引写读正常(P1-1 回归锚定)。 +#[test] +fn array_length_compound_write_uses_array_set_length() { + let (r, _hits, _misses) = run_once( + r#"var arr = [1, 2, 3]; + arr.length += 1; + var l1 = arr.length; + arr.push(5); + arr[1] = 9; + l1 + ":" + arr.length + ":" + arr.join(",")"#, + ); + assert_eq!(r, "4:5:1,9,3,,5", "length 复合写后 l1=4,push 后 length=5,元素区正确"); +} + +/// 数组 length 复合写后再直接赋值 `arr.length = N`:两写路径均走 ArraySetLength, +/// 元素区按新长度伸缩,不残留影子槽(P1-1 双路径对称回归锚定)。 +#[test] +fn array_length_compound_then_direct_assign() { + let (r, _hits, _misses) = run_once( + r#"var arr = [1, 2, 3]; + arr.length += 1; + arr.length = 5; + var l = arr.length; + arr[4] = 7; + l + ":" + arr.join(",")"#, + ); + assert_eq!(r, "5:1,2,3,,7", "length=5 后逻辑长度与元素区同步伸缩"); +} + +/// 数组写新命名属性(IC_SET 快路径建 shape 槽):命名槽在元素区之后,元素写入不错位。 +#[test] +fn array_named_prop_create_then_element_write() { + let (r, _hits, _misses) = run_once( + r#"var arr = [1]; + arr.foo = 1; + arr[0] = 9; + arr.foo"#, + ); + assert_eq!(r, "1", "元素区写入后命名属性 foo 不错位(槽在元素区之后)"); +} + +/// 数组命名属性 member 复合写命中 shadow 槽 + 元素增长后读回正确(P2-1)。 +#[test] +fn array_named_prop_compound_hit_after_element_growth() { + let (r, _hits, _misses) = run_once( + r#"var arr = [1]; + arr.foo = 2; + arr.foo += 3; + arr[0] = 9; + arr.foo"#, + ); + assert_eq!(r, "5", "member 复合写命中 shadow 槽(2+3=5),元素增长后仍不错位"); +}