2025-08-20 17:58:51 +09:00
|
|
|
|
/*!
|
|
|
|
|
|
* ScopeTracker - Track Box instances for proper lifecycle management
|
|
|
|
|
|
*
|
|
|
|
|
|
* Phase 9.78a: Unified Box lifecycle management for VM
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
use crate::box_trait::NyashBox;
|
2025-08-20 18:57:10 +09:00
|
|
|
|
use crate::instance_v2::InstanceBox;
|
|
|
|
|
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
|
|
|
|
|
use crate::runtime::plugin_loader_v2::PluginBoxV2;
|
2025-08-20 17:58:51 +09:00
|
|
|
|
|
|
|
|
|
|
/// Tracks Box instances created in different scopes for proper fini calls
|
|
|
|
|
|
pub struct ScopeTracker {
|
|
|
|
|
|
/// Stack of scopes, each containing Boxes created in that scope
|
|
|
|
|
|
scopes: Vec<Vec<Arc<dyn NyashBox>>>,
|
2025-08-27 17:06:46 +09:00
|
|
|
|
/// Root regions for GC (values pinned as roots during a dynamic region)
|
|
|
|
|
|
roots: Vec<Vec<crate::backend::vm::VMValue>>,
|
2025-08-20 17:58:51 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl ScopeTracker {
|
|
|
|
|
|
/// Create a new scope tracker
|
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
scopes: vec![Vec::new()], // Start with one root scope
|
2025-08-27 17:06:46 +09:00
|
|
|
|
roots: vec![Vec::new()], // Start with one root region
|
2025-08-20 17:58:51 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Enter a new scope
|
|
|
|
|
|
pub fn push_scope(&mut self) {
|
|
|
|
|
|
self.scopes.push(Vec::new());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Exit current scope and call fini on all Boxes created in it
|
|
|
|
|
|
pub fn pop_scope(&mut self) {
|
|
|
|
|
|
if let Some(scope) = self.scopes.pop() {
|
|
|
|
|
|
// Call fini in reverse order of creation
|
|
|
|
|
|
for arc_box in scope.into_iter().rev() {
|
2025-08-20 18:57:10 +09:00
|
|
|
|
// InstanceBox: call fini()
|
|
|
|
|
|
if let Some(instance) = arc_box.as_any().downcast_ref::<InstanceBox>() {
|
|
|
|
|
|
let _ = instance.fini();
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2025-08-22 05:01:11 +09:00
|
|
|
|
// PluginBoxV2: 明示ライフサイクルに合わせ、スコープ終了時にfini(自己責任運用)
|
2025-08-20 18:57:10 +09:00
|
|
|
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
2025-08-22 05:01:11 +09:00
|
|
|
|
if let Some(p) = arc_box.as_any().downcast_ref::<PluginBoxV2>() {
|
|
|
|
|
|
p.finalize_now();
|
2025-08-20 18:57:10 +09:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
// Builtin and others: no-op for now
|
2025-08-20 17:58:51 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Ensure we always have at least one scope
|
2025-08-27 17:06:46 +09:00
|
|
|
|
if self.scopes.is_empty() { self.scopes.push(Vec::new()); }
|
2025-08-20 17:58:51 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Register a Box in the current scope
|
|
|
|
|
|
pub fn register_box(&mut self, nyash_box: Arc<dyn NyashBox>) {
|
|
|
|
|
|
if let Some(current_scope) = self.scopes.last_mut() {
|
|
|
|
|
|
current_scope.push(nyash_box);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Clear all scopes (used when resetting VM state)
|
|
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
|
|
// Pop all scopes and call fini
|
|
|
|
|
|
while self.scopes.len() > 1 {
|
|
|
|
|
|
self.pop_scope();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Clear the root scope
|
|
|
|
|
|
if let Some(root_scope) = self.scopes.first_mut() {
|
|
|
|
|
|
root_scope.clear();
|
|
|
|
|
|
}
|
2025-08-27 17:06:46 +09:00
|
|
|
|
|
|
|
|
|
|
// Reset roots to a single empty region
|
|
|
|
|
|
self.roots.clear();
|
|
|
|
|
|
self.roots.push(Vec::new());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ===== GC root region API (Phase 10.4 prep) =====
|
|
|
|
|
|
/// Enter a new GC root region
|
|
|
|
|
|
pub fn enter_root_region(&mut self) {
|
2025-09-03 05:04:56 +09:00
|
|
|
|
if crate::config::env::gc_trace() {
|
2025-08-27 17:06:46 +09:00
|
|
|
|
eprintln!("[GC] roots: enter");
|
|
|
|
|
|
}
|
|
|
|
|
|
self.roots.push(Vec::new());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Leave current GC root region (dropping all pinned values)
|
|
|
|
|
|
pub fn leave_root_region(&mut self) {
|
|
|
|
|
|
if let Some(_) = self.roots.pop() {
|
2025-09-03 05:04:56 +09:00
|
|
|
|
if crate::config::env::gc_trace() {
|
2025-08-27 17:06:46 +09:00
|
|
|
|
eprintln!("[GC] roots: leave");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.roots.is_empty() { self.roots.push(Vec::new()); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Pin a VMValue into the current root region (cheap clone)
|
|
|
|
|
|
pub fn pin_root(&mut self, v: &crate::backend::vm::VMValue) {
|
|
|
|
|
|
if let Some(cur) = self.roots.last_mut() {
|
|
|
|
|
|
cur.push(v.clone());
|
2025-09-03 05:04:56 +09:00
|
|
|
|
if crate::config::env::gc_trace() {
|
2025-08-27 17:06:46 +09:00
|
|
|
|
eprintln!("[GC] roots: pin {:?}", v);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Total number of pinned roots across all regions (for GC PoC diagnostics)
|
|
|
|
|
|
pub fn root_count_total(&self) -> usize { self.roots.iter().map(|r| r.len()).sum() }
|
|
|
|
|
|
|
|
|
|
|
|
/// Number of active root regions
|
|
|
|
|
|
pub fn root_regions(&self) -> usize { self.roots.len() }
|
|
|
|
|
|
|
|
|
|
|
|
/// Snapshot a flat vector of current roots (cloned) for diagnostics
|
|
|
|
|
|
pub fn roots_snapshot(&self) -> Vec<crate::backend::vm::VMValue> {
|
|
|
|
|
|
let mut out = Vec::new();
|
|
|
|
|
|
for region in &self.roots { out.extend(region.iter().cloned()); }
|
|
|
|
|
|
out
|
2025-08-20 17:58:51 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Default for ScopeTracker {
|
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
|
Self::new()
|
|
|
|
|
|
}
|
2025-08-20 18:57:10 +09:00
|
|
|
|
}
|