refactor(vm): StaticBoxRegistry unifies static box management

箱化モジュール化: Phase 173-B の散在した static box 管理を一元化

Before (4箇所に散在):
- static_box_decls: HashMap (AST経由のBox宣言)
- static_boxes: HashMap (実行時シングルトン)
- vm.rs Phase 173-B手動検出コード (~60行)
- method.rs static_box_decls.contains_key() 直接参照

After (StaticBoxRegistry箱に統一):
- declarations: AST経由のBox宣言を登録
- detected_boxes: MIR関数名から自動検出 (using import対応)
- instances: 遅延作成シングルトン
- naming utilities: parse/format関数

Benefits:
- vm.rs: 63行削減 (Phase 173-B手動コード削除)
- 自動検出: using import した static box も対応
- 単一責務: static box lifecycle を1箱に集約
- Fail-Fast: 存在しないBoxへのアクセスは即エラー

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-04 18:56:23 +09:00
parent 9d29718f03
commit 397bc77eb3
5 changed files with 316 additions and 119 deletions

View File

@ -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();

View File

@ -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());

View File

@ -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<ValueId, VMValue>,
pub(super) mem: HashMap<ValueId, VMValue>,
@ -34,10 +37,8 @@ pub struct MirInterpreter {
pub(super) last_block: Option<BasicBlockId>,
pub(super) last_inst: Option<MirInstruction>,
pub(super) last_inst_index: Option<usize>,
// Static box singleton instances (persistent across method calls)
pub(super) static_boxes: HashMap<String, crate::instance_v2::InstanceBox>,
// Static box declarations (metadata for creating instances)
pub(super) static_box_decls: HashMap<String, crate::core::model::BoxDeclaration>,
// 🎯 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<String> {
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")

View File

@ -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<String, BoxDeclaration>,
/// MIR関数名から検出された静的Box名
/// (using import でも自動検出可能)
detected_boxes: HashSet<String>,
/// 実行時シングルトン (遅延作成)
instances: HashMap<String, InstanceBox>,
}
/// 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::<usize>().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<String> {
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<Item = &'a String>,
{
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<Item = &String> {
self.declarations
.keys()
.chain(self.detected_boxes.iter())
}
/// declarations への直接アクセス (既存コードとの互換性)
pub fn declarations(&self) -> &HashMap<String, BoxDeclaration> {
&self.declarations
}
/// detected_boxes への直接アクセス
#[allow(dead_code)]
pub fn detected_boxes(&self) -> &HashSet<String> {
&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"));
}
}

View File

@ -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<String> = 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();