From ad1b1f85753b49ac6f4eae36aab9513678b162f3 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 7 Jun 2026 19:47:49 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20for-in=20=E8=AF=AD?= =?UTF-8?q?=E6=B3=95=E5=8E=9F=E5=9E=8B=E9=93=BE=E4=B8=A2=E5=A4=B1=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/src/vm.rs | 42 ++++++++++++++--------- crates/oxide_vm/tests/regression_tests.rs | 9 +++++ 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 2759b543..d3fde230 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -2333,28 +2333,38 @@ impl Vm { if !obj_val.is_object() { return Err("TypeError: for-in right-hand side is not an object".into()); } - let obj = unsafe { &*obj_val.as_js_object_ptr() }; - let shape_id = obj.shape_id(); let mut keys_vec = bumpalo::collections::Vec::new_in(self.epoch.bump()); - let mut cursor = Some(shape_id); - while let Some(id) = cursor { - if id == oxide_kernel::shape_forge::EMPTY_SHAPE_ID { + let mut seen = std::collections::HashSet::new(); + let mut current = obj_val; + + loop { + if !current.is_object() { break; } - if let Some(shape) = self.kernel.shape_forge().get_shape(id) { - if shape.property_name != u32::MAX { - let hash = self - .kernel - .string_forge() - .get_hash(shape.property_name) - .unwrap_or(0); - keys_vec.push(JsValue::string(shape.property_name, hash)); + let cur = unsafe { &*current.as_js_object_ptr() }; + let mut cursor = Some(cur.shape_id()); + while let Some(id) = cursor { + if id == EMPTY_SHAPE_ID { + break; + } + if let Some(shape) = self.kernel.shape_forge().get_shape(id) { + if shape.property_name != u32::MAX + && seen.insert(shape.property_name) + { + let hash = self + .kernel + .string_forge() + .get_hash(shape.property_name) + .unwrap_or(0); + keys_vec.push(JsValue::string(shape.property_name, hash)); + } + cursor = shape.parent; + } else { + break; } - cursor = shape.parent; - } else { - break; } + current = cur.proto(); } let iter = self.epoch.alloc(ForInIter { diff --git a/crates/oxide_vm/tests/regression_tests.rs b/crates/oxide_vm/tests/regression_tests.rs index 82f031a2..80ea9667 100644 --- a/crates/oxide_vm/tests/regression_tests.rs +++ b/crates/oxide_vm/tests/regression_tests.rs @@ -64,3 +64,12 @@ fn regression_throw_statement_errors() { "compile error: throw statement not supported" ); } + +#[test] +fn regression_for_in_prototype_chain() { + assert_eq!( + eval("var c=0;for(var k in {a:1}){c=c+1;}c"), + "2", + "for-in should include inherited constructor from prototype" + ); +}