From d67e1469c1c3d46c5d337fad18e8e7c04dc020ab Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Mon, 14 Sep 2026 17:35:40 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=BF=AD=E4=BB=A3=E5=99=A8=E5=BF=AB?= =?UTF-8?q?=E9=80=9F=E8=B7=AF=E5=BE=84=E5=B0=8A=E9=87=8D=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E8=A6=86=E7=9B=96=E7=9A=84=20@@iterator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/iterator.rs | 56 +++++++++++++++--- .../oxide_vm/tests/builtins_iterator_tests.rs | 59 +++++++++++++++++++ 2 files changed, 106 insertions(+), 9 deletions(-) diff --git a/crates/oxide_builtins/src/iterator.rs b/crates/oxide_builtins/src/iterator.rs index 08bebdfa..d585bd29 100644 --- a/crates/oxide_builtins/src/iterator.rs +++ b/crates/oxide_builtins/src/iterator.rs @@ -1406,9 +1406,42 @@ pub(crate) fn peek_iterator_method(vm: &mut H, value: JsValue) -> Res Ok(false) } -/// 经迭代协议取内层迭代器:内建集合直接作为内层(无缓存 next),`@@iterator` -/// 调用结果作为内层(无缓存 next),鸭子回退路径同时回传读到的 `next` 闭包 -/// 供调用方缓存进包装器 `__next__` 槽(消费期不重触发 getter)。 +/// 判断已读到的 `@@iterator` 结果是否仍是内建集合的默认迭代器函数 +/// (内建原型别名槽上的同一函数对象)。 +/// +/// # 边界 +/// value 须为内建集合(Array/TypedArray/Map/Set),锚点槽按种类选取 +/// (Array/TypedArray/Set 为 `values`、Map 为 `entries`)。 +/// +/// # 注意 +/// 锚点槽读取失败(如用户把别名改写为抛出 getter)一律返回 false, +/// 落入通用协议按 `method` 继续处理,不在此处吞错。 +fn builtin_iterator_default_intact(vm: &mut H, value: JsValue, method: JsValue) -> bool { + if !method.is_object() { + return false; + } + let world = vm.session().builtin_world(); + let (anchor_ptr, anchor_name) = if is_array_value(value) { + (world.array_proto.as_ptr() as *mut JsObject, "values") + } else if is_typed_array_value(value) { + (world.typed_array_proto.as_ptr() as *mut JsObject, "values") + } else if is_map_value(value) { + (world.map_proto.as_ptr() as *mut JsObject, "entries") + } else { + (world.set_proto.as_ptr() as *mut JsObject, "values") + }; + let anchor_obj = unsafe { &*anchor_ptr }; + let anchor_si = vm.kernel_core().perm_interner().intern(anchor_name).0; + let Ok(anchor) = vm.ordinary_get(anchor_obj, anchor_si, JsValue::from_js_object(anchor_ptr)) else { + return false; + }; + anchor.is_object() && std::ptr::eq(method.as_js_object_ptr(), anchor.as_js_object_ptr()) +} + +/// 经迭代协议取内层迭代器:内建集合(原型链 `@@iterator` 未被覆盖时)直接 +/// 作为内层(无缓存 next),用户覆盖的 `@@iterator` 或 duck-next 回退路径 +/// 的调用结果作为内层(无缓存 next),鸭子回退路径同时回传读到的 `next` +/// 闭包供调用方缓存进包装器 `__next__` 槽(消费期不重触发 getter)。 /// /// # 返回值 /// - `Ok(Some((inner, next)))`:`inner` 为内层迭代器;鸭子回退时 `next` 为 @@ -1416,14 +1449,16 @@ pub(crate) fn peek_iterator_method(vm: &mut H, value: JsValue) -> Res /// - `Ok(None)`:不可迭代; /// - `Err`:`@@iterator` 或 `next` getter 抛错,透传原异常值。 fn get_iterator(vm: &mut H, value: JsValue) -> Result)>, JsValue> { - if value.is_string() - || is_array_value(value) - || is_typed_array_value(value) - || is_map_value(value) - || is_set_value(value) - { + if value.is_string() { return Ok(Some((value, None))); } + // 内建集合标记:仅当 @@iterator 经原型链读到的仍是内建默认迭代器函数 + // 时才走快速路径;用户覆盖(自身属性或原型链改写/删除)走通用协议 + // 调用用户函数。 + let builtin_value = is_array_value(value) + || is_typed_array_value(value) + || is_map_value(value) + || is_set_value(value); // 非字符串 primitive(boolean/number/symbol/bigint):GetIterator 先 ToObject, // 再走迭代协议(如 `yield* true` 委托 Boolean.prototype[Symbol.iterator])。 @@ -1449,6 +1484,9 @@ fn get_iterator(vm: &mut H, value: JsValue) -> Result it, diff --git a/crates/oxide_vm/tests/builtins_iterator_tests.rs b/crates/oxide_vm/tests/builtins_iterator_tests.rs index 88702527..0ec749bd 100644 --- a/crates/oxide_vm/tests/builtins_iterator_tests.rs +++ b/crates/oxide_vm/tests/builtins_iterator_tests.rs @@ -2112,3 +2112,62 @@ fn iterator_helper_rebound_after_full_reset() { assert_eq!(result.as_bool(), expected, "for {}", src); } } + +#[test] +fn for_of_respects_overridden_array_prototype_iterator() { + let mut vm = Vm::new(); + // 红形钉:内置原型上改写的 @@iterator 必须被迭代入口调用(展开与 for-of + // 同面),红形为短路直取内建迭代器、无视覆盖。 + let r = eval( + &mut vm, + "(() => { + const old = Array.prototype[Symbol.iterator]; + Array.prototype[Symbol.iterator] = function* () { yield 42; }; + try { + const spread = [...[1, 2, 3]].join(','); + let looped = ''; + for (const v of [7]) looped += v; + return spread + '|' + looped; + } finally { Array.prototype[Symbol.iterator] = old; } + })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, r), "42|42"); +} + +#[test] +fn for_of_respects_own_iterator_override_internal_paths_unaffected() { + let mut vm = Vm::new(); + // 红形钉:实例自身属性的 @@iterator 覆盖须生效;slice 走内部元素直读面, + // 不受覆盖影响(内部路径不观测用户迭代器)。 + let r = eval( + &mut vm, + "(() => { + const a = [7, 8]; + a[Symbol.iterator] = function* () { yield 99; }; + return [...a].join(',') + '|' + a.slice(0, 2).join(','); + })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, r), "99|7,8"); +} + +#[test] +fn for_of_throws_when_array_prototype_iterator_deleted() { + let mut vm = Vm::new(); + // 红形钉:原型 @@iterator 被删除后 GetMethod 取不到方法,迭代入口须抛 + // TypeError(红形为短路直取元素区、静默迭代成功)。 + let r = eval( + &mut vm, + "(() => { + const old = Array.prototype[Symbol.iterator]; + delete Array.prototype[Symbol.iterator]; + try { + try { for (const _v of [1, 2]) {} return 'no'; } + catch (e) { return e.constructor.name; } + } finally { Array.prototype[Symbol.iterator] = old; } + })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, r), "TypeError"); +}