From bf19cf112bef8cd49ccd060f2b708eb681431ca5 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sat, 26 Sep 2026 19:59:23 +0000 Subject: [PATCH] =?UTF-8?q?perf:=20for-of=20=E5=86=85=E7=BD=AE=E8=BF=AD?= =?UTF-8?q?=E4=BB=A3=E5=99=A8=E5=BF=AB=E8=B7=AF=E5=BE=84=E7=9B=B4=E6=AD=A5?= =?UTF-8?q?=EF=BC=8C=E8=B7=B3=E8=BF=87=20native=20=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E5=B8=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/iterator.rs | 133 +++++++++++++++++++++--- crates/oxide_vm/src/session_gc/tests.rs | 2 + crates/oxide_vm/src/suspended.rs | 6 ++ crates/oxide_vm/src/vm/gc_hooks.rs | 2 + crates/oxide_vm/src/vm_dispatch_misc.rs | 47 ++++++++- crates/oxide_vm/src/vm_runtime.rs | 2 + crates/oxide_vm/src/vm_state.rs | 13 ++- crates/oxide_vm/src/vm_support/tests.rs | 2 + 8 files changed, 187 insertions(+), 20 deletions(-) diff --git a/crates/oxide_builtins/src/iterator.rs b/crates/oxide_builtins/src/iterator.rs index c9ad56fb..bdbd37ae 100644 --- a/crates/oxide_builtins/src/iterator.rs +++ b/crates/oxide_builtins/src/iterator.rs @@ -1693,9 +1693,67 @@ fn str_iter_step(units: &[u16], at: usize) -> (usize, Option) { (at + 1, Some(StrIterElem::One(u))) } -fn next_array_like( +/// 内置包装器的内层值种类:for-of 内置包装器(Array/String/TA/Map/Set)的 +/// 内层类型五判据(与 `next_array_like` 臂分派同判据)。快路径按 kind 直步, +/// kind 失配时回落慢路径。 +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum BuiltinIterKind { + Array, + String, + TypedArray, + MapEntries, + SetValues, +} + +/// 按五判据分类内层值(臂序与 `next_array_like` 一致),非内置类型返回 None。 +fn classify_inner(inner: JsValue) -> Option { + if is_array_value(inner) { + Some(BuiltinIterKind::Array) + } else if inner.is_string() { + Some(BuiltinIterKind::String) + } else if is_typed_array_value(inner) { + Some(BuiltinIterKind::TypedArray) + } else if is_map_value(inner) { + Some(BuiltinIterKind::MapEntries) + } else if is_set_value(inner) { + Some(BuiltinIterKind::SetValues) + } else { + None + } +} + +/// 检测内置包装器种类:读 `__inner__` 槽后按五判据分类。 +/// +/// # 边界 +/// - 包装器非对象 / `__inner__` 缺失(undefined)/ 非内置类型 → None(调用方走慢路径)。 +/// +/// # 注意事项 +/// - `__inner__` 是用户可观察槽,随时可能被改写;检测结果只是 init 期提示, +/// 快路径每步重读复判(见 `builtin_iter_fast_step`)。 +pub fn builtin_iter_kind(vm: &mut H, wrapper: JsValue) -> Option { + if !wrapper.is_object() { + return None; + } + // SAFETY: wrapper 刚判定为对象。 + let wrapper_obj = unsafe { &*wrapper.as_js_object_ptr() }; + let inner_si = vm.kernel_core().perm_interner().intern(INNER_PROP).0; + let inner = match vm.ordinary_get(wrapper_obj, inner_si, wrapper) { + Ok(inner) if !inner.is_undefined() => inner, + _ => return None, + }; + classify_inner(inner) +} + +/// 内置包装器步进核心:按内层类型产出元素值与 done 标志,并把新游标写回 +/// 包装器 `__index__` 槽(用户可观察槽,每步直读直写、不跨步缓存)。 +/// +/// # 返回值 +/// - `Ok(None)`:内层非内置类型(调用方落慢路径); +/// - `Ok(Some((value, done)))`:本步元素值与 done 标志; +/// - `Err`:原始异常值(元素 getter 抛出、TA detach/越界等)。 +fn step_builtin_inner( vm: &mut H, wrapper: &mut JsObject, inner: JsValue, index_si: u32, -) -> Result, JsValue> { +) -> Result, JsValue> { if is_array_value(inner) { let index = current_index(vm, wrapper, index_si); let arr = unsafe { &*inner.as_js_object_ptr() }; @@ -1713,9 +1771,9 @@ fn next_array_like( } }; vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((index + 1) as i32)); - return Ok(Some(make_iter_result(vm, value, false))); + return Ok(Some((value, false))); } - return Ok(Some(make_iter_result(vm, JsValue::undefined(), true))); + return Ok(Some((JsValue::undefined(), true))); } if inner.is_string() { @@ -1761,9 +1819,9 @@ fn next_array_like( }, StrIterElem::Pair(hi, lo) => vm.new_string_units(&[hi, lo]), }; - return Ok(Some(make_iter_result(vm, value, false))); + return Ok(Some((value, false))); } - return Ok(Some(make_iter_result(vm, JsValue::undefined(), true))); + return Ok(Some((JsValue::undefined(), true))); } if is_typed_array_value(inner) { @@ -1779,22 +1837,57 @@ fn next_array_like( 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(make_iter_result(vm, value, false))); + return Ok(Some((value, false))); } - return Ok(Some(make_iter_result(vm, JsValue::undefined(), true))); + return Ok(Some((JsValue::undefined(), true))); } // for-of 循环默认迭代:Map 产出 [key, value] 对,Set 产出值。 if is_map_value(inner) { - return Ok(Some(map_set_step(vm, wrapper, inner, index_si, MapSetMode::MapEntries))); + let (value, done) = map_set_step_core(vm, wrapper, inner, index_si, MapSetMode::MapEntries); + return Ok(Some((value, done))); } if is_set_value(inner) { - return Ok(Some(map_set_step(vm, wrapper, inner, index_si, MapSetMode::SetValues))); + let (value, done) = map_set_step_core(vm, wrapper, inner, index_si, MapSetMode::SetValues); + return Ok(Some((value, done))); } Ok(None) } +/// 内置包装器步进一步(for-of 快路径入口):重读 `__inner__` 槽并复判 kind, +/// 失配 / 缺失 / 非对象时以 `None` 信号回落慢路径(调用方清快标志)。 +/// +/// # 返回值 +/// - `Ok(None)`:kind 失配或 `__inner__` 缺失(非内置包装器); +/// - `Ok(Some((value, done)))`:本步元素值与 done 标志; +/// - `Err`:原始异常值。 +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), + } +} + +/// 内置包装器的 `next` 步进(慢路径):步进核心产出元素后包成 `{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))), + None => Ok(None), + } +} + fn current_index(vm: &mut H, wrapper: &JsObject, index_si: u32) -> usize { match vm.ordinary_get(wrapper, index_si, JsValue::undefined()) { Ok(value) if value.is_int() => value.as_int().max(0) as usize, @@ -1999,13 +2092,15 @@ impl MapSetMode { } } -/// 把 Map/Set 迭代器包装器推进一步,按指定模式产出 `{value, done}` 结果。 +/// Map/Set 迭代步进核心:按模式产出元素值与 done 标志,并把新下标写回 +/// 包装器 `__index__` 槽。 +/// /// 条目存放在集合 native-data 槽中(Map 为槽表:空槽跳过、下标口径为"下一待检槽"; /// Set 为插入序表);(a, b) 对在任意分配之前拷出,使对 native 集合的借用不会 /// 跨越 `vm` 调用保持。 -fn map_set_step( +fn map_set_step_core( vm: &mut H, wrapper: &mut JsObject, inner: JsValue, index_si: u32, mode: MapSetMode, -) -> JsValue { +) -> (JsValue, bool) { let index = current_index(vm, wrapper, index_si); let is_map = matches!(mode, MapSetMode::MapEntries | MapSetMode::MapValues | MapSetMode::MapKeys); let (entry, next_index): (Option<(JsValue, JsValue)>, usize) = unsafe { @@ -2056,17 +2151,25 @@ fn map_set_step( MapSetMode::MapValues => b, MapSetMode::MapKeys | MapSetMode::SetValues => a, }; - make_iter_result(vm, value, false) + (value, false) } None => { // 迭代器已耗尽:把下标推进到永不匹配的哨兵值,使后续 next() 恒返回 done, // 即使集合之后又新增元素也不会"复活"。 vm.set_or_create_prop_value(wrapper, index_si, JsValue::int(i32::MAX)); - make_iter_result(vm, JsValue::undefined(), true) + (JsValue::undefined(), true) } } } +/// 把 Map/Set 迭代器包装器推进一步,按指定模式产出 `{value, done}` 结果。 +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); + make_iter_result(vm, value, done) +} + /// Map/Set 迭代器 `next`:模式从 wrapper `__mode__` 槽读取后按模式推进。 /// /// %MapIteratorPrototype%/%SetIteratorPrototype% 绑定同一实现——家族与模式 diff --git a/crates/oxide_vm/src/session_gc/tests.rs b/crates/oxide_vm/src/session_gc/tests.rs index 71f70d30..21bcabab 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() { iterator: JsValue::from_js_object(child_session), last_result: JsValue::from_js_object(root_session), is_async: false, + fast: None, + fast_value: JsValue::undefined(), }); let mut roots = Vec::new(); diff --git a/crates/oxide_vm/src/suspended.rs b/crates/oxide_vm/src/suspended.rs index 85674bf1..eb1d37d4 100644 --- a/crates/oxide_vm/src/suspended.rs +++ b/crates/oxide_vm/src/suspended.rs @@ -194,6 +194,7 @@ impl SuspendedFrame { for entry in &self.for_of_iters { f(entry.iterator); f(entry.last_result); + f(entry.fast_value); } if let Some(it) = self.delegated_iterator { f(it); @@ -248,6 +249,7 @@ impl SuspendedFrame { for entry in &mut self.for_of_iters { entry.iterator = rewrite(entry.iterator); entry.last_result = rewrite(entry.last_result); + entry.fast_value = rewrite(entry.fast_value); } self.delegated_iterator = self.delegated_iterator.map(&mut rewrite); self.exception_value = self.exception_value.map(&mut rewrite); @@ -324,6 +326,8 @@ impl SuspendedFrame { iterator: rewrite(entry.iterator), last_result: rewrite(entry.last_result), is_async: entry.is_async, + fast: entry.fast, + fast_value: rewrite(entry.fast_value), }) .collect(), delegated_iterator: self.delegated_iterator.map(&mut rewrite), @@ -413,6 +417,8 @@ mod tests { iterator: JsValue::float(6.0), last_result: JsValue::float(7.0), is_async: false, + fast: None, + fast_value: JsValue::undefined(), }); frame.delegated_iterator = Some(JsValue::float(8.0)); frame.saved_bytecode_stack.push(Arc::from(vec![0u32])); diff --git a/crates/oxide_vm/src/vm/gc_hooks.rs b/crates/oxide_vm/src/vm/gc_hooks.rs index ee79f6be..6ed3a10b 100644 --- a/crates/oxide_vm/src/vm/gc_hooks.rs +++ b/crates/oxide_vm/src/vm/gc_hooks.rs @@ -70,6 +70,7 @@ impl Vm { for &entry in &self.iters.for_of_iters { f(entry.iterator); f(entry.last_result); + f(entry.fast_value); } // 微任务队列中的处理器/能力/值都是 GC 根。 for job in &self.job_queue { @@ -151,6 +152,7 @@ impl Vm { for entry in &mut self.iters.for_of_iters { entry.iterator = rewrite(entry.iterator); entry.last_result = rewrite(entry.last_result); + entry.fast_value = rewrite(entry.fast_value); } // 微任务队列中的值随 sweep 重写。 for job in &mut self.job_queue { diff --git a/crates/oxide_vm/src/vm_dispatch_misc.rs b/crates/oxide_vm/src/vm_dispatch_misc.rs index f57f92bc..f5f6785f 100644 --- a/crates/oxide_vm/src/vm_dispatch_misc.rs +++ b/crates/oxide_vm/src/vm_dispatch_misc.rs @@ -641,7 +641,9 @@ impl Vm { let iterable = self.regs[a]; match oxide_builtins::iterator::make_iterator_for_value(self, iterable) { Ok(iterator) => { - self.iters.push_for_of(iterator, false); + // 内置包装器检测:Array/String/TA/Map/Set 走快路径(异步恒慢)。 + let kind = oxide_builtins::iterator::builtin_iter_kind(self, iterator); + self.iters.push_for_of(iterator, false, kind); Ok(()) } Err(err) => { @@ -659,7 +661,7 @@ 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); + self.iters.push_for_of(iterator, true, None); Ok(()) } Err(err) => { @@ -762,6 +764,33 @@ impl Vm { // 调用 next()/读 done 前清空异常值槽:跨调用残留不得污染本指令的取槽。 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) { + return Ok(()); + } + match step { + Ok(Some((value, done))) => { + self.iters.for_of_iters.last_mut().unwrap().fast_value = value; + 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(()); + } + let iter_obj = unsafe { &*iterator.as_js_object_ptr() }; let next_si = self.kernel_core.perm_interner().intern("next").0; let next_fn = match self.ordinary_get(iter_obj, next_si, iterator) { @@ -792,6 +821,11 @@ impl Vm { pub(crate) fn dispatch_for_of_next(&mut self, rd: usize) -> Result<(), String> { vm_trace!("FOR_OF_NEXT rd={}", rd); self.last_uncaught_value = None; + // 快路径:元素值由前一次 DONE 快步产出,直写寄存器。 + if self.iters.for_of_iters.last().is_some_and(|e| e.fast.is_some()) { + self.regs[rd] = self.iters.for_of_iters.last().unwrap().fast_value; + return Ok(()); + } let result = self.iters.last_result(); if !result.is_object() { self.regs[rd] = JsValue::undefined(); @@ -811,11 +845,16 @@ impl Vm { /// 重新抛出原始值。按 ECMA-262,next() 抛出时不会经 return() 关闭迭代器—— /// 先弹出它,使展开时的 IteratorClose 遍历跳过该迭代器。 fn throw_for_of_error(&mut self, msg: String) -> Result<(), String> { - self.iters.pop_for_of(); let exc = match self.last_uncaught_value.take() { Some(v) => v, None => oxide_builtins::error::create_from_text(self, &msg), }; + self.throw_for_of_error_value(exc) + } + + /// 以指定异常值展开(慢路径文本重建与快路径原值透传共用)。 + fn throw_for_of_error_value(&mut self, exc: JsValue) -> Result<(), String> { + self.iters.pop_for_of(); self.exception_value = Some(exc); self.pending_error_kind = Some(self.thrown_error_kind(exc)); // 无论取到原值还是重建错误对象,异常必须展开传播,不得静默返回 Ok—— @@ -1234,6 +1273,8 @@ mod tests { iterator: JsValue::undefined(), last_result: JsValue::undefined(), is_async: false, + fast: None, + fast_value: JsValue::undefined(), }); vm.run(&Arc::new(module)) diff --git a/crates/oxide_vm/src/vm_runtime.rs b/crates/oxide_vm/src/vm_runtime.rs index c9de739f..f5cadc48 100644 --- a/crates/oxide_vm/src/vm_runtime.rs +++ b/crates/oxide_vm/src/vm_runtime.rs @@ -828,6 +828,8 @@ mod tests { iterator: JsValue::float(8.5), last_result: JsValue::float(9.0), is_async: false, + fast: None, + fast_value: JsValue::undefined(), }); 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 1bd8d62b..1cf73320 100644 --- a/crates/oxide_vm/src/vm_state.rs +++ b/crates/oxide_vm/src/vm_state.rs @@ -15,6 +15,7 @@ use rustc_hash::FxBuildHasher; use crate::session_gc::SessionGc; use crate::vm::ForInIter; +use oxide_builtins::iterator::BuiltinIterKind; use oxide_types::object::{Cell as UpvalueCell, JsObject, JsString}; use oxide_types::private_key::WELL_KNOWN_SYMBOL_COUNT; use oxide_types::value::JsValue; @@ -165,6 +166,11 @@ pub(crate) struct ForOfEntry { /// promise(独立异步机制),同步逃出路径(break/continue/return 计数关闭) /// 不得同步调用其 return()。 pub(crate) is_async: bool, + /// 内置包装器快路径:迭代器为 Array/String/TA/Map/Set 内置包装器时非 None, + /// DONE/NEXT 直步不经 native 调用;kind 失配清标志回落慢路径。 + pub(crate) fast: Option, + /// 快路径本迭代产出的元素值(仅 DONE→NEXT 之间有效,不跨迭代)。 + pub(crate) fast_value: JsValue, } /// for-in / for-of 的活跃迭代器状态。 @@ -192,12 +198,15 @@ impl IterState { } /// 压入新迭代器条目:`last_result` 初始为 undefined(尚未执行任何 next())。 - /// `is_async` 标记 for-await-of 的异步迭代器(异步逃出关闭走独立机制)。 - pub(crate) fn push_for_of(&mut self, iterator: JsValue, is_async: bool) { + /// `is_async` 标记 for-await-of 的异步迭代器(异步逃出关闭走独立机制); + /// `fast` 为内置包装器种类(None = 慢路径)。 + pub(crate) fn push_for_of(&mut self, iterator: JsValue, is_async: bool, fast: Option) { self.for_of_iters.push(ForOfEntry { iterator, last_result: JsValue::undefined(), is_async, + fast, + fast_value: JsValue::undefined(), }); } diff --git a/crates/oxide_vm/src/vm_support/tests.rs b/crates/oxide_vm/src/vm_support/tests.rs index a4afad38..05bf6f5f 100644 --- a/crates/oxide_vm/src/vm_support/tests.rs +++ b/crates/oxide_vm/src/vm_support/tests.rs @@ -853,6 +853,8 @@ fn reset_clears_runtime_state_like_rerun() { iterator: JsValue::undefined(), last_result: JsValue::undefined(), is_async: false, + fast: None, + fast_value: JsValue::undefined(), }); vm.saved_bytecode_stack.push(Arc::from(vec![oxide_bytecode::opcode::encode( oxide_bytecode::opcode::OpCode::HALT,