for-of 迭代器栈条目化:迭代器与最近 next 结果配对存放

单一 last_for_of_result 槽被嵌套迭代覆盖,CLOSE 判定本迭代器是否
自然 done 时误读他者结果,导致数组解构/spread/嵌套 for-of 提前退出
漏调或误调 return()。改为栈条目 (iterator, last_result) 配对:

- IterState.for_of_iters 元素改 ForOfEntry,删除 last_for_of_result
- INIT 压条目,DONE 写本条目,NEXT/CLOSE 读本条目结果判 done
- GC mark/rewrite、inline save/restore、挂起帧快照同步配对遍历
- for-await-of 数组解构提前 break 现正确触发 return()(任务 40 样例)
This commit is contained in:
2026-08-16 04:00:06 +08:00
parent 19fcdd73d0
commit b885b672b9
9 changed files with 166 additions and 65 deletions
+4 -2
View File
@@ -839,8 +839,10 @@ mod tests {
vm.regs[1] = JsValue::from_js_object(child_session);
vm.exception_value = Some(JsValue::from_js_object(root_session));
vm.pending_exception = Some(JsValue::from_js_object(child_session));
vm.iters.for_of_iters.push(JsValue::from_js_object(child_session));
vm.iters.last_for_of_result = JsValue::from_js_object(root_session);
vm.iters.for_of_iters.push(crate::vm_state::ForOfEntry {
iterator: JsValue::from_js_object(child_session),
last_result: JsValue::from_js_object(root_session),
});
let mut roots = Vec::new();
vm.for_each_root(|v| roots.push(v));
+22 -16
View File
@@ -13,6 +13,7 @@ use oxide_types::object::Cell;
use oxide_types::value::JsValue;
use crate::vm::{CallFrame, Completion, ForInIter, TryHandler, Vm};
use crate::vm_state::ForOfEntry;
/// 挂起执行上下文快照(三份状态结构体共享的执行核心)。
pub(crate) struct SuspendedFrame {
@@ -30,8 +31,7 @@ pub(crate) struct SuspendedFrame {
pub cell_stack: Vec<Vec<*mut Cell>>,
pub try_stack: Vec<TryHandler>,
pub for_in_iters: Vec<*mut ForInIter<'static>>,
pub for_of_iters: Vec<JsValue>,
pub last_for_of_result: JsValue,
pub for_of_iters: Vec<ForOfEntry>,
/// `yield*` 委托中的内层迭代器;异步函数恒为 None。
pub delegated_iterator: Option<JsValue>,
pub saved_bytecode_stack: Vec<Arc<[opcode::Instr]>>,
@@ -58,7 +58,6 @@ impl SuspendedFrame {
try_stack: Vec::new(),
for_in_iters: Vec::new(),
for_of_iters: Vec::new(),
last_for_of_result: JsValue::undefined(),
delegated_iterator: None,
saved_bytecode_stack: Vec::new(),
saved_immutables_stack: Vec::new(),
@@ -96,7 +95,6 @@ impl SuspendedFrame {
self.try_stack = std::mem::take(&mut vm.try_stack);
self.for_in_iters = std::mem::take(&mut vm.iters.for_in_iters);
self.for_of_iters = std::mem::take(&mut vm.iters.for_of_iters);
self.last_for_of_result = vm.iters.last_for_of_result;
self.delegated_iterator = std::mem::take(&mut vm.delegated_iterator);
self.saved_bytecode_stack = std::mem::take(&mut vm.saved_bytecode_stack);
self.saved_immutables_stack = std::mem::take(&mut vm.saved_immutables_stack);
@@ -134,7 +132,6 @@ impl SuspendedFrame {
vm.try_stack = std::mem::take(&mut self.try_stack);
vm.iters.for_in_iters = std::mem::take(&mut self.for_in_iters);
vm.iters.for_of_iters = std::mem::take(&mut self.for_of_iters);
vm.iters.last_for_of_result = self.last_for_of_result;
vm.delegated_iterator = std::mem::take(&mut self.delegated_iterator);
vm.saved_bytecode_stack = std::mem::take(&mut self.saved_bytecode_stack);
vm.saved_immutables_stack = std::mem::take(&mut self.saved_immutables_stack);
@@ -174,10 +171,10 @@ impl SuspendedFrame {
f(unsafe { &*p }.value);
}
}
for v in &self.for_of_iters {
f(*v);
for entry in &self.for_of_iters {
f(entry.iterator);
f(entry.last_result);
}
f(self.last_for_of_result);
if let Some(it) = self.delegated_iterator {
f(it);
}
@@ -228,10 +225,10 @@ impl SuspendedFrame {
cell.value = rewrite(cell.value);
}
}
for v in &mut self.for_of_iters {
*v = rewrite(*v);
for entry in &mut self.for_of_iters {
entry.iterator = rewrite(entry.iterator);
entry.last_result = rewrite(entry.last_result);
}
self.last_for_of_result = rewrite(self.last_for_of_result);
self.delegated_iterator = self.delegated_iterator.map(&mut rewrite);
self.exception_value = self.exception_value.map(&mut rewrite);
self.pending_exception = self.pending_exception.map(&mut rewrite);
@@ -291,8 +288,15 @@ impl SuspendedFrame {
cell_stack: self.cell_stack.clone(),
try_stack: self.try_stack.clone(),
for_in_iters: self.for_in_iters.clone(),
for_of_iters: self.for_of_iters.iter().copied().map(&mut rewrite).collect(),
last_for_of_result: rewrite(self.last_for_of_result),
for_of_iters: self
.for_of_iters
.iter()
.copied()
.map(|entry| ForOfEntry {
iterator: rewrite(entry.iterator),
last_result: rewrite(entry.last_result),
})
.collect(),
delegated_iterator: self.delegated_iterator.map(&mut rewrite),
saved_bytecode_stack: self.saved_bytecode_stack.clone(),
saved_immutables_stack: self.saved_immutables_stack.clone(),
@@ -314,7 +318,7 @@ impl SuspendedFrame {
self.bytecode.len() as u64 * std::mem::size_of::<opcode::Instr>() as u64
+ self.spill_stack.capacity() as u64 * std::mem::size_of::<JsValue>() as u64
+ self.save_stack.capacity() as u64 * std::mem::size_of::<JsValue>() as u64
+ self.for_of_iters.capacity() as u64 * std::mem::size_of::<JsValue>() as u64
+ self.for_of_iters.capacity() as u64 * std::mem::size_of::<ForOfEntry>() as u64
}
}
@@ -368,8 +372,10 @@ mod tests {
frame_depth: 0,
for_of_depth: 0,
});
frame.for_of_iters.push(JsValue::float(6.0));
frame.last_for_of_result = JsValue::float(7.0);
frame.for_of_iters.push(ForOfEntry {
iterator: JsValue::float(6.0),
last_result: JsValue::float(7.0),
});
frame.delegated_iterator = Some(JsValue::float(8.0));
frame.saved_bytecode_stack.push(Arc::from(vec![0u32]));
frame.saved_immutables_stack.push(std::ptr::slice_from_raw_parts(std::ptr::null(), 0));
+17 -12
View File
@@ -12,7 +12,7 @@ use smallvec::SmallVec;
pub use crate::bindings::init_kernel_builtins;
use crate::native::NativeFn;
use crate::session_gc::SessionGc;
use crate::vm_state::{GcState, IterState, ProfilingState, SymbolState};
use crate::vm_state::{ForOfEntry, GcState, IterState, ProfilingState, SymbolState};
use crate::{vm_debug, vm_error, vm_trace, vm_warn};
use oxide_kernel::kernel::{KernelCore, KernelSession};
use oxide_runtime_api as coercion;
@@ -309,8 +309,7 @@ pub(crate) struct InlineSyncState {
pub(crate) pending_error_kind: Option<&'static str>,
pub(crate) pending_completion: Option<Completion>,
pub(crate) for_in_iters: Vec<*mut ForInIter<'static>>,
pub(crate) for_of_iters: Vec<JsValue>,
pub(crate) last_for_of_result: JsValue,
pub(crate) for_of_iters: Vec<ForOfEntry>,
pub(crate) saved_bytecode_stack: Vec<Arc<[opcode::Instr]>>,
pub(crate) saved_immutables_stack: Vec<*const [JsValue]>,
pub(crate) save_stack: Vec<JsValue>,
@@ -727,10 +726,10 @@ impl Vm {
f(self.async_context.unwrap_or(JsValue::undefined()));
f(self.async_gen_context.unwrap_or(JsValue::undefined()));
f(self.inline_callee.unwrap_or(JsValue::undefined()));
for &v in &self.iters.for_of_iters {
f(v);
for &entry in &self.iters.for_of_iters {
f(entry.iterator);
f(entry.last_result);
}
f(self.iters.last_for_of_result);
// 微任务队列中的处理器/能力/值都是 GC 根。
for job in &self.job_queue {
crate::promise::for_each_job_value(job, &mut f);
@@ -791,10 +790,10 @@ impl Vm {
self.async_context = self.async_context.map(&mut rewrite);
self.async_gen_context = self.async_gen_context.map(&mut rewrite);
self.inline_callee = self.inline_callee.map(&mut rewrite);
for v in &mut self.iters.for_of_iters {
*v = rewrite(*v);
for entry in &mut self.iters.for_of_iters {
entry.iterator = rewrite(entry.iterator);
entry.last_result = rewrite(entry.last_result);
}
self.iters.last_for_of_result = rewrite(self.iters.last_for_of_result);
// 微任务队列中的值随 sweep 重写。
for job in &mut self.job_queue {
crate::promise::rewrite_job_values(job, &mut rewrite);
@@ -2255,7 +2254,7 @@ fn rehome_subtree(module: &CompiledModule, base: u32, out: &mut Vec<Arc<Compiled
#[cfg(test)]
mod tests {
use super::{opcode, JsValue, TryHandler, Vm};
use super::{opcode, ForOfEntry, JsValue, TryHandler, Vm};
use oxide_bytecode::module::CompiledModule;
use oxide_runtime_api::{NativeResult, VmHost};
use oxide_types::object::NativeFnPtr;
@@ -2381,7 +2380,10 @@ mod tests {
vm.iters
.for_in_iters
.push(std::ptr::dangling_mut::<super::ForInIter<'static>>());
vm.iters.for_of_iters.push(JsValue::undefined());
vm.iters.for_of_iters.push(ForOfEntry {
iterator: JsValue::undefined(),
last_result: JsValue::undefined(),
});
vm.saved_bytecode_stack
.push(Arc::from(vec![opcode::encode(opcode::OpCode::HALT, 0, 0, 0)]));
vm.saved_immutables_stack
@@ -2437,7 +2439,10 @@ mod tests {
..CompiledModule::new()
};
let mut vm = Vm::new();
vm.iters.for_of_iters.push(JsValue::undefined());
vm.iters.for_of_iters.push(ForOfEntry {
iterator: JsValue::undefined(),
last_result: JsValue::undefined(),
});
vm.run(&module).expect("FOR_OF_CLOSE should tolerate non-object sentinel");
+12 -12
View File
@@ -538,7 +538,6 @@ impl Vm {
match oxide_builtins::iterator::make_iterator_for_value(self, iterable) {
Ok(iterator) => {
self.iters.push_for_of(iterator);
self.iters.clear_last_for_of_result();
Ok(())
}
Err(err) => {
@@ -557,7 +556,6 @@ impl Vm {
match crate::async_from_sync::make_async_iterator(self, iterable) {
Ok(iterator) => {
self.iters.push_for_of(iterator);
self.iters.clear_last_for_of_result();
Ok(())
}
Err(err) => {
@@ -594,14 +592,14 @@ impl Vm {
}
/// for-await-of 步进完成检查:读取 `AWAIT` 恢复值(a 槽,即迭代器结果对象)的
/// `done`,写入 last_for_of_result 并把 `!done` 写 rd(供 JMP_IF_FALSE 分支)。
/// `done`,写入本迭代器条目结果并把 `!done` 写 rd(供 JMP_IF_FALSE 分支)。
pub(crate) fn dispatch_for_await_of_done(&mut self, rd: usize, a: usize) -> Result<(), String> {
vm_trace!("FOR_AWAIT_OF_DONE rd={} r{}={:?}", rd, a, self.regs[a]);
let result = self.regs[a];
if !result.is_object() {
return self.raise_type_error("iterator result is not an object");
}
self.iters.set_last_for_of_result(result);
self.iters.set_last_result(result);
let result_obj = unsafe { &*result.as_js_object_ptr() };
let done_si = self.kernel_core.perm_interner().intern("done").0;
let done_val = match self.ordinary_get(result_obj, done_si, result) {
@@ -616,10 +614,11 @@ impl Vm {
/// 调用 `return()`,其返回的 promise 经 await 挂起,恢复后继续循环后的指令。
pub(crate) fn dispatch_for_await_of_close(&mut self) -> Result<(), String> {
vm_trace!("FOR_AWAIT_OF_CLOSE");
let Some(iterator) = self.iters.pop_for_of() else {
let Some(entry) = self.iters.pop_for_of() else {
return Ok(());
};
let result = self.iters.last_for_of_result();
let iterator = entry.iterator;
let result = entry.last_result;
if result.is_object() {
let result_obj = unsafe { &*result.as_js_object_ptr() };
let done_si = self.kernel_core.perm_interner().intern("done").0;
@@ -678,7 +677,7 @@ impl Vm {
Err(e) => return self.throw_for_of_error(e),
};
let done = to_boolean(done_val);
self.iters.set_last_for_of_result(result);
self.iters.set_last_result(result);
self.regs[rd] = JsValue::bool(!done);
Ok(())
}
@@ -686,7 +685,7 @@ 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;
let result = self.iters.last_for_of_result();
let result = self.iters.last_result();
if !result.is_object() {
self.regs[rd] = JsValue::undefined();
return Ok(());
@@ -716,12 +715,13 @@ impl Vm {
pub(crate) fn dispatch_for_of_close(&mut self) -> Result<(), String> {
vm_trace!("FOR_OF_CLOSE");
let Some(iterator) = self.iters.pop_for_of() else {
let Some(entry) = self.iters.pop_for_of() else {
return Ok(());
};
let iterator = entry.iterator;
let result = entry.last_result;
// 迭代已自然结束(最后一次 next 返回 done:true)时不调 return();
// 仅当元素耗尽但迭代器未 done(提前退出)才执行 IteratorClose。
let result = self.iters.last_for_of_result();
if result.is_object() {
let result_obj = unsafe { &*result.as_js_object_ptr() };
let done_si = self.kernel_core.perm_interner().intern("done").0;
@@ -789,10 +789,10 @@ impl Vm {
/// 路径(它已弹出自己的迭代器)。
pub(crate) fn close_for_of_above(&mut self, depth: usize) {
while self.iters.for_of_iters.len() > depth {
let Some(iterator) = self.iters.pop_for_of() else {
let Some(entry) = self.iters.pop_for_of() else {
break;
};
let _ = self.close_for_of_iterator(iterator, true);
let _ = self.close_for_of_iterator(entry.iterator, true);
}
}
+7 -6
View File
@@ -50,7 +50,6 @@ macro_rules! inline_save_field {
($recv:ident, $window_regs:ident, pending_completion, opt_copy) => { $recv.pending_completion };
($recv:ident, $window_regs:ident, for_in_iters, for_in_keys) => { std::mem::take(&mut $recv.iters.for_in_iters) };
($recv:ident, $window_regs:ident, for_of_iters, iter_take) => { std::mem::take(&mut $recv.iters.for_of_iters) };
($recv:ident, $window_regs:ident, last_for_of_result, iter_copy) => { $recv.iters.last_for_of_result };
($recv:ident, $window_regs:ident, saved_bytecode_stack, move_field) => { std::mem::take(&mut $recv.saved_bytecode_stack) };
($recv:ident, $window_regs:ident, saved_immutables_stack, move_field) => { std::mem::take(&mut $recv.saved_immutables_stack) };
($recv:ident, $window_regs:ident, save_stack, move_field) => { std::mem::take(&mut $recv.save_stack) };
@@ -83,7 +82,6 @@ macro_rules! inline_restore_field {
($recv:ident, $saved:ident, pending_completion, opt_copy) => { $recv.pending_completion = $saved.pending_completion };
($recv:ident, $saved:ident, for_in_iters, for_in_keys) => { $recv.iters.for_in_iters = $saved.for_in_iters };
($recv:ident, $saved:ident, for_of_iters, iter_take) => { $recv.iters.for_of_iters = $saved.for_of_iters };
($recv:ident, $saved:ident, last_for_of_result, iter_copy) => { $recv.iters.last_for_of_result = $saved.last_for_of_result };
($recv:ident, $saved:ident, saved_bytecode_stack, move_field) => { $recv.saved_bytecode_stack = $saved.saved_bytecode_stack };
($recv:ident, $saved:ident, saved_immutables_stack, move_field) => { $recv.saved_immutables_stack = $saved.saved_immutables_stack };
($recv:ident, $saved:ident, save_stack, move_field) => { $recv.save_stack = $saved.save_stack };
@@ -117,7 +115,6 @@ macro_rules! inline_core_fields {
(pending_completion, opt_copy), // V M
(for_in_iters, for_in_keys), // V M
(for_of_iters, iter_take), // V M
(last_for_of_result, iter_copy), // V M
(saved_bytecode_stack, move_field), // M
(saved_immutables_stack, move_field), // M
(save_stack, move_field), // V M
@@ -195,7 +192,6 @@ impl Vm {
self.try_stack.clear();
self.iters.for_in_iters.clear();
self.iters.for_of_iters.clear();
self.iters.last_for_of_result = JsValue::undefined();
self.spill_stack.clear();
self.save_stack.clear();
self.saved_bytecode_stack.clear();
@@ -480,7 +476,10 @@ mod tests {
value: JsValue::float(8.0),
remaining_finally: 1,
});
vm.iters.last_for_of_result = JsValue::float(9.0);
vm.iters.for_of_iters.push(crate::vm_state::ForOfEntry {
iterator: JsValue::float(8.5),
last_result: JsValue::float(9.0),
});
vm.spill_stack.push(JsValue::float(10.0));
vm.save_stack.push(JsValue::float(11.0));
vm.inline_callee = Some(JsValue::float(12.0));
@@ -506,7 +505,9 @@ mod tests {
vm.pending_completion,
Some(Completion::Return { value, remaining_finally: 1 }) if value == JsValue::float(8.0)
));
assert_eq!(vm.iters.last_for_of_result, JsValue::float(9.0));
assert_eq!(vm.iters.for_of_iters.len(), 1);
assert_eq!(vm.iters.for_of_iters[0].iterator, JsValue::float(8.5));
assert_eq!(vm.iters.for_of_iters[0].last_result, JsValue::float(9.0));
assert_eq!(vm.spill_stack, vec![JsValue::float(10.0)]);
assert_eq!(vm.save_stack, vec![JsValue::float(11.0)]);
assert_eq!(vm.inline_callee, Some(JsValue::float(12.0)));
+30 -15
View File
@@ -116,18 +116,29 @@ impl SymbolState {
}
}
/// for-of 迭代器栈条目:迭代器对象与其最近一次 `next()` 结果对象配对存放。
///
/// `last_result` 只属于本迭代器(嵌套 for-of / 数组解构 / spread 各自持有自己的
/// 结果),`FOR_OF_CLOSE`/`FOR_AWAIT_OF_CLOSE` 据此判定本迭代器是否自然 done,
/// 避免共享单槽被其它迭代器覆盖导致漏调/多调 `return()`。
#[derive(Debug, Clone, Copy)]
pub(crate) struct ForOfEntry {
/// 迭代器对象(同步迭代器 / 异步迭代器 / AsyncFromSyncIterator 包装)。
pub(crate) iterator: JsValue,
/// 本迭代器最近一次 DONE 返回的结果对象(CLOSE 判 done 用)。
pub(crate) last_result: JsValue,
}
/// for-in / for-of 的活跃迭代器状态。
pub(crate) struct IterState {
pub(crate) for_in_iters: Vec<*mut ForInIter<'static>>,
pub(crate) for_of_iters: Vec<JsValue>,
pub(crate) last_for_of_result: JsValue,
pub(crate) for_of_iters: Vec<ForOfEntry>,
}
impl IterState {
pub(crate) fn reset(&mut self) {
self.for_in_iters.clear();
self.for_of_iters.clear();
self.last_for_of_result = JsValue::undefined();
}
pub(crate) fn push_for_in(&mut self, iter: *mut ForInIter<'static>) {
@@ -142,28 +153,32 @@ impl IterState {
self.for_in_iters.last().copied().unwrap_or(std::ptr::null_mut())
}
pub(crate) fn push_for_of(&mut self, val: JsValue) {
self.for_of_iters.push(val);
/// 压入新迭代器条目:`last_result` 初始为 undefined(尚未执行任何 next())。
pub(crate) fn push_for_of(&mut self, iterator: JsValue) {
self.for_of_iters.push(ForOfEntry {
iterator,
last_result: JsValue::undefined(),
});
}
pub(crate) fn last_for_of(&self) -> Option<JsValue> {
self.for_of_iters.last().copied()
self.for_of_iters.last().map(|e| e.iterator)
}
pub(crate) fn pop_for_of(&mut self) -> Option<JsValue> {
pub(crate) fn pop_for_of(&mut self) -> Option<ForOfEntry> {
self.for_of_iters.pop()
}
pub(crate) fn last_for_of_result(&self) -> JsValue {
self.last_for_of_result
/// 栈顶条目最近一次 DONE 结果;栈空时返回 undefined(防御)。
pub(crate) fn last_result(&self) -> JsValue {
self.for_of_iters.last().map(|e| e.last_result).unwrap_or(JsValue::undefined())
}
pub(crate) fn set_last_for_of_result(&mut self, val: JsValue) {
self.last_for_of_result = val;
}
pub(crate) fn clear_last_for_of_result(&mut self) {
self.last_for_of_result = JsValue::undefined();
/// 写入栈顶条目的 DONE 结果(本迭代器自身的结果,不跨迭代器覆盖)。
pub(crate) fn set_last_result(&mut self, val: JsValue) {
if let Some(entry) = self.for_of_iters.last_mut() {
entry.last_result = val;
}
}
}
-2
View File
@@ -108,7 +108,6 @@ impl Vm {
iters: IterState {
for_in_iters: Vec::new(),
for_of_iters: Vec::new(),
last_for_of_result: JsValue::undefined(),
},
profiling: ProfilingState {
ic_hits: std::cell::Cell::new(0),
@@ -213,7 +212,6 @@ impl Vm {
iters: IterState {
for_in_iters: Vec::new(),
for_of_iters: Vec::new(),
last_for_of_result: JsValue::undefined(),
},
profiling: ProfilingState {
ic_hits: std::cell::Cell::new(0),