From b95e7b1628851210b7a91bafaa26f67e47d72d7c Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Mon, 10 Aug 2026 13:57:13 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20VM=20=E7=BB=9F=E4=B8=80=20GC=20?= =?UTF-8?q?=E9=81=8D=E5=8E=86=20for=5Feach=5Fvalue/rewrite=5Fvalues?= =?UTF-8?q?=EF=BC=88=E5=8F=8C=E5=AD=90=E6=B8=85=E5=8D=95=E6=94=B6=E6=95=9B?= =?UTF-8?q?=E4=B8=89=E5=A4=84=E6=89=8B=E5=86=99=E6=B8=85=E5=8D=95=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/src/session_gc.rs | 107 ++++++++++++++---------------- crates/oxide_vm/src/vm.rs | 101 ++++++++++++++++++++++++---- 2 files changed, 135 insertions(+), 73 deletions(-) diff --git a/crates/oxide_vm/src/session_gc.rs b/crates/oxide_vm/src/session_gc.rs index 6c59a045..7540541b 100644 --- a/crates/oxide_vm/src/session_gc.rs +++ b/crates/oxide_vm/src/session_gc.rs @@ -7,7 +7,7 @@ use oxide_types::object::{JsObject, JsString, PropMetaEntry}; use oxide_types::value::JsValue; use rustc_hash::FxBuildHasher; -use crate::vm::{Completion, Vm}; +use crate::vm::Vm; use oxide_builtins::{array_buffer, data_view, map, regexp, set, typed_array}; /// session 级 mark-sweep GC 的状态与统计。 @@ -553,63 +553,8 @@ fn rewrite_forwarded_value( fn rewrite_vm_roots(vm: &mut Vm, forwarding: &HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>) { vm_debug!("[GC] rewrite_vm_roots: {} forwarded objects", forwarding.len()); - for value in &mut vm.regs { - *value = rewrite_forwarded_value(*value, forwarding); - } - for value in &mut vm.save_stack { - *value = rewrite_forwarded_value(*value, forwarding); - } - for value in &mut vm.spill_stack { - *value = rewrite_forwarded_value(*value, forwarding); - } - for frame in &mut vm.frames { - frame.saved_this = rewrite_forwarded_value(frame.saved_this, forwarding); - frame.saved_new_target = rewrite_forwarded_value(frame.saved_new_target, forwarding); - frame.callee = rewrite_forwarded_value(frame.callee, forwarding); - frame.constructed_this = frame.constructed_this.map(|value| rewrite_forwarded_value(value, forwarding)); - } - vm.exception_value = vm.exception_value.map(|value| rewrite_forwarded_value(value, forwarding)); - vm.pending_exception = vm.pending_exception.map(|value| rewrite_forwarded_value(value, forwarding)); - vm.last_uncaught_value = vm.last_uncaught_value.map(|value| rewrite_forwarded_value(value, forwarding)); - vm.pending_completion = vm.pending_completion.map(|completion| match completion { - Completion::Return { value, remaining_finally } => Completion::Return { - value: rewrite_forwarded_value(value, forwarding), - remaining_finally, - }, - other => other, - }); - vm.generator_suspended = vm.generator_suspended.map(|value| rewrite_forwarded_value(value, forwarding)); - vm.delegated_iterator = vm.delegated_iterator.map(|value| rewrite_forwarded_value(value, forwarding)); - vm.async_context = vm.async_context.map(|value| rewrite_forwarded_value(value, forwarding)); - vm.async_gen_context = vm.async_gen_context.map(|value| rewrite_forwarded_value(value, forwarding)); - vm.inline_callee = vm.inline_callee.map(|value| rewrite_forwarded_value(value, forwarding)); - for value in &mut vm.iters.for_of_iters { - *value = rewrite_forwarded_value(*value, forwarding); - } - vm.iters.last_for_of_result = rewrite_forwarded_value(vm.iters.last_for_of_result, forwarding); - // 微任务队列中的值随 sweep 重写。 - for job in &mut vm.job_queue { - crate::promise::rewrite_job_values(job, |value| rewrite_forwarded_value(value, forwarding)); - } - // 已转换的不可变常量不是 session 值(标量 + perm 字符串)——不重写。 - for iter in &mut vm.iters.for_in_iters { - if iter.is_null() { - continue; - } - // SAFETY: for_in_iters 存放由当前 VM epoch 拥有的存活迭代器指针。 - unsafe { - for (value, _si) in (*(*iter)).keys.iter_mut() { - *value = rewrite_forwarded_value(*value, forwarding); - } - } - } - let global_ptr = vm.session.global_object().as_ptr() as *mut JsObject; - if !global_ptr.is_null() { - // SAFETY: KernelSession 在 VM 生命周期内拥有 global_object。 - unsafe { - (*global_ptr).rewrite_object_values(|value| rewrite_forwarded_value(value, forwarding)); - } - } + // 统一遍历:与 for_each_value 共用同一字段清单。 + vm.rewrite_values(|value| rewrite_forwarded_value(value, forwarding)); } #[cfg(test)] @@ -720,8 +665,52 @@ mod tests { } #[test] - fn mark_phase_reaches_cycles_and_unreachable_are_unmarked() { + fn rewrite_matches_roots_coverage() { + // 指针重写必须覆盖根收集访问的同一组执行核心字段:session→marker 映射后, + // 每个持有 session 的字段都应被改写为 marker(与 Step 1 的 for_each 覆盖测试 + // 互补,共同构成孪生清单验证)。 let mut vm = Vm::new(); + let obj = plain_object(&mut vm); + let session = vm.promote_object(obj); + let marker = plain_object(&mut vm); + let marker_session = vm.promote_object(marker); + vm.regs[0] = JsValue::from_js_object(session); + vm.last_uncaught_value = Some(JsValue::from_js_object(session)); + vm.generator_suspended = Some(JsValue::from_js_object(session)); + vm.delegated_iterator = Some(JsValue::from_js_object(session)); + vm.async_context = Some(JsValue::from_js_object(session)); + vm.async_gen_context = Some(JsValue::from_js_object(session)); + vm.inline_callee = Some(JsValue::from_js_object(session)); + vm.pending_completion = Some(crate::vm::Completion::Return { + value: JsValue::from_js_object(session), + remaining_finally: 0, + }); + + let mut forwarding = std::collections::HashMap::new(); + forwarding.insert(session, marker_session); + vm.rewrite_values(|v| { + if v.is_object() { + if let Some(&m) = forwarding.get(&v.as_js_object_ptr()) { + return JsValue::from_js_object(m); + } + } + v + }); + assert_eq!(vm.regs[0].as_js_object_ptr(), marker_session); + assert_eq!(vm.last_uncaught_value.unwrap().as_js_object_ptr(), marker_session); + assert_eq!(vm.generator_suspended.unwrap().as_js_object_ptr(), marker_session); + assert_eq!(vm.delegated_iterator.unwrap().as_js_object_ptr(), marker_session); + assert_eq!(vm.async_context.unwrap().as_js_object_ptr(), marker_session); + assert_eq!(vm.async_gen_context.unwrap().as_js_object_ptr(), marker_session); + assert_eq!(vm.inline_callee.unwrap().as_js_object_ptr(), marker_session); + match vm.pending_completion.unwrap() { + crate::vm::Completion::Return { value, .. } => assert_eq!(value.as_js_object_ptr(), marker_session), + _ => panic!("expected Return completion"), + } + } + + #[test] + fn mark_phase_reaches_cycles_and_unreachable_are_unmarked() { let mut vm = Vm::new(); let root = plain_object(&mut vm); let reachable = plain_object(&mut vm); let unreachable = plain_object(&mut vm); diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index f43385ce..57d78a5d 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -520,11 +520,13 @@ impl Vm { self.active_immutables = vec.as_slice() as *const [JsValue]; } - pub(crate) fn for_each_root(&self, mut f: impl FnMut(JsValue)) { + /// GC 根收集的统一遍历(对象与字符串都产出)。与 `rewrite_values` 字段一一对应。 + /// 覆盖执行核心的全部 JsValue 持有点:regs/帧/各栈段/cell/在途异常与完成/ + /// 挂起信号/迭代器/微任务/global。新增执行字段必须同时登记在此与 + /// `rewrite_values`。 + pub(crate) fn for_each_value(&self, mut f: impl FnMut(JsValue)) { for value in &self.regs { - if value.is_object() || value.is_string() { - f(*value); - } + f(*value); } for frame in &self.frames { f(frame.saved_this); @@ -535,7 +537,7 @@ impl Vm { for &v in &self.save_stack { f(v); } - // spill 栈是 session GC 根(漏根 → 溢出值被回收 → use-after-free) + // spill 栈是 session GC 根(漏根 → 溢出值被回收 → use-after-free)。 for &v in &self.spill_stack { f(v); } @@ -544,21 +546,16 @@ impl Vm { if cell_ptr.is_null() { continue; } - let cell = unsafe { &*cell_ptr }; - if cell.value.is_object() || cell.value.is_string() { - f(cell.value); - } + // SAFETY: cell 由 session_epoch 分配,本 session 内指针有效。 + f(unsafe { &*cell_ptr }.value); } } - f(JsValue::from_js_object(self.session.global_object().as_ptr() as *mut JsObject)); f(self.exception_value.unwrap_or(JsValue::undefined())); f(self.pending_exception.unwrap_or(JsValue::undefined())); f(self.last_uncaught_value.unwrap_or(JsValue::undefined())); // 悬挂的 return 完成持有返回值,是 GC 根。 if let Some(Completion::Return { value, .. }) = self.pending_completion { - if value.is_object() || value.is_string() { - f(value); - } + f(value); } f(self.generator_suspended.unwrap_or(JsValue::undefined())); f(self.delegated_iterator.unwrap_or(JsValue::undefined())); @@ -573,17 +570,93 @@ impl Vm { for job in &self.job_queue { crate::promise::for_each_job_value(job, &mut f); } - // 已转换的不可变常量(标量 + perm 字符串)不作为 session GC 根,不参与扫描。 for iter in &self.iters.for_in_iters { if iter.is_null() { continue; } + // SAFETY: for_in_iters 存放由当前 VM epoch 拥有的存活迭代器指针。 unsafe { for (v, _si) in (*(*iter)).keys.iter() { f(*v); } } } + f(JsValue::from_js_object(self.session.global_object().as_ptr() as *mut JsObject)); + } + + /// GC 指针重写(session 搬移后调用)。与 `for_each_value` 字段一一对应。 + pub(crate) fn rewrite_values(&mut self, mut rewrite: impl FnMut(JsValue) -> JsValue) { + for value in &mut self.regs { + *value = rewrite(*value); + } + for frame in &mut self.frames { + frame.saved_this = rewrite(frame.saved_this); + frame.saved_new_target = rewrite(frame.saved_new_target); + frame.callee = rewrite(frame.callee); + frame.constructed_this = frame.constructed_this.map(&mut rewrite); + } + for v in &mut self.save_stack { + *v = rewrite(*v); + } + for v in &mut self.spill_stack { + *v = rewrite(*v); + } + for cell_vec in &mut self.cell_stack { + for &mut cell_ptr in cell_vec.iter_mut() { + if cell_ptr.is_null() { + continue; + } + // SAFETY: cell 由 session_epoch 分配,本 session 内指针有效。 + let cell = unsafe { &mut *cell_ptr }; + cell.value = rewrite(cell.value); + } + } + self.exception_value = self.exception_value.map(&mut rewrite); + self.pending_exception = self.pending_exception.map(&mut rewrite); + self.last_uncaught_value = self.last_uncaught_value.map(&mut rewrite); + self.pending_completion = self.pending_completion.map(|completion| match completion { + Completion::Return { value, remaining_finally } => Completion::Return { + value: rewrite(value), + remaining_finally, + }, + other => other, + }); + self.generator_suspended = self.generator_suspended.map(&mut rewrite); + self.delegated_iterator = self.delegated_iterator.map(&mut rewrite); + 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); + } + 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); + } + for iter in &mut self.iters.for_in_iters { + if iter.is_null() { + continue; + } + // SAFETY: for_in_iters 存放由当前 VM epoch 拥有的存活迭代器指针。 + unsafe { + for (v, _si) in (*(*iter)).keys.iter_mut() { + *v = rewrite(*v); + } + } + } + let global_ptr = self.session.global_object().as_ptr() as *mut JsObject; + if !global_ptr.is_null() { + // SAFETY: KernelSession 在 VM 生命周期内拥有 global_object。 + unsafe { + (*global_ptr).rewrite_object_values(|value| rewrite(value)); + } + } + } + + pub(crate) fn for_each_root(&self, f: impl FnMut(JsValue)) { + // 统一遍历:根收集与指针重写共用同一字段清单。 + self.for_each_value(f); } pub(crate) fn maybe_collect_session_gc(&mut self) {