# VM Plugin Integration仕様書

## 🎯 概要

NyashのVMバックエンドとプラグインシステム（BID-FFI v1）の統合に関する技術仕様。Everything is Box哲学に基づき、**すべてのBox型（ビルトイン、ユーザー定義、プラグイン）**をVMで統一的に扱えるようにする。

## ⚠️ **現在のVM実装の重大な問題**

1. **ユーザー定義Box未対応** - NewBoxで文字列を返すだけ
2. **birth/finiライフサイクル欠落** - コンストラクタ・デストラクタが呼ばれない
3. **メソッド呼び出しハードコード** - 新メソッド追加が困難

これらを解決し、インタープリターと同等の統一処理を実現する。

## 🏗️ アーキテクチャ

### 統一Box管理モデル

```
┌─────────────────────────────────────────────────┐
│                  Nyash VM                       │
├─────────────────────────────────────────────────┤
│  VMValue                                        │
│  ├─ Integer(i64)     ← 基本型は直接保持       │
│  ├─ String(String)                             │
│  ├─ Bool(bool)                                 │
│  └─ BoxRef(Arc<dyn NyashBox>) ← 複雑型全般    │
├─────────────────────────────────────────────────┤
│  統一Box管理層                                  │
│  ├─ BoxFactory       : 統一Box作成             │
│  ├─ ScopeTracker     : ライフサイクル管理      │
│  └─ MethodDispatcher : 統一メソッド呼び出し    │
├─────────────────────────────────────────────────┤
│  変換レイヤー                                   │
│  ├─ to_nyash_box()   : VMValue → Box          │
│  └─ from_nyash_box() : Box → VMValue          │
├─────────────────────────────────────────────────┤
│  プラグインローダー (PluginLoaderV2)           │
│  └─ BID-FFI v1プロトコルで通信                │
└─────────────────────────────────────────────────┘
```

### VM構造体の完全形

```rust
pub struct VM {
    // 既存フィールド
    registers: HashMap<RegisterId, VMValue>,
    memory: HashMap<MemoryLocation, VMValue>,
    
    // 統一Box管理（新規）
    box_factory: Arc<BoxFactory>,           // 統一Box作成
    plugin_loader: Option<Arc<PluginLoaderV2>>, // プラグイン
    scope_tracker: ScopeTracker,            // finiライフサイクル
    box_declarations: Arc<RwLock<HashMap<String, BoxDeclaration>>>, // ユーザー定義Box
}
```

## 📊 VMValue拡張仕様

### 型定義

```rust
pub enum VMValue {
    // 基本型（既存）
    Integer(i64),
    Float(f64),
    Bool(bool),
    String(String),
    Future(FutureBox),
    Void,
    
    // 拡張型（新規）
    BoxRef(Arc<dyn NyashBox>),
}
```

### 変換規則

#### NyashBox → VMValue

1. **基本型の最適化**
   - IntegerBox → VMValue::Integer（値を直接保持）
   - StringBox → VMValue::String（値を直接保持）
   - BoolBox → VMValue::Bool（値を直接保持）

2. **複雑型の参照保持**
   - PluginBoxV2 → VMValue::BoxRef
   - ユーザー定義Box → VMValue::BoxRef
   - その他のBox → VMValue::BoxRef

#### VMValue → NyashBox

1. **基本型の再Box化**
   - VMValue::Integer → IntegerBox::new()
   - VMValue::String → StringBox::new()
   - VMValue::Bool → BoolBox::new()

2. **参照型のクローン**
   - VMValue::BoxRef → Arc::clone_box()

## 🔄 MIR命令の処理

### NewBox命令の統一実装

```rust
MirInstruction::NewBox { dst, box_type, args } => {
    // 🌟 統一Box作成プロセス
    
    // Step 1: 引数を評価してNyashBoxに変換
    let nyash_args: Vec<Box<dyn NyashBox>> = args.iter()
        .map(|id| self.get_value(*id)?.to_nyash_box())
        .collect::<Result<Vec<_>, _>>()?;
    
    // Step 2: BoxFactory経由で統一作成
    let new_box = self.box_factory.create_box(box_type, &nyash_args)?;
    
    // Step 3: birth実行（ユーザー定義Boxの場合）
    if let Some(instance) = new_box.as_any().downcast_ref::<InstanceBox>() {
        // birthコンストラクタを検索
        let birth_key = format!("birth/{}", args.len());
        
        if let Some(box_decl) = self.box_declarations.read().unwrap().get(&instance.class_name) {
            if let Some(constructor) = box_decl.constructors.get(&birth_key) {
                // birthメソッドを実行
                self.push_scope(); // 新しいスコープ
                self.set_variable("me", new_box.clone()); // me をバインド
                
                // コンストラクタ本体を実行
                let result = self.execute_constructor(constructor, nyash_args)?;
                
                self.pop_scope(); // スコープ終了
            }
        }
    }
    
    // Step 4: プラグインBoxのbirth実行
    #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
    if new_box.as_any().downcast_ref::<PluginBoxV2>().is_some() {
        // プラグインのbirthは既にcreate_box内で実行済み
    }
    
    // Step 5: スコープ追跡に登録（fini用）
    self.scope_tracker.register_box(new_box.clone());
    
    // Step 6: VMValueに変換して格納
    let vm_value = VMValue::from_nyash_box(new_box);
    self.set_value(*dst, vm_value);
}
```

### BoxCall命令の統一処理

```rust
MirInstruction::BoxCall { dst, box_val, method, args, effects } => {
    let box_vm_value = self.get_value(*box_val)?;
    
    // 統一的なメソッド呼び出し
    let result = match &box_vm_value {
        // 基本型の最適化パス
        VMValue::String(s) => {
            self.call_string_method_optimized(s, method, args)?
        },
        VMValue::Integer(i) => {
            self.call_integer_method_optimized(i, method, args)?
        },
        
        // BoxRef経由の汎用パス
        VMValue::BoxRef(arc_box) => {
            let nyash_args = convert_args_to_nyash(args);
            self.call_box_method_generic(arc_box.as_ref(), method, nyash_args)?
        },
        
        _ => return Err(VMError::TypeError("Not a box type"))
    };
    
    if let Some(dst_id) = dst {
        self.set_value(*dst_id, result);
    }
}
```

### ExternCall命令の実装

```rust
MirInstruction::ExternCall { dst, iface_name, method_name, args, effects } => {
    match (iface_name.as_str(), method_name.as_str()) {
        // プラグインBox作成
        ("plugin", "new") => {
            let box_type = self.get_value(args[0])?.to_string();
            let ctor_args = self.convert_args_to_nyash(&args[1..])?;
            
            if let Some(loader) = &self.plugin_loader {
                let plugin_box = loader.create_box(&box_type, ctor_args)?;
                let vm_value = VMValue::from_nyash_box(plugin_box);
                
                if let Some(dst_id) = dst {
                    self.set_value(*dst_id, vm_value);
                }
            }
        },
        
        // 既存のconsole.log等
        ("env.console", "log") => {
            // 既存の処理
        },
        
        _ => {
            println!("ExternCall stub: {}.{}", iface_name, method_name);
        }
    }
}
```

## 🔧 メモリ管理

### 参照カウント管理

1. **BoxRefの作成時**
   - Arc::fromでBox<dyn NyashBox>をArc<dyn NyashBox>に変換
   - 参照カウント = 1

2. **BoxRefのクローン時**
   - Arc::cloneで参照カウント増加
   - 軽量なポインタコピー

3. **BoxRefの破棄時**
   - 参照カウント減少
   - 0になったら自動解放

### スコープとライフタイム

```rust
// VMのスコープ管理
impl VM {
    fn exit_scope(&mut self) {
        // BoxRefを含むレジスタがクリアされると
        // 参照カウントが自動的に減少
        self.registers.clear();
    }
}
```

## 📈 パフォーマンス最適化

### 基本型の直接処理

```rust
// 最適化されたStringメソッド呼び出し
fn call_string_method_optimized(&self, s: &str, method: &str, args: &[ValueId]) 
    -> Result<VMValue, VMError> {
    match method {
        "length" => Ok(VMValue::Integer(s.len() as i64)),
        "substring" => {
            // 引数を直接整数として取得（Box化を回避）
            let start = self.get_value(args[0])?.to_i64()?;
            let end = self.get_value(args[1])?.to_i64()?;
            Ok(VMValue::String(s[start..end].to_string()))
        },
        _ => {
            // 未知のメソッドは汎用パスへ
            let string_box = Box::new(StringBox::new(s));
            self.call_box_method_generic(&*string_box, method, args)
        }
    }
}
```

### プラグイン呼び出しの最適化

1. **メソッドIDキャッシュ**
   - 頻繁に呼ばれるメソッドのIDをキャッシュ
   - 文字列比較を回避

2. **TLV変換の遅延評価**
   - 必要になるまでTLV変換を遅延
   - 基本型は直接渡す

## 🧪 テスト戦略

### 単体テスト

```rust
#[test]
fn test_vm_plugin_box_creation() {
    let plugin_loader = create_test_plugin_loader();
    let mut vm = VM::new_with_plugins(plugin_loader);
    
    // FileBoxの作成
    let result = vm.execute_extern_call(
        "plugin", "new", 
        vec!["FileBox", "test.txt"]
    );
    
    assert!(matches!(result, Ok(VMValue::BoxRef(_))));
}
```

### 統合テスト

```nyash
// VMで実行されるNyashコード
local file = new FileBox("output.txt")
file.write("VM Plugin Test")
local content = file.read()
assert(content == "VM Plugin Test")
```

### パフォーマンステスト

```rust
#[bench]
fn bench_plugin_method_call(b: &mut Bencher) {
    let vm = setup_vm_with_plugins();
    let file_box = create_file_box(&vm);
    
    b.iter(|| {
        vm.call_box_method(&file_box, "write", &["test"])
    });
}
```

## 🚨 エラーハンドリング

### プラグイン関連エラー

```rust
pub enum VMError {
    // 既存のエラー
    TypeError(String),
    RuntimeError(String),
    
    // プラグイン関連（新規）
    PluginNotFound(String),
    PluginMethodError { 
        plugin: String, 
        method: String, 
        error: String 
    },
    PluginInitError(String),
}
```

### エラー伝播

```rust
// プラグインエラーをVMエラーに変換
impl From<PluginError> for VMError {
    fn from(err: PluginError) -> Self {
        match err {
            PluginError::MethodNotFound(m) => {
                VMError::PluginMethodError { 
                    plugin: "unknown".to_string(),
                    method: m,
                    error: "Method not found".to_string()
                }
            },
            // ... 他のエラー変換
        }
    }
}
```

## 📊 メトリクスとモニタリング

### パフォーマンスメトリクス

- プラグイン呼び出し回数
- 平均呼び出し時間
- TLV変換オーバーヘッド
- メモリ使用量

### デバッグ情報

```rust
// デバッグモードでの詳細ログ
if cfg!(debug_assertions) {
    eprintln!("VM: Calling plugin method {}.{}", box_type, method);
    eprintln!("VM: Args: {:?}", args);
    eprintln!("VM: Result: {:?}", result);
}
```

## 🔄 ライフサイクル管理

### スコープ管理とfini呼び出し

```rust
pub struct ScopeTracker {
    scopes: Vec<Scope>,
}

pub struct Scope {
    boxes: Vec<(u64, Arc<dyn NyashBox>)>,  // (id, box)
    variables: HashMap<String, VMValue>,     // ローカル変数
}

impl VM {
    /// スコープ開始
    fn push_scope(&mut self) {
        self.scope_tracker.scopes.push(Scope::new());
    }
    
    /// スコープ終了時の自動fini呼び出し
    fn pop_scope(&mut self) -> Result<(), VMError> {
        if let Some(scope) = self.scope_tracker.scopes.pop() {
            // 逆順でfiniを呼ぶ（作成順と逆）
            for (_, box_ref) in scope.boxes.iter().rev() {
                self.call_fini_if_needed(box_ref)?;
            }
        }
        Ok(())
    }
    
    /// 統一fini呼び出し
    fn call_fini_if_needed(&mut self, box_ref: &Arc<dyn NyashBox>) -> Result<(), VMError> {
        match box_ref.type_name() {
            // ユーザー定義Box
            name if self.box_declarations.read().unwrap().contains_key(name) => {
                if let Some(instance) = box_ref.as_any().downcast_ref::<InstanceBox>() {
                    // finiメソッドが定義されているか確認
                    if let Some(box_decl) = self.box_declarations.read().unwrap().get(name) {
                        if let Some(fini_method) = box_decl.methods.get("fini") {
                            // finiを実行
                            self.set_variable("me", box_ref.clone_box());
                            self.execute_method(fini_method.clone())?;
                        }
                    }
                }
            },
            
            // プラグインBox
            #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
            _ if box_ref.as_any().downcast_ref::<PluginBoxV2>().is_some() => {
                if let Some(plugin) = box_ref.as_any().downcast_ref::<PluginBoxV2>() {
                    plugin.call_fini();
                }
            },
            
            // ビルトインBox（将来finiサポート予定）
            _ => {
                // 現在ビルトインBoxはfiniなし
                // 将来的にはStringBox等もfini対応
            }
        }
        Ok(())
    }
}
```

### ライフサイクルの完全性

```nyash
// 🌟 すべてのBoxが同じライフサイクル

{  // スコープ開始
    local str = new StringBox("hello")      // birth（引数1つ）
    local user = new UserBox("Alice", 25)   // birth（引数2つ）
    local file = new FileBox("test.txt")    // birth（引数1つ）
    
    // 使用
    str.length()
    user.greet()
    file.write("data")
    
}  // スコープ終了 → 自動的にfini呼び出し
   // file.fini() → user.fini() → str.fini() の順
```

## 🎯 統一の利点

### 1. **シンプルな実装**
- すべてのBox型が同じコードパスを通る
- 特殊ケースの削減
- バグの温床排除

### 2. **拡張性**
- 新しいBox型追加が容易
- プラグインも同じ扱い
- 将来の機能追加も簡単

### 3. **パフォーマンス**
- 基本型は最適化パス維持
- 必要時のみBoxRef使用
- メソッドディスパッチの効率化

---

**最終更新**: 2025-08-21  
**関連文書**: 
- [BID-FFI v1 実装仕様書](./bid-ffi-v1-actual-specification.md)
- [Phase 9.78a VM Plugin Integration](../../予定/native-plan/issues/phase_9_78a_vm_plugin_integration.md)
- [Phase 9.78a 深層分析](../../予定/native-plan/issues/phase_9_78a_vm_plugin_integration_deep_analysis.md)
 - [nyash.toml v2.1: BoxRef仕様](../plugin-system/nyash-toml-v2_1-spec.md)

### 付録: 引数エンコード（v2.1 追加）
- TLVタグ: 1=Bool, 2=I32, 3=I64, 4=F32, 5=F64, 6=String, 7=Bytes, 8=Handle(BoxRef)
- BoxRef payload(tag=8): `type_id:u32` + `instance_id:u32`（LE, 8バイト）
- `nyash.toml` の `args` で `{ kind="box", category="plugin" }` を指定したとき、Loaderは `tag=8` を使用

### 返り値（v2.2）
- プラグインが `tag=8` を返した場合、Loaderは `type_id` からBox型名を逆引きし `PluginBoxV2` を構築
- 同一ライブラリでなくてもOK（構成ファイル全体から探索）
