diff --git a/src/backend/mir_interpreter/handlers/boxes_object_fields.rs b/src/backend/mir_interpreter/handlers/boxes_object_fields.rs index ca50fbf1..4c0d9fe2 100644 --- a/src/backend/mir_interpreter/handlers/boxes_object_fields.rs +++ b/src/backend/mir_interpreter/handlers/boxes_object_fields.rs @@ -41,7 +41,7 @@ pub(super) fn try_handle_object_fields( // Static box support: if box_val is a string matching a static box name, // resolve it to the singleton instance let actual_box_val = if let Ok(VMValue::String(ref box_name)) = this.reg_load(box_val) { - if this.static_box_decls.contains_key(box_name) { + if this.static_box_registry.exists(box_name) { // Get or create singleton instance let instance = this.ensure_static_box_instance(box_name)?; let instance_clone = instance.clone(); @@ -363,7 +363,7 @@ pub(super) fn try_handle_object_fields( // Static box support: if box_val is a string matching a static box name, // resolve it to the singleton instance let actual_box_val = if let Ok(VMValue::String(ref box_name)) = this.reg_load(box_val) { - if this.static_box_decls.contains_key(box_name) { + if this.static_box_registry.exists(box_name) { // Get or create singleton instance let instance = this.ensure_static_box_instance(box_name)?; let instance_clone = instance.clone(); diff --git a/src/backend/mir_interpreter/handlers/calls/method.rs b/src/backend/mir_interpreter/handlers/calls/method.rs index 4c46fddd..d2673d41 100644 --- a/src/backend/mir_interpreter/handlers/calls/method.rs +++ b/src/backend/mir_interpreter/handlers/calls/method.rs @@ -137,7 +137,7 @@ impl MirInterpreter { Ok(out) } else { // Receiver not provided: try static singleton instance for the box (methodize PoC fallback) - if self.static_box_decls.contains_key(box_name) { + if self.static_box_registry.exists(box_name) { // 🎯 Phase 173-B: Static box methods are in MIR function table // Try calling the MIR function directly (BoxName.method/arity pattern) let func_name = format!("{}.{}/{}", box_name, method, args.len()); diff --git a/src/backend/mir_interpreter/mod.rs b/src/backend/mir_interpreter/mod.rs index ce4622d4..587a6deb 100644 --- a/src/backend/mir_interpreter/mod.rs +++ b/src/backend/mir_interpreter/mod.rs @@ -21,8 +21,11 @@ mod exec; mod handlers; mod helpers; mod method_router; +pub mod static_box_registry; mod utils; +pub use static_box_registry::StaticBoxRegistry; + pub struct MirInterpreter { pub(super) regs: HashMap, pub(super) mem: HashMap, @@ -34,10 +37,8 @@ pub struct MirInterpreter { pub(super) last_block: Option, pub(super) last_inst: Option, pub(super) last_inst_index: Option, - // Static box singleton instances (persistent across method calls) - pub(super) static_boxes: HashMap, - // Static box declarations (metadata for creating instances) - pub(super) static_box_decls: HashMap, + // 🎯 Phase 173-B: Unified static box management via StaticBoxRegistry + pub(super) static_box_registry: StaticBoxRegistry, // Lightweight dev counters (opt-in print via NYASH_VM_STATS=1) pub(super) inst_count: u64, pub(super) branch_count: u64, @@ -58,8 +59,7 @@ impl MirInterpreter { last_block: None, last_inst: None, last_inst_index: None, - static_boxes: HashMap::new(), - static_box_decls: HashMap::new(), + static_box_registry: StaticBoxRegistry::new(), inst_count: 0, branch_count: 0, compare_count: 0, @@ -73,17 +73,26 @@ impl MirInterpreter { } /// Register static box declarations (called from vm.rs during setup) + /// Now delegated to StaticBoxRegistry pub fn register_static_box_decl( &mut self, name: String, decl: crate::core::model::BoxDeclaration, ) { - self.static_box_decls.insert(name, decl); + self.static_box_registry.register_declaration(name, decl); } /// Check if a static box is already registered (Phase 173-B) + /// Now delegated to StaticBoxRegistry pub fn has_static_box_decl(&self, name: &str) -> bool { - self.static_box_decls.contains_key(name) + self.static_box_registry.exists(name) + } + + /// Initialize registry from MIR function names (auto-detect using imports) + /// Called from vm.rs after module is loaded + pub fn detect_static_boxes_from_functions(&mut self) { + self.static_box_registry + .detect_from_mir_functions(self.functions.keys()); } /// Execute a BoxCall with VM's complete semantics (Phase 27-shortterm S-5.2-improved) @@ -153,60 +162,23 @@ impl MirInterpreter { /// Ensure static box singleton instance exists, create if not /// Returns mutable reference to the singleton instance + /// Now delegated to StaticBoxRegistry fn ensure_static_box_instance( &mut self, box_name: &str, ) -> Result<&mut crate::instance_v2::InstanceBox, VMError> { - // Check if instance already exists - if !self.static_boxes.contains_key(box_name) { - // Get declaration - let decl = self - .static_box_decls - .get(box_name) - .ok_or_else(|| { - VMError::InvalidInstruction(format!( - "static box declaration not found: {}", - box_name - )) - })? - .clone(); - - // Create instance from declaration - let instance = crate::instance_v2::InstanceBox::from_declaration( - box_name.to_string(), - decl.fields.clone(), - decl.methods.clone(), - ); - - self.static_boxes.insert(box_name.to_string(), instance); - - if std::env::var("NYASH_VM_STATIC_TRACE").ok().as_deref() == Some("1") { - eprintln!( - "[vm-static] created singleton instance for static box: {}", - box_name - ); - } - } - - // Return mutable reference - self.static_boxes.get_mut(box_name).ok_or_else(|| { - VMError::InvalidInstruction(format!( - "static box instance not found after creation: {}", - box_name - )) - }) + self.static_box_registry + .get_or_create_instance(box_name) + .map_err(|e| VMError::InvalidInstruction(e)) } /// Check if a function name represents a static box method /// Format: "BoxName.method/Arity" + /// Now uses StaticBoxRegistry naming utilities #[allow(dead_code)] fn is_static_box_method(&self, func_name: &str) -> Option { - if let Some((box_name, _rest)) = func_name.split_once('.') { - if self.static_box_decls.contains_key(box_name) { - return Some(box_name.to_string()); - } - } - None + static_box_registry::naming::extract_box_name(func_name) + .filter(|name| self.static_box_registry.exists(name)) } /// Execute module entry (main) and return boxed result @@ -214,6 +186,10 @@ impl MirInterpreter { // Snapshot functions for call resolution self.functions = module.functions.clone(); + // 🎯 Phase 173-B: Auto-detect static boxes from MIR function names + // This handles using-imported static boxes that aren't in AST + self.detect_static_boxes_from_functions(); + // Determine entry function with sensible fallbacks // Priority: // 1) NYASH_ENTRY env (exact), then basename before '/' if provided (e.g., "Main.main/0" → "Main.main") diff --git a/src/backend/mir_interpreter/static_box_registry.rs b/src/backend/mir_interpreter/static_box_registry.rs new file mode 100644 index 00000000..c23d3219 --- /dev/null +++ b/src/backend/mir_interpreter/static_box_registry.rs @@ -0,0 +1,284 @@ +/*! + * StaticBoxRegistry - 静的Box管理の一元化箱 + * + * 箱理論の実践: + * - 箱にする: static_box_decls + static_boxes + MIR関数検出を1箱に集約 + * - 境界を作る: 静的Boxの存在証明・シングルトン管理を単一責務に + * - Fail-Fast: 存在しないBoxへのアクセスは即座にエラー + * + * 設計原則: + * - MIR関数名から自動検出: "BoxName.method/arity" パターンでusing importも対応 + * - 遅延シングルトン: 実際にアクセスされるまでインスタンス作成しない + * - 明示的登録も可能: AST経由のBoxDeclarationも受け入れ + */ + +use std::collections::{HashMap, HashSet}; + +use crate::core::model::BoxDeclaration; +use crate::instance_v2::InstanceBox; + +/// 静的Box管理の一元化レジストリ +/// +/// Phase 173-B で発生した問題を根本解決: +/// - using import された静的Boxが static_box_decls に登録されない問題 +/// - MIR関数テーブルと static_box_decls の不整合問題 +pub struct StaticBoxRegistry { + /// 明示的に登録されたBoxDeclaration (AST経由) + declarations: HashMap, + + /// MIR関数名から検出された静的Box名 + /// (using import でも自動検出可能) + detected_boxes: HashSet, + + /// 実行時シングルトン (遅延作成) + instances: HashMap, +} + +/// MIR関数名のパースユーティリティ +pub mod naming { + /// "BoxName.method/arity" 形式の関数名をパース + /// Returns: Some((box_name, method, arity)) or None + pub fn parse_static_method_name(fn_name: &str) -> Option<(String, String, usize)> { + // "BoxName.method/arity" or "BoxName.method" + let dot_pos = fn_name.find('.')?; + let box_name = &fn_name[..dot_pos]; + let rest = &fn_name[dot_pos + 1..]; + + let (method, arity) = if let Some(slash_pos) = rest.find('/') { + let method = &rest[..slash_pos]; + let arity_str = &rest[slash_pos + 1..]; + let arity = arity_str.parse::().ok()?; + (method.to_string(), arity) + } else { + (rest.to_string(), 0) + }; + + Some((box_name.to_string(), method, arity)) + } + + /// 静的Boxメソッド名を生成 + pub fn static_method_name(box_name: &str, method: &str, arity: usize) -> String { + format!("{}.{}/{}", box_name, method, arity) + } + + /// 関数名からBox名を抽出 (メソッド・arity無視) + pub fn extract_box_name(fn_name: &str) -> Option { + parse_static_method_name(fn_name).map(|(box_name, _, _)| box_name) + } +} + +/// 除外する組み込みBox名 (静的Boxではない) +const BUILTIN_RUNTIME_BOXES: &[&str] = &[ + "Main", + "main", + "StringBox", + "IntegerBox", + "BoolBox", + "ArrayBox", + "MapBox", + "FloatBox", + "VoidBox", + "NullBox", + "ConsoleBox", + "MathBox", + "FileBox", + "NetBox", + "CounterBox", +]; + +impl StaticBoxRegistry { + /// 空のレジストリを作成 + pub fn new() -> Self { + Self { + declarations: HashMap::new(), + detected_boxes: HashSet::new(), + instances: HashMap::new(), + } + } + + /// MIR関数名一覧から静的Boxを自動検出 + /// + /// using import された静的Boxも含めて検出可能 + pub fn detect_from_mir_functions<'a, I>(&mut self, fn_names: I) + where + I: Iterator, + { + for fn_name in fn_names { + if let Some(box_name) = naming::extract_box_name(fn_name) { + // 組み込みランタイムBoxは除外 + if !BUILTIN_RUNTIME_BOXES.contains(&box_name.as_str()) { + self.detected_boxes.insert(box_name); + } + } + } + + if std::env::var("NYASH_STATIC_BOX_REGISTRY_TRACE") + .ok() + .as_deref() + == Some("1") + { + eprintln!( + "[static-box-registry] detected {} static boxes from MIR functions", + self.detected_boxes.len() + ); + for name in &self.detected_boxes { + eprintln!(" - {}", name); + } + } + } + + /// BoxDeclarationを明示的に登録 (AST経由) + pub fn register_declaration(&mut self, name: String, decl: BoxDeclaration) { + self.declarations.insert(name, decl); + } + + /// 静的Boxが存在するか確認 + /// + /// 以下のいずれかで存在とみなす: + /// 1. declarations に登録済み + /// 2. detected_boxes に検出済み + pub fn exists(&self, name: &str) -> bool { + self.declarations.contains_key(name) || self.detected_boxes.contains(name) + } + + /// シングルトンインスタンスを取得または作成 + /// + /// 遅延作成: 初回アクセス時にのみ作成 + pub fn get_or_create_instance(&mut self, name: &str) -> Result<&mut InstanceBox, String> { + if !self.exists(name) { + return Err(format!( + "static box '{}' not found in registry (neither declared nor detected)", + name + )); + } + + // 既存インスタンスがあればそれを返す + if self.instances.contains_key(name) { + return Ok(self.instances.get_mut(name).unwrap()); + } + + // 新規作成 + let instance = if let Some(decl) = self.declarations.get(name) { + // 明示的な宣言がある場合 + InstanceBox::from_declaration( + name.to_string(), + decl.fields.clone(), + decl.methods.clone(), + ) + } else { + // MIR関数から検出された場合 (宣言なし) + // 最小限のInstanceBoxを作成 (メソッドはMIR関数テーブルにある) + InstanceBox::from_declaration(name.to_string(), vec![], HashMap::new()) + }; + + if std::env::var("NYASH_STATIC_BOX_REGISTRY_TRACE") + .ok() + .as_deref() + == Some("1") + { + eprintln!( + "[static-box-registry] created singleton instance for '{}'", + name + ); + } + + self.instances.insert(name.to_string(), instance); + Ok(self.instances.get_mut(name).unwrap()) + } + + /// シングルトンインスタンスが既に作成済みか確認 + #[allow(dead_code)] + pub fn has_instance(&self, name: &str) -> bool { + self.instances.contains_key(name) + } + + /// 登録済み/検出済みの静的Box名一覧 + pub fn all_box_names(&self) -> impl Iterator { + self.declarations + .keys() + .chain(self.detected_boxes.iter()) + } + + /// declarations への直接アクセス (既存コードとの互換性) + pub fn declarations(&self) -> &HashMap { + &self.declarations + } + + /// detected_boxes への直接アクセス + #[allow(dead_code)] + pub fn detected_boxes(&self) -> &HashSet { + &self.detected_boxes + } +} + +impl Default for StaticBoxRegistry { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_static_method_name() { + assert_eq!( + naming::parse_static_method_name("JsonParserBox.parse/1"), + Some(("JsonParserBox".to_string(), "parse".to_string(), 1)) + ); + assert_eq!( + naming::parse_static_method_name("MyBox.toString/0"), + Some(("MyBox".to_string(), "toString".to_string(), 0)) + ); + assert_eq!(naming::parse_static_method_name("main"), None); + assert_eq!(naming::parse_static_method_name("no_dot"), None); + } + + #[test] + fn test_static_method_name() { + assert_eq!( + naming::static_method_name("JsonParserBox", "parse", 1), + "JsonParserBox.parse/1" + ); + } + + #[test] + fn test_detect_from_mir_functions() { + let mut registry = StaticBoxRegistry::new(); + let fn_names = vec![ + "JsonParserBox.parse/1".to_string(), + "JsonParserBox.toString/0".to_string(), + "ProgramJSONBox.get/1".to_string(), + "Main.main/0".to_string(), // 除外される + "StringBox.length/0".to_string(), // 除外される + ]; + + registry.detect_from_mir_functions(fn_names.iter()); + + assert!(registry.exists("JsonParserBox")); + assert!(registry.exists("ProgramJSONBox")); + assert!(!registry.exists("Main")); // 除外 + assert!(!registry.exists("StringBox")); // 除外 + } + + #[test] + fn test_get_or_create_instance() { + let mut registry = StaticBoxRegistry::new(); + registry.detected_boxes.insert("TestBox".to_string()); + + let result = registry.get_or_create_instance("TestBox"); + assert!(result.is_ok()); + + // 2回目も同じインスタンスを返す + assert!(registry.has_instance("TestBox")); + } + + #[test] + fn test_nonexistent_box_error() { + let mut registry = StaticBoxRegistry::new(); + let result = registry.get_or_create_instance("NonExistent"); + assert!(result.is_err()); + assert!(result.unwrap_err().contains("not found in registry")); + } +} diff --git a/src/runner/modes/vm.rs b/src/runner/modes/vm.rs index f1a7724d..016f5e34 100644 --- a/src/runner/modes/vm.rs +++ b/src/runner/modes/vm.rs @@ -496,74 +496,11 @@ impl NyashRunner { use crate::backend::MirInterpreter; let mut vm = MirInterpreter::new(); - // Register static box declarations for singleton persistence + // Register static box declarations for singleton persistence (AST-based) for (name, decl) in static_box_decls { vm.register_static_box_decl(name, decl); } - // 🎯 Phase 173-B: Register using-imported static boxes from MIR function names - // Extract box names from MIR function patterns like "BoxName.method/arity" - // This enables VM singleton creation for using-imported static boxes - { - use std::collections::HashSet; - let mut imported_boxes: HashSet = HashSet::new(); - - for fn_name in module_vm.functions.keys() { - // Parse "BoxName.method/arity" pattern - if let Some(dot_pos) = fn_name.find('.') { - let box_name = &fn_name[..dot_pos]; - // Skip known non-static boxes (runtime data boxes) - if !matches!( - box_name, - "Main" - | "main" - | "StringBox" - | "IntegerBox" - | "BoolBox" - | "ArrayBox" - | "MapBox" - | "FileBox" - | "NetBox" - ) { - imported_boxes.insert(box_name.to_string()); - } - } - } - - // Register any boxes not already in static_box_decls - for box_name in imported_boxes { - if !vm.has_static_box_decl(&box_name) { - // Create minimal BoxDeclaration for using-imported boxes - // VM only needs the name for singleton creation - let decl = crate::core::model::BoxDeclaration { - name: box_name.clone(), - fields: Vec::new(), - public_fields: Vec::new(), - private_fields: Vec::new(), - methods: std::collections::HashMap::new(), - constructors: std::collections::HashMap::new(), - init_fields: Vec::new(), - weak_fields: Vec::new(), - is_interface: false, - extends: Vec::new(), - implements: Vec::new(), - type_parameters: Vec::new(), - }; - if std::env::var("NYASH_USING_STATIC_BOX_TRACE") - .ok() - .as_deref() - == Some("1") - { - eprintln!( - "[phase173-b] Registering using-imported static box: {}", - box_name - ); - } - vm.register_static_box_decl(box_name, decl); - } - } - } - // Optional: verify MIR before execution (dev-only) if crate::config::env::env_bool("NYASH_VM_VERIFY_MIR") { let ring0 = crate::runtime::ring0::get_global_ring0();