diff --git a/Cargo.lock b/Cargo.lock index 4d4351ff..a9148e76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1095,7 +1095,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.6.6" +version = "0.7.0" dependencies = [ "chrono", "fast-float", @@ -1120,7 +1120,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.6.6" +version = "0.7.0" dependencies = [ "num-bigint", "oxide_log", @@ -1128,7 +1128,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.6.6" +version = "0.7.0" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1139,7 +1139,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.6.6" +version = "0.7.0" dependencies = [ "ansi_term", "bumpalo", @@ -1159,7 +1159,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.6.6" +version = "0.7.0" dependencies = [ "lru", "oxide_bytecode", @@ -1168,7 +1168,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.6.6" +version = "0.7.0" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1187,7 +1187,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.6.6" +version = "0.7.0" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1201,7 +1201,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.6.6" +version = "0.7.0" dependencies = [ "num-bigint", "oxc_allocator", @@ -1216,7 +1216,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.6.6" +version = "0.7.0" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1225,7 +1225,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.6.6" +version = "0.7.0" dependencies = [ "bumpalo", "dashmap", @@ -1240,7 +1240,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.6.6" +version = "0.7.0" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1252,7 +1252,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.6.6" +version = "0.7.0" dependencies = [ "tracing", "tracing-appender", @@ -1261,7 +1261,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.6.6" +version = "0.7.0" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1276,7 +1276,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.6.6" +version = "0.7.0" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1292,7 +1292,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.6.6" +version = "0.7.0" dependencies = [ "num-bigint", "num-traits", @@ -1304,7 +1304,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.6.6" +version = "0.7.0" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.6.6" +version = "0.7.0" dependencies = [ "bumpalo", "hashbrown 0.14.5", @@ -1333,7 +1333,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.6.6" +version = "0.7.0" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index c7b7607b..bd702fe5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ members = [ ] [workspace.package] -version = "0.6.6" +version = "0.7.0" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_builtins/src/iterator.rs b/crates/oxide_builtins/src/iterator.rs index dfbbd1ce..23bcbd12 100644 --- a/crates/oxide_builtins/src/iterator.rs +++ b/crates/oxide_builtins/src/iterator.rs @@ -1721,15 +1721,16 @@ fn classify_inner(inner: JsValue) -> Option { } } -/// 检测内置包装器种类:读 `__inner__` 槽后按五判据分类。 +/// 检测内置包装器种类:读 `__inner__` 槽后按五判据分类,连带返回 inner 值。 /// -/// # 边界 -/// - 包装器非对象 / `__inner__` 缺失(undefined)/ 非内置类型 → None(调用方走慢路径)。 +/// # 返回值 +/// - `Some((kind, inner))`:内置包装器,inner 为 `__inner__` 槽值快照; +/// - `None`:包装器非对象 / `__inner__` 缺失(undefined)/ 非内置类型(调用方走慢路径)。 /// /// # 注意事项 -/// - `__inner__` 是用户可观察槽,随时可能被改写;检测结果只是 init 期提示, -/// 快路径每步重读复判(见 `builtin_iter_fast_step`)。 -pub fn builtin_iter_kind(vm: &mut H, wrapper: JsValue) -> Option { +/// - `__inner__` 是用户可观察槽,随时可能被改写;返回值是 init 期快照, +/// 快路径按快照直步不再每步重读复判(见 `builtin_iter_fast_step`)。 +pub fn builtin_iter_kind(vm: &mut H, wrapper: JsValue) -> Option<(BuiltinIterKind, JsValue)> { if !wrapper.is_object() { return None; } @@ -1740,21 +1741,23 @@ pub fn builtin_iter_kind(vm: &mut H, wrapper: JsValue) -> Option inner, _ => return None, }; - classify_inner(inner) + classify_inner(inner).map(|kind| (kind, inner)) } -/// 内置包装器步进核心:按内层类型产出元素值与 done 标志,并把新游标写回 -/// 包装器 `__index__` 槽(用户可观察槽,每步直读直写、不跨步缓存)。 +/// 内置包装器步进核心:按内层类型产出元素值、done 标志与新游标。 +/// +/// 游标由调用方传入并回传(快路径存条目、慢路径写回 `__index__` 槽), +/// 本核心不触碰包装器属性槽。 /// /// # 返回值 /// - `Ok(None)`:内层非内置类型(调用方落慢路径); -/// - `Ok(Some((value, done)))`:本步元素值与 done 标志; +/// - `Ok(Some((value, done, next_cursor)))`:本步元素值、done 标志与新游标; /// - `Err`:原始异常值(元素 getter 抛出、TA detach/越界等)。 fn step_builtin_inner( - vm: &mut H, wrapper: &mut JsObject, inner: JsValue, index_si: u32, -) -> Result, JsValue> { + vm: &mut H, inner: JsValue, cursor: usize, +) -> Result, JsValue> { if is_array_value(inner) { - let index = current_index(vm, wrapper, index_si); + let index = cursor; let arr = unsafe { &*inner.as_js_object_ptr() }; if index < arr.prop_count() as usize { // 数组元素读取走 GetValue:普通数据属性返回槽值,访问器属性 @@ -1769,21 +1772,17 @@ fn step_builtin_inner( return Err(exc); } }; - vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((index + 1) as i32)); - return Ok(Some((value, false))); + return Ok(Some((value, false, index + 1))); } - return Ok(Some((JsValue::undefined(), true))); + return Ok(Some((JsValue::undefined(), true, cursor))); } if inner.is_string() { - // index 槽在字符串分支存游标(按载荷形态钉死):单元形态(FlatU16/Cons) - // 存单元偏移;Flat 形态存 字节偏移×2(与单元口径对齐,每字符步长按其 - // UTF-16 单元数)。游标只被 next 内部读写、无外部消费者,包装器创建后 - // 类型固定不跨类型复用。 + // 游标按载荷形态钉死:单元形态(FlatU16/Cons)存单元偏移;Flat 形态存 + // 字节偏移×2(与单元口径对齐,每字符步长按其 UTF-16 单元数)。 // // 产出按 Unicode 标量(规格口径):合法代理对整体产出一个 2 单元元素, // 孤立 surrogate 各产出 1 单元元素。 - let cursor = current_index(vm, wrapper, index_si); // 源串裸指针借用压缩到单个表达式:产出元素为 Copy 值,不携带借用, // 之后对 VM 状态的可变访问不再与源串借用共存。 let (next_cursor, elem): (usize, Option) = unsafe { @@ -1809,7 +1808,6 @@ fn step_builtin_inner( } }; if let Some(elem) = elem { - vm.set_or_create_prop_value(wrapper, index_si, JsValue::int(next_cursor as i32)); let value = match elem { // ASCII 单元走单字符缓存零分配,其余(含孤立 surrogate)落串创建。 StrIterElem::One(u) => match vm.single_unit(u) { @@ -1818,13 +1816,13 @@ fn step_builtin_inner( }, StrIterElem::Pair(hi, lo) => vm.new_string_units(&[hi, lo]), }; - return Ok(Some((value, false))); + return Ok(Some((value, false, next_cursor))); } - return Ok(Some((JsValue::undefined(), true))); + return Ok(Some((JsValue::undefined(), true, cursor))); } if is_typed_array_value(inner) { - let index = current_index(vm, wrapper, index_si); + let index = cursor; let obj = unsafe { &*inner.as_js_object_ptr() }; // 每次 next 入口校验:detach/越界抛 TypeError,auto 收缩按 live 长度停。 let view = crate::typed_array::get_typed_array_data(vm, inner)?; @@ -1835,54 +1833,52 @@ fn step_builtin_inner( Ok(v) => v, Err(e) => return Err(crate::error::create_type_error(vm, &e)), }; - vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((index + 1) as i32)); - return Ok(Some((value, false))); + return Ok(Some((value, false, index + 1))); } - return Ok(Some((JsValue::undefined(), true))); + return Ok(Some((JsValue::undefined(), true, cursor))); } // for-of 循环默认迭代:Map 产出 [key, value] 对,Set 产出值。 if is_map_value(inner) { - let (value, done) = map_set_step_core(vm, wrapper, inner, index_si, MapSetMode::MapEntries); - return Ok(Some((value, done))); + let (value, done, next) = map_set_step_core(vm, inner, cursor, MapSetMode::MapEntries); + return Ok(Some((value, done, next))); } if is_set_value(inner) { - let (value, done) = map_set_step_core(vm, wrapper, inner, index_si, MapSetMode::SetValues); - return Ok(Some((value, done))); + let (value, done, next) = map_set_step_core(vm, inner, cursor, MapSetMode::SetValues); + return Ok(Some((value, done, next))); } Ok(None) } -/// 内置包装器步进一步(for-of 快路径入口):重读 `__inner__` 槽并复判 kind, -/// 失配 / 缺失 / 非对象时以 `None` 信号回落慢路径(调用方清快标志)。 +/// 内置包装器步进一步(for-of 快路径入口):按 init 期快照的 inner 与游标直步, +/// 不重读 `__inner__` 槽、不读写 `__index__` 槽。 /// /// # 返回值 -/// - `Ok(None)`:kind 失配或 `__inner__` 缺失(非内置包装器); -/// - `Ok(Some((value, done)))`:本步元素值与 done 标志; -/// - `Err`:原始异常值。 +/// - `Ok((value, done, next_cursor))`:本步元素值、done 标志与新游标; +/// - `Err`:原始异常值(元素 getter 抛出、TA detach/越界等)。 pub fn builtin_iter_fast_step( - vm: &mut H, wrapper: &mut JsObject, kind: BuiltinIterKind, -) -> Result, JsValue> { - let inner_si = vm.kernel_core().perm_interner().intern(INNER_PROP).0; - let index_si = vm.kernel_core().perm_interner().intern(INDEX_PROP).0; - let inner = match vm.ordinary_get(wrapper, inner_si, JsValue::undefined()) { - Ok(inner) if !inner.is_undefined() => inner, - _ => return Ok(None), - }; - match classify_inner(inner) { - Some(actual) if actual == kind => step_builtin_inner(vm, wrapper, inner, index_si), - _ => Ok(None), + vm: &mut H, inner: JsValue, cursor: usize, +) -> Result<(JsValue, bool, usize), JsValue> { + match step_builtin_inner(vm, inner, cursor)? { + Some(step) => Ok(step), + // 内层非内置类型:理论上不可达(kind 在 init 期已判且 inner 为快照), + // 防御性按 done 收口。 + None => Ok((JsValue::undefined(), true, cursor)), } } -/// 内置包装器的 `next` 步进(慢路径):步进核心产出元素后包成 `{value, done}` -/// 结果对象;慢路径行为与抽提前逐位一致。 +/// 内置包装器的 `next` 步进(慢路径):从 `__index__` 槽读游标、步进核心产出 +/// 元素、新游标写回槽后包成 `{value, done}` 结果对象;慢路径行为与抽提前逐位一致。 fn next_array_like( vm: &mut H, wrapper: &mut JsObject, inner: JsValue, index_si: u32, ) -> Result, JsValue> { - match step_builtin_inner(vm, wrapper, inner, index_si)? { - Some((value, done)) => Ok(Some(make_iter_result(vm, value, done))), + let cursor = current_index(vm, wrapper, index_si); + match step_builtin_inner(vm, inner, cursor)? { + Some((value, done, next_cursor)) => { + vm.set_or_create_prop_value(wrapper, index_si, JsValue::int(next_cursor as i32)); + Ok(Some(make_iter_result(vm, value, done))) + } None => Ok(None), } } @@ -2086,16 +2082,14 @@ impl MapSetMode { } } -/// Map/Set 迭代步进核心:按模式产出元素值与 done 标志,并把新下标写回 -/// 包装器 `__index__` 槽。 +/// Map/Set 迭代步进核心:按模式产出元素值、done 标志与新下标。 /// /// 条目存放在集合 native-data 槽中(Map 为槽表:空槽跳过、下标口径为"下一待检槽"; /// Set 为插入序表);(a, b) 对在任意分配之前拷出,使对 native 集合的借用不会 -/// 跨越 `vm` 调用保持。 -fn map_set_step_core( - vm: &mut H, wrapper: &mut JsObject, inner: JsValue, index_si: u32, mode: MapSetMode, -) -> (JsValue, bool) { - let index = current_index(vm, wrapper, index_si); +/// 跨越 `vm` 调用保持。下标由调用方传入并回传(快路径存条目、慢路径写回 +/// `__index__` 槽),本核心不触碰包装器属性槽。 +fn map_set_step_core(vm: &mut H, inner: JsValue, cursor: usize, mode: MapSetMode) -> (JsValue, bool, usize) { + let index = cursor; let is_map = matches!(mode, MapSetMode::MapEntries | MapSetMode::MapValues | MapSetMode::MapKeys); let (entry, next_index): (Option<(JsValue, JsValue)>, usize) = unsafe { let obj_ptr = inner.as_js_object_ptr(); @@ -2139,28 +2133,29 @@ fn map_set_step_core( match entry { Some((a, b)) => { - vm.set_or_create_prop_value(wrapper, index_si, JsValue::int(next_index as i32)); let value = match mode { MapSetMode::MapEntries | MapSetMode::SetEntries => make_map_set_pair(vm, a, b), MapSetMode::MapValues => b, MapSetMode::MapKeys | MapSetMode::SetValues => a, }; - (value, false) + (value, false, next_index) } None => { // 迭代器已耗尽:把下标推进到永不匹配的哨兵值,使后续 next() 恒返回 done, // 即使集合之后又新增元素也不会"复活"。 - vm.set_or_create_prop_value(wrapper, index_si, JsValue::int(i32::MAX)); - (JsValue::undefined(), true) + (JsValue::undefined(), true, i32::MAX as usize) } } } -/// 把 Map/Set 迭代器包装器推进一步,按指定模式产出 `{value, done}` 结果。 +/// 把 Map/Set 迭代器包装器推进一步,按指定模式产出 `{value, done}` 结果: +/// 从 `__index__` 槽读游标、步进核心产出元素、新游标写回槽。 fn map_set_step( vm: &mut H, wrapper: &mut JsObject, inner: JsValue, index_si: u32, mode: MapSetMode, ) -> JsValue { - let (value, done) = map_set_step_core(vm, wrapper, inner, index_si, mode); + let cursor = current_index(vm, wrapper, index_si); + let (value, done, next_cursor) = map_set_step_core(vm, inner, cursor, mode); + vm.set_or_create_prop_value(wrapper, index_si, JsValue::int(next_cursor as i32)); make_iter_result(vm, value, done) } diff --git a/crates/oxide_vm/src/session_gc/tests.rs b/crates/oxide_vm/src/session_gc/tests.rs index 14ac62fc..363a01ca 100644 --- a/crates/oxide_vm/src/session_gc/tests.rs +++ b/crates/oxide_vm/src/session_gc/tests.rs @@ -117,6 +117,8 @@ fn gc_roots_contains_registers_frames_and_root_roots() { is_async: false, fast: None, fast_value: JsValue::undefined(), + fast_inner: JsValue::from_js_object(child_session), + fast_cursor: 0, }); let mut roots = Vec::new(); diff --git a/crates/oxide_vm/src/suspended.rs b/crates/oxide_vm/src/suspended.rs index ed238da9..a9455fe1 100644 --- a/crates/oxide_vm/src/suspended.rs +++ b/crates/oxide_vm/src/suspended.rs @@ -208,6 +208,7 @@ impl SuspendedFrame { f(entry.iterator); f(entry.last_result); f(entry.fast_value); + f(entry.fast_inner); } if let Some(it) = self.delegated_iterator { f(it); diff --git a/crates/oxide_vm/src/vm/gc_hooks.rs b/crates/oxide_vm/src/vm/gc_hooks.rs index 63d13ad1..520578a2 100644 --- a/crates/oxide_vm/src/vm/gc_hooks.rs +++ b/crates/oxide_vm/src/vm/gc_hooks.rs @@ -80,6 +80,7 @@ impl Vm { f(entry.iterator); f(entry.last_result); f(entry.fast_value); + f(entry.fast_inner); } // 微任务队列中的处理器/能力/值都是 GC 根。 for job in &self.job_queue { diff --git a/crates/oxide_vm/src/vm_dispatch_misc.rs b/crates/oxide_vm/src/vm_dispatch_misc.rs index 1fbceba8..135f5882 100644 --- a/crates/oxide_vm/src/vm_dispatch_misc.rs +++ b/crates/oxide_vm/src/vm_dispatch_misc.rs @@ -638,8 +638,12 @@ impl Vm { match oxide_builtins::iterator::make_iterator_for_value(self, iterable) { Ok(iterator) => { // 内置包装器检测:Array/String/TA/Map/Set 走快路径(异步恒慢)。 - let kind = oxide_builtins::iterator::builtin_iter_kind(self, iterator); - self.iters.push_for_of(iterator, false, kind); + // init 期把 inner 快照入条目,步内不重读 `__inner__` 槽。 + let (kind, inner) = match oxide_builtins::iterator::builtin_iter_kind(self, iterator) { + Some((k, i)) => (Some(k), i), + None => (None, JsValue::undefined()), + }; + self.iters.push_for_of(iterator, false, kind, inner); Ok(()) } Err(err) => { @@ -657,7 +661,8 @@ impl Vm { let iterable = self.regs[a]; match crate::async_from_sync::make_async_iterator(self, iterable) { Ok(iterator) => { - self.iters.push_for_of(iterator, true, None); + // for-await-of 恒慢路径:无快路径快照,inner 传 undefined。 + self.iters.push_for_of(iterator, true, None, JsValue::undefined()); Ok(()) } Err(err) => { @@ -770,26 +775,32 @@ impl Vm { self.last_uncaught_value = None; // 快路径:内置包装器(Array/String/TA/Map/Set)直步,跳过 native 调用帧、 - // 结果对象分配与 done/value 两次读;kind 失配清标志,本迭代回落慢路径。 - let fast_kind = self.iters.for_of_iters.last().and_then(|e| e.fast); - if let Some(kind) = fast_kind { - // SAFETY: iterator 刚判定为对象。 - let iter_obj = unsafe { &mut *iterator.as_js_object_ptr() }; - let step = oxide_builtins::iterator::builtin_iter_fast_step(self, iter_obj, kind); - // 元素 getter / __inner__ 访问器抛出:深度 0 的 raise_call_error 已就地 - // unwind(跳转 catch),条目已弹出、异常值已入异常通道——直接返回, - // 派发循环自 catch 点续行,不得再触碰迭代器栈。 - if !self.iters.for_of_iters.last().is_some_and(|e| e.iterator == iterator) { + // 结果对象分配与 done/value 两次读;inner 与游标取条目快照,步内不重跑槽协议。 + if self.iters.for_of_iters.last().is_some_and(|e| e.fast.is_some()) { + // inner 与游标为 Copy 值:先拷出释放借用,再做步进核心的可变调用。 + let (inner, cursor) = { + let e = self.iters.for_of_iters.last().unwrap(); + (e.fast_inner, e.fast_cursor) + }; + let stack_len = self.iters.for_of_iters.len(); + let step = oxide_builtins::iterator::builtin_iter_fast_step(self, inner, cursor); + // 元素 getter / 访问器抛出:深度 0 的 raise_call_error 已就地 unwind + // (跳转 catch),条目已弹出、异常值已入异常通道——直接返回,派发循环 + // 自 catch 点续行,不得再触碰迭代器栈。 + // 判据须含栈深度:同迭代器两层嵌套时栈顶被外层条目顶替,仅凭 iterator + // 同一性会误判通过,须以步前深度不变作锚。 + if self.iters.for_of_iters.len() != stack_len + || !self.iters.for_of_iters.last().is_some_and(|e| e.iterator == iterator) + { return Ok(()); } match step { - Ok(Some((value, done))) => { - self.iters.for_of_iters.last_mut().unwrap().fast_value = value; + Ok((value, done, next_cursor)) => { + let entry = self.iters.for_of_iters.last_mut().unwrap(); + entry.fast_value = value; + entry.fast_cursor = next_cursor; self.regs[rd] = JsValue::bool(!done); } - Ok(None) => { - self.iters.for_of_iters.last_mut().unwrap().fast = None; - } Err(exc) => return self.throw_for_of_error_value(exc), } return Ok(()); @@ -1283,6 +1294,8 @@ mod tests { is_async: false, fast: None, fast_value: JsValue::undefined(), + fast_inner: JsValue::undefined(), + fast_cursor: 0, }); vm.run(&Arc::new(module)) diff --git a/crates/oxide_vm/src/vm_runtime.rs b/crates/oxide_vm/src/vm_runtime.rs index 1efc07e4..90464cd9 100644 --- a/crates/oxide_vm/src/vm_runtime.rs +++ b/crates/oxide_vm/src/vm_runtime.rs @@ -943,6 +943,8 @@ mod tests { is_async: false, fast: None, fast_value: JsValue::undefined(), + fast_inner: JsValue::undefined(), + fast_cursor: 0, }); vm.spill_stack.push(JsValue::float(10.0)); vm.save_stack.push(JsValue::float(11.0)); diff --git a/crates/oxide_vm/src/vm_state.rs b/crates/oxide_vm/src/vm_state.rs index a78056f0..735d5483 100644 --- a/crates/oxide_vm/src/vm_state.rs +++ b/crates/oxide_vm/src/vm_state.rs @@ -155,10 +155,16 @@ pub(crate) struct ForOfEntry { /// 不得同步调用其 return()。 pub(crate) is_async: bool, /// 内置包装器快路径:迭代器为 Array/String/TA/Map/Set 内置包装器时非 None, - /// DONE/NEXT 直步不经 native 调用;kind 失配清标志回落慢路径。 + /// DONE/NEXT 直步不经 native 调用。 pub(crate) fast: Option, /// 快路径本迭代产出的元素值(仅 DONE→NEXT 之间有效,不跨迭代)。 pub(crate) fast_value: JsValue, + /// 快路径 inner 快照:init 期读 `__inner__` 槽后固化,步内不重读槽。 + /// 是 GC 边(session 对象指针),须随 fast_value 接 mark。 + pub(crate) fast_inner: JsValue, + /// 快路径游标:步内推进的迭代下标,替代每步读写 `__index__` 槽。 + /// Map/Set 族耗尽哨兵为 i32::MAX(口径与慢路径槽一致)。 + pub(crate) fast_cursor: usize, } /// for-in / for-of 的活跃迭代器状态。 @@ -203,14 +209,19 @@ impl IterState { /// 压入新迭代器条目:`last_result` 初始为 undefined(尚未执行任何 next())。 /// `is_async` 标记 for-await-of 的异步迭代器(异步逃出关闭走独立机制); - /// `fast` 为内置包装器种类(None = 慢路径)。 - pub(crate) fn push_for_of(&mut self, iterator: JsValue, is_async: bool, fast: Option) { + /// `fast` 为内置包装器种类(None = 慢路径),`fast_inner` 为快路径 inner 快照 + /// (慢路径传 undefined,游标恒 0)。 + pub(crate) fn push_for_of( + &mut self, iterator: JsValue, is_async: bool, fast: Option, fast_inner: JsValue, + ) { self.for_of_iters.push(ForOfEntry { iterator, last_result: JsValue::undefined(), is_async, fast, fast_value: JsValue::undefined(), + fast_inner, + fast_cursor: 0, }); } diff --git a/crates/oxide_vm/src/vm_support/tests.rs b/crates/oxide_vm/src/vm_support/tests.rs index 7577cf92..88063cf1 100644 --- a/crates/oxide_vm/src/vm_support/tests.rs +++ b/crates/oxide_vm/src/vm_support/tests.rs @@ -792,6 +792,8 @@ fn reset_clears_runtime_state_like_rerun() { is_async: false, fast: None, fast_value: JsValue::undefined(), + fast_inner: JsValue::undefined(), + fast_cursor: 0, }); vm.saved_bytecode_stack.push(Arc::from(vec![oxide_bytecode::opcode::encode( oxide_bytecode::opcode::OpCode::HALT,