Phase 15.5 Core Box削除後の新テストシステム構築: ## 実装内容 - スモークテストv2システム完全実装(3段階プロファイル) - 共通ライブラリ(test_runner/plugin_manager/result_checker/preflight) - インタープリター層完全削除(約350行) - PyVM重要インフラ特化保持戦略(JSON v0ブリッジ専用) - nyash.tomlパス修正(13箇所、プラグイン正常ロード確認) ## 動作確認済み - 基本算術演算(+, -, *, /) - 制御構文(if, loop, break, continue) - 変数代入とスコープ - プラグインロード(20個の.soファイル) ## 既知の問題 - StringBox/IntegerBoxメソッドが動作しない - オブジェクト生成は成功するがメソッド呼び出しでエラー - Phase 15.5影響でプラグイン実装が不完全な可能性 ## ドキュメント - docs/development/testing/smoke-tests-v2.md 作成 - docs/reference/pyvm-usage-guidelines.md 作成 - CODEX_QUESTION.md(Codex相談用)作成 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
273 lines
9.0 KiB
Rust
273 lines
9.0 KiB
Rust
/*!
|
||
* Unified Box Factory Architecture
|
||
*
|
||
* Phase 9.78: 統合BoxFactoryアーキテクチャ
|
||
* すべてのBox生成(ビルトイン、ユーザー定義、プラグイン)を統一的に扱う
|
||
*
|
||
* Design principles:
|
||
* - "Everything is Box" 哲学の実装レベルでの体現
|
||
* - birth/finiライフサイクルの明確な責務分離
|
||
* - 保守性と拡張性の劇的向上
|
||
*/
|
||
|
||
use crate::box_trait::NyashBox;
|
||
use std::collections::HashMap;
|
||
use std::sync::{Arc, RwLock};
|
||
|
||
/// Runtime error types for Box operations
|
||
#[derive(Debug, thiserror::Error)]
|
||
pub enum RuntimeError {
|
||
#[error("invalid operation: {message}")]
|
||
InvalidOperation { message: String },
|
||
#[error("type error: {message}")]
|
||
TypeError { message: String },
|
||
}
|
||
|
||
/// Shared state for interpreter context (legacy compatibility)
|
||
#[derive(Debug, Default, Clone)]
|
||
pub struct SharedState;
|
||
|
||
impl SharedState {
|
||
pub fn new() -> Self {
|
||
Self
|
||
}
|
||
}
|
||
|
||
/// Unified interface for all Box creation
|
||
pub trait BoxFactory: Send + Sync {
|
||
/// Create a new Box instance with given arguments
|
||
fn create_box(
|
||
&self,
|
||
name: &str,
|
||
args: &[Box<dyn NyashBox>],
|
||
) -> Result<Box<dyn NyashBox>, RuntimeError>;
|
||
|
||
/// Check if this factory is currently available
|
||
fn is_available(&self) -> bool {
|
||
true
|
||
}
|
||
|
||
/// Get list of Box types this factory can create
|
||
fn box_types(&self) -> Vec<&str>;
|
||
|
||
/// Check if this factory supports birth/fini lifecycle
|
||
fn supports_birth(&self) -> bool {
|
||
true
|
||
}
|
||
|
||
/// Identify builtin factory to enforce reserved-name protections
|
||
fn is_builtin_factory(&self) -> bool {
|
||
false
|
||
}
|
||
}
|
||
|
||
/// Registry that manages all BoxFactory implementations
|
||
pub struct UnifiedBoxRegistry {
|
||
/// Ordered list of factories (priority: builtin > user > plugin)
|
||
pub factories: Vec<Arc<dyn BoxFactory>>,
|
||
|
||
/// Quick lookup cache for performance
|
||
type_cache: RwLock<HashMap<String, usize>>, // maps type name to factory index
|
||
}
|
||
|
||
impl UnifiedBoxRegistry {
|
||
/// Create a new empty registry
|
||
pub fn new() -> Self {
|
||
Self {
|
||
factories: Vec::new(),
|
||
type_cache: RwLock::new(HashMap::new()),
|
||
}
|
||
}
|
||
|
||
/// Register a new factory
|
||
pub fn register(&mut self, factory: Arc<dyn BoxFactory>) {
|
||
// Get all types this factory can create
|
||
let types = factory.box_types();
|
||
let factory_index = self.factories.len();
|
||
|
||
// Update cache
|
||
let mut cache = self.type_cache.write().unwrap();
|
||
// Reserved core types that must remain builtin-owned
|
||
fn is_reserved_type(name: &str) -> bool {
|
||
// Phase 15.5: 環境変数でプラグイン優先モード時は保護解除
|
||
if std::env::var("NYASH_USE_PLUGIN_BUILTINS").is_ok() {
|
||
if let Ok(types) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES") {
|
||
if types.split(',').any(|t| t.trim() == name) {
|
||
return false; // 予約型として扱わない
|
||
}
|
||
}
|
||
}
|
||
|
||
matches!(
|
||
name,
|
||
// Core value types
|
||
"StringBox" | "IntegerBox" | "BoolBox" | "FloatBox" | "NullBox"
|
||
// Core containers and result
|
||
| "ArrayBox" | "MapBox" | "ResultBox"
|
||
// Core method indirection
|
||
| "MethodBox"
|
||
)
|
||
}
|
||
for type_name in types {
|
||
// Enforce reserved names: only builtin factory may claim them
|
||
if is_reserved_type(type_name) && !factory.is_builtin_factory() {
|
||
eprintln!(
|
||
"[UnifiedBoxRegistry] ❌ Rejecting registration of reserved type '{}' by non-builtin factory #{}",
|
||
type_name, factory_index
|
||
);
|
||
continue;
|
||
}
|
||
|
||
// First registered factory wins (priority order)
|
||
let entry = cache.entry(type_name.to_string());
|
||
use std::collections::hash_map::Entry;
|
||
match entry {
|
||
Entry::Occupied(existing) => {
|
||
// Collision: type already claimed by earlier factory
|
||
eprintln!("[UnifiedBoxRegistry] ⚠️ Duplicate registration for '{}': keeping factory #{}, ignoring later factory #{}",
|
||
existing.key(), existing.get(), factory_index);
|
||
}
|
||
Entry::Vacant(v) => {
|
||
v.insert(factory_index);
|
||
}
|
||
}
|
||
}
|
||
|
||
self.factories.push(factory);
|
||
}
|
||
|
||
/// Create a Box using the unified interface
|
||
pub fn create_box(
|
||
&self,
|
||
name: &str,
|
||
args: &[Box<dyn NyashBox>],
|
||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||
// Prefer plugin-builtins when enabled and provider is available in v2 registry
|
||
if std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1") {
|
||
use crate::runtime::{get_global_registry, BoxProvider};
|
||
// Allowlist types for override: env NYASH_PLUGIN_OVERRIDE_TYPES="ArrayBox,MapBox" (default: none)
|
||
let allow: Vec<String> = if let Ok(list) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES")
|
||
{
|
||
list.split(',')
|
||
.map(|s| s.trim().to_string())
|
||
.filter(|s| !s.is_empty())
|
||
.collect()
|
||
} else {
|
||
vec![]
|
||
};
|
||
if allow.iter().any(|t| t == name) {
|
||
let v2 = get_global_registry();
|
||
if let Some(provider) = v2.get_provider(name) {
|
||
if let BoxProvider::Plugin(_lib) = provider {
|
||
return v2.create_box(name, args).map_err(|e| {
|
||
RuntimeError::InvalidOperation {
|
||
message: format!("Plugin Box creation failed: {}", e),
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// Check cache first
|
||
let cache = self.type_cache.read().unwrap();
|
||
if let Some(&factory_index) = cache.get(name) {
|
||
if let Some(factory) = self.factories.get(factory_index) {
|
||
if factory.is_available() {
|
||
return factory.create_box(name, args);
|
||
}
|
||
}
|
||
}
|
||
drop(cache);
|
||
|
||
// Linear search through all factories
|
||
for factory in &self.factories {
|
||
if !factory.is_available() {
|
||
continue;
|
||
}
|
||
|
||
// For factories that advertise types, check if they support this type
|
||
let box_types = factory.box_types();
|
||
if !box_types.is_empty() && !box_types.contains(&name) {
|
||
continue;
|
||
}
|
||
|
||
// Try to create the box (factories with empty box_types() will always be tried)
|
||
match factory.create_box(name, args) {
|
||
Ok(boxed) => return Ok(boxed),
|
||
Err(_) => continue, // Try next factory
|
||
}
|
||
}
|
||
// Final fallback: if v2 plugin registry has a provider for this name, try it once
|
||
{
|
||
let v2 = crate::runtime::get_global_registry();
|
||
if let Some(_prov) = v2.get_provider(name) {
|
||
if let Ok(b) = v2.create_box(name, args) {
|
||
return Ok(b);
|
||
}
|
||
}
|
||
}
|
||
|
||
Err(RuntimeError::InvalidOperation {
|
||
message: format!("Unknown Box type: {}", name),
|
||
})
|
||
}
|
||
|
||
/// Check whether a type name is known to the registry
|
||
pub fn has_type(&self, name: &str) -> bool {
|
||
// Check cache first
|
||
{
|
||
let cache = self.type_cache.read().unwrap();
|
||
if let Some(&idx) = cache.get(name) {
|
||
if let Some(factory) = self.factories.get(idx) {
|
||
if factory.is_available() {
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// Fallback: scan factories that can enumerate types
|
||
for factory in &self.factories {
|
||
if !factory.is_available() {
|
||
continue;
|
||
}
|
||
let types = factory.box_types();
|
||
if !types.is_empty() && types.contains(&name) {
|
||
return true;
|
||
}
|
||
}
|
||
false
|
||
}
|
||
|
||
/// Get all available Box types
|
||
pub fn available_types(&self) -> Vec<String> {
|
||
let mut types = Vec::new();
|
||
for factory in &self.factories {
|
||
if factory.is_available() {
|
||
for type_name in factory.box_types() {
|
||
types.push(type_name.to_string());
|
||
}
|
||
}
|
||
}
|
||
types.sort();
|
||
types.dedup();
|
||
types
|
||
}
|
||
}
|
||
|
||
pub mod builtin;
|
||
pub mod plugin;
|
||
/// Re-export submodules
|
||
#[cfg(feature = "interpreter-legacy")]
|
||
pub mod user_defined;
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_registry_creation() {
|
||
let registry = UnifiedBoxRegistry::new();
|
||
assert_eq!(registry.available_types().len(), 0);
|
||
}
|
||
}
|