mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
fix: 迭代器快速路径尊重用户覆盖的 @@iterator
This commit is contained in:
@@ -1406,9 +1406,42 @@ pub(crate) fn peek_iterator_method<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, value: JsValue) -> Res
|
||||
/// - `Ok(None)`:不可迭代;
|
||||
/// - `Err`:`@@iterator` 或 `next` getter 抛错,透传原异常值。
|
||||
fn get_iterator<H: VmHost>(vm: &mut H, value: JsValue) -> Result<Option<(JsValue, Option<JsValue>)>, 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<H: VmHost>(vm: &mut H, value: JsValue) -> Result<Option<(JsValue
|
||||
return Err(exc);
|
||||
}
|
||||
};
|
||||
if builtin_value && builtin_iterator_default_intact(vm, value, method) {
|
||||
return Ok(Some((value, None)));
|
||||
}
|
||||
if is_callable(method) {
|
||||
let iterator = match vm.call_function_sync(method, obj_value, &[]) {
|
||||
Ok(it) => it,
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user