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:
@ -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();
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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")
|
||||
|
||||
284
src/backend/mir_interpreter/static_box_registry.rs
Normal file
284
src/backend/mir_interpreter/static_box_registry.rs
Normal 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"));
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
|
||||
Reference in New Issue
Block a user