feat(plugin): Fix plugin BoxRef return and Box argument support

- Fixed deadlock in FileBox plugin copyFrom implementation (single lock)
- Added TLV Handle (tag=8) parsing in calls.rs for returned BoxRefs
- Improved plugin loader with config path consistency and detailed logging
- Fixed loader routing for proper Handle type_id/fini_method_id resolution
- Added detailed logging for TLV encoding/decoding in plugin_loader_v2

Test docs/examples/plugin_boxref_return.nyash now works correctly:
- cloneSelf() returns FileBox Handle properly
- copyFrom(Box) accepts plugin Box arguments
- Both FileBox instances close and fini correctly

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-08-21 00:41:26 +09:00
parent af32896574
commit cc2a820af7
274 changed files with 7244 additions and 4608 deletions

View File

@ -0,0 +1,87 @@
# Nyash Plugin System Documentation
## 🎯 Quick Start
**For new developers**: Start with [BID-FFI v1 実装仕様書](./bid-ffi-v1-actual-specification.md)
## 📚 Documentation Index
### 🟢 **Current & Accurate**
- **[bid-ffi-v1-actual-specification.md](./bid-ffi-v1-actual-specification.md)** - **主要仕様書**
- 実際に動作している実装をベースとした正確な仕様
- FileBoxプラグインで実証済み
- プラグイン開発者はここから始める
- **[vm-plugin-integration.md](./vm-plugin-integration.md)** - **VM統合仕様書** 🆕
- VMバックエンドとプラグインシステムの統合
- BoxRef型による統一アーキテクチャ
- パフォーマンス最適化とエラーハンドリング
- **[plugin-tester.md](./plugin-tester.md)** - プラグイン診断ツール
- プラグインの動作確認とデバッグに使用
- `tools/plugin-tester`ツールの使用方法
- **[filebox-bid-mapping.md](./filebox-bid-mapping.md)** - 参考資料
- FileBox APIとプラグイン実装の対応表
- API設計の参考として有用
### 🔄 **Migration & Reference**
- **[migration-guide.md](./migration-guide.md)** - 移行ガイド
- 古いドキュメントから現在の実装への移行方法
- ドキュメント状況の整理
### ⚠️ **Deprecated - 非推奨**
- **[ffi-abi-specification.md](./ffi-abi-specification.md)** - ❌ 理想案、未実装
- **[plugin-system.md](./plugin-system.md)** - ❌ 将来構想
- **[nyash-toml-v2-spec.md](./nyash-toml-v2-spec.md)** - ⚠️ 部分的に古い
## 🚀 For Plugin Developers
### 1. **Read the Specification**
```bash
# 主要仕様書を読む
cat docs/説明書/reference/plugin-system/bid-ffi-v1-actual-specification.md
```
### 2. **Study Working Example**
```bash
# FileBoxプラグインを参考にする
cd plugins/nyash-filebox-plugin
cat src/lib.rs
```
### 3. **Configure Your Plugin**
```bash
# nyash.tomlで設定
cat nyash.toml # 実際の設定形式を確認
```
### 4. **Test Your Plugin**
```bash
# プラグインテスターで確認
cd tools/plugin-tester
cargo build --release
./target/release/plugin-tester check path/to/your/plugin.so
```
## 🔧 For Nyash Core Developers
### Implementation Files
- **[plugin_loader_v2.rs](../../../../src/runtime/plugin_loader_v2.rs)** - プラグインローダー実装
- **[nyash_toml_v2.rs](../../../../src/config/nyash_toml_v2.rs)** - 設定パーサー
- **[tlv.rs](../../../../src/bid/tlv.rs)** - TLVエンコーダー/デコーダー
### Next Steps
- **Phase 3**: MIR ExternCall → plugin system 接続実装
- **Future**: HTTP系ボックスのプラグイン化
## 📞 Support & Issues
- **Working Examples**: `plugins/nyash-filebox-plugin/`
- **Issues**: Report at [GitHub Issues](https://github.com/moe-charm/nyash/issues)
- **Configuration**: `nyash.toml` in project root
---
**Status**: Phase 2 Documentation Reorganization - Completed
**Last Updated**: 2025-08-20

View File

@ -0,0 +1,194 @@
# BID-FFI v1 実装仕様書 (実装ベース)
## 🎯 概要
**これは現在動作している実装をベースとした正確な仕様書です。**
- FileBoxプラグインで実証済み
- plugin_loader_v2.rsの実装に基づく
- 理想案ではなく、実際に動く仕様
## 📋 プラグインAPI仕様
### 必須エクスポート関数
#### 1. ABI Version (オプション)
```c
extern "C" u32 nyash_plugin_abi(void) {
return 1; // BID-FFI v1
}
```
#### 2. 初期化 (オプション)
```c
extern "C" i32 nyash_plugin_init(void) {
// グローバルリソース初期化
// 0=成功, 負数=エラー(プラグイン無効化)
return 0;
}
```
#### 3. メソッド呼び出し (必須)
```c
extern "C" i32 nyash_plugin_invoke(
u32 type_id, // Box型ID (6=FileBox)
u32 method_id, // メソッドID (0=birth, 4294967295=fini)
u32 instance_id, // インスタンスID (0=static call)
const u8* args, // TLV引数
usize args_len, // 引数サイズ
u8* result, // TLV結果バッファ
usize* result_len // [IN/OUT]バッファサイズ
) -> i32; // 0=成功, 負数=エラー
```
#### 4. 終了処理 (オプション)
```c
extern "C" void nyash_plugin_shutdown(void) {
// グローバルリソース解放
}
```
## 📊 エラーコード
```c
#define NYB_SUCCESS 0 // 成功
#define NYB_E_SHORT_BUFFER -1 // バッファ不足
#define NYB_E_INVALID_TYPE -2 // 無効な型ID
#define NYB_E_INVALID_METHOD -3 // 無効なメソッドID
#define NYB_E_INVALID_ARGS -4 // 無効な引数
#define NYB_E_PLUGIN_ERROR -5 // プラグイン内部エラー
#define NYB_E_INVALID_HANDLE -8 // 無効なハンドル
```
## 🏗️ TLV (Type-Length-Value) 形式
### ヘッダー構造
```c
struct TlvHeader {
u16 version; // 1 (BID-FFI v1)
u16 argc; // 引数数
};
```
### エントリー構造
```c
struct TlvEntry {
u8 tag; // 型タグ
u8 reserved; // 0将来拡張用
u16 size; // ペイロードサイズ
// followed by payload data
};
```
### 型タグ定義
```c
#define BID_TAG_BOOL 1 // bool: 1 byte (0/1)
#define BID_TAG_I32 2 // i32: 4 bytes (little-endian)
#define BID_TAG_I64 3 // i64: 8 bytes (little-endian)
#define BID_TAG_F32 4 // f32: 4 bytes (IEEE 754)
#define BID_TAG_F64 5 // f64: 8 bytes (IEEE 754)
#define BID_TAG_STRING 6 // string: UTF-8 bytes
#define BID_TAG_BYTES 7 // bytes: binary data
#define BID_TAG_HANDLE 8 // handle: 8 bytes (type_id + instance_id)
#define BID_TAG_VOID 9 // void: 0 bytes
```
## 🔧 nyash.toml設定仕様
### 基本構造
```toml
[libraries."<library_name>"]
boxes = ["BoxType1", "BoxType2"] # 提供するBox型
path = "./path/to/library.so" # ライブラリパス
[libraries."<library_name>".<BoxType>]
type_id = <number> # Box型ID (必須)
[libraries."<library_name>".<BoxType>.methods]
<method_name> = { method_id = <number> }
```
### 実例 (FileBox)
```toml
[libraries."libnyash_filebox_plugin.so"]
boxes = ["FileBox"]
path = "./plugins/nyash-filebox-plugin/target/release/libnyash_filebox_plugin.so"
[libraries."libnyash_filebox_plugin.so".FileBox]
type_id = 6
[libraries."libnyash_filebox_plugin.so".FileBox.methods]
birth = { method_id = 0 } # コンストラクタ
open = { method_id = 1 }
read = { method_id = 2 }
write = { method_id = 3 }
close = { method_id = 4 }
fini = { method_id = 4294967295 } # デストラクタ (u32::MAX)
```
## 🔄 必須メソッド規約
### birth() - コンストラクタ
- **method_id**: 必ず 0
- **引数**: TLV形式型依存
- **戻り値**: instance_id (u32, little-endian, 4bytes)
- **呼び出し**: instance_id=0 (static call)
### fini() - デストラクタ
- **method_id**: 必ず 4294967295 (u32::MAX)
- **引数**: 空のTLV (version=1, argc=0)
- **戻り値**: Void
- **呼び出し**: 対象のinstance_id
## 📝 PluginBoxV2構造体
```rust
pub struct PluginBoxV2 {
pub box_type: String, // "FileBox"
pub type_id: u32, // 6
pub invoke_fn: InvokeFn, // 関数ポインタ
pub instance_id: u32, // プラグイン生成ID
pub fini_method_id: Option<u32>, // finiメソッドID
}
```
## 🚨 重要な制約
### メモリ管理
- **プラグイン責任**: プラグインが確保したメモリはプラグインが解放
- **2段階呼び出し**:
1. result=NULL でサイズ取得
2. ホストがバッファ確保後、実際のデータ取得
### 文字列エンコーディング
- **UTF-8必須**: すべての文字列はUTF-8
- **NUL終端不要**: lengthが正確性を保証
### インスタンス管理
- **instance_id**: プラグイン内で一意
- **birth順序**: birth() → 実際のメソッド → fini()
- **共有・複製**: clone_box()は新birth()、share_box()は同一instance_id
## 🔗 実装ファイル
### Nyash側
- `src/runtime/plugin_loader_v2.rs` - プラグインローダー
- `src/config/nyash_toml_v2.rs` - 設定パーサー
- `src/bid/tlv.rs` - TLVエンコーダー/デコーダー
### プラグイン例
- `plugins/nyash-filebox-plugin/src/lib.rs` - FileBox実装
- `plugins/nyash-test-multibox/src/lib.rs` - マルチBox実装
## ✅ 動作確認済み
- ✅ FileBoxプラグイン完全動作
- ✅ birth/finiライフサイクル
- ✅ TLVエンコーディング/デコーディング
- ✅ clone_box/share_box メソッド
- ✅ マルチインスタンス管理
---
**最終更新**: 2025年8月20日 - Phase 1現実調査完了
**ベース**: plugin_loader_v2.rs実装 + FileBox実証
**状態**: Production Ready (実際に動作中)

View File

@ -0,0 +1,626 @@
# Box FFI/ABI v0 (BID-1 Enhanced Edition)
> ⚠️ **DEPRECATED - 理想案、未実装**
>
> この文書は将来の理想的なプラグインシステム設計案です。
> **現在の実装とは異なります。**
>
> **実際に動作している仕様については、以下を参照してください:**
> - [BID-FFI v1 実装仕様書](./bid-ffi-v1-actual-specification.md) - 現在動作中の仕様
> - [nyash.toml設定例](../../../../nyash.toml) - 実際の設定形式
> - [plugin_loader_v2.rs](../../../../src/runtime/plugin_loader_v2.rs) - 実装詳細
Purpose
- Define a language-agnostic ABI to call external libraries as Boxes.
- Serve as a single source of truth for MIR ExternCall, WASM RuntimeImports, VM stubs, and future language codegens (TS/Python/Rust/LLVM IR).
- Support Everything is Box philosophy with efficient Handle design.
Design Goals
- Simple first: UTF-8 strings as (ptr,len), i32 for small integers, 32-bit linear memory alignment friendly.
- Deterministic and portable across WASM/VM/native backends.
- Align with MIR effect system (pure/mut/io/control) to preserve optimization safety.
- Efficient Handle design for Box instances (type_id + instance_id).
Core Types
- i32, i64, f32, f64, bool (0|1)
- string: UTF-8 in linear memory as (ptr: usize, len: usize)
- bytes: Binary data as (ptr: usize, len: usize)
- handle: Efficient Box handle as {type_id: u32, instance_id: u32} or packed u64
- array(T): (ptr: usize, len: usize)
- void (no return value)
Memory & Alignment
- All pointers are platform-dependent (usize): 32-bit in WASM MVP, 64-bit on native x86-64.
- Alignment: 8-byte boundary for all structures.
- Strings/arrays must be contiguous in linear memory; no NUL terminator required (len is authoritative).
- Box layout examples for built-ins (for backends that materialize Boxes in memory):
- Header: [type_id:i32][ref_count:i32][field_count:i32]
- StringBox: header + [data_ptr:usize][length:usize]
Handle Design (BID-1 Enhancement)
- Handle represents a Box instance with two components:
- type_id: u32 (1=StringBox, 6=FileBox, 7=FutureBox, 8=P2PBox, etc.)
- instance_id: u32 (unique instance identifier)
- Can be passed as single u64 (type_id << 32 | instance_id) or struct
Naming & Resolution
- Interface namespace: `env.console`, `env.canvas`, `nyash.file`, etc.
- Method: `log`, `fillRect`, `fillText`, `open`, `read`, `write`, `close`.
- Fully-qualified name: `env.console.log`, `env.canvas.fillRect`, `nyash.file.open`.
Calling Convention (BID-1)
- Single entry point: `nyash_plugin_invoke`
- Arguments passed as BID-1 TLV format
- Return values in BID-1 TLV format
- Two-call pattern for dynamic results:
1. First call with null result buffer to get size
2. Second call with allocated buffer to get data
Error Model (BID-1)
- Standardized error codes:
- NYB_SUCCESS (0): Operation successful
- NYB_E_SHORT_BUFFER (-1): Buffer too small
- NYB_E_INVALID_TYPE (-2): Invalid type ID
- NYB_E_INVALID_METHOD (-3): Invalid method ID
- NYB_E_INVALID_ARGS (-4): Invalid arguments
- NYB_E_PLUGIN_ERROR (-5): Plugin internal error
Effects
- Each BID method declares one of: pure | mut | io | control.
- Optimizer/verifier rules:
- pure: reordering permitted, memoization possible
- mut: preserve order w.r.t same resource
- io: preserve program order strictly
- control: affects CFG; handled as terminators or dedicated ops
BID-1 TLV Format
```c
// BID-1 TLV specification - unified format for arguments and results
struct BidTLV {
u16 version; // 1 (BID-1)
u16 argc; // argument count
// followed by TLVEntry array
};
struct TLVEntry {
u8 tag; // type tag
u8 reserved; // future use (0)
u16 size; // payload size
// followed by payload data
};
// Tag definitions (Phase 1)
#define BID_TAG_BOOL 1 // payload: 1 byte (0/1)
#define BID_TAG_I32 2 // payload: 4 bytes (little-endian)
#define BID_TAG_I64 3 // payload: 8 bytes (little-endian)
#define BID_TAG_F32 4 // payload: 4 bytes (IEEE 754)
#define BID_TAG_F64 5 // payload: 8 bytes (IEEE 754)
#define BID_TAG_STRING 6 // payload: UTF-8 bytes
#define BID_TAG_BYTES 7 // payload: binary data
#define BID_TAG_HANDLE 8 // payload: 8 bytes (type_id + instance_id)
// Phase 2 reserved
#define BID_TAG_RESULT 20 // Result<T,E>
#define BID_TAG_OPTION 21 // Option<T>
#define BID_TAG_ARRAY 22 // Array<T>
```
Plugin API (BID-1)
```c
// src/bid/plugin_api.h - Plugin API complete version
// Host function table
typedef struct {
void* (*alloc)(size_t size); // Memory allocation
void (*free)(void* ptr); // Memory deallocation
void (*wake)(u32 future_id); // FutureBox wake
void (*log)(const char* msg); // Log output
} NyashHostVtable;
// Plugin information
typedef struct {
u32 type_id; // Box type ID
const char* type_name; // "FileBox" etc.
u32 method_count; // Method count
const NyashMethodInfo* methods; // Method table
} NyashPluginInfo;
typedef struct {
u32 method_id; // Method ID
const char* method_name; // "open", "read" etc.
u32 signature_hash; // Type signature hash
} NyashMethodInfo;
// Plugin API (required implementation)
extern "C" {
// Get ABI version
u32 nyash_plugin_abi(void);
// Initialize (host integration & metadata registration)
i32 nyash_plugin_init(const NyashHostVtable* host, NyashPluginInfo* info);
// Unified method invocation
i32 nyash_plugin_invoke(
u32 type_id, // Box type ID
u32 method_id, // Method ID
u32 instance_id, // Instance ID
const u8* args, // BID-1 TLV arguments
size_t args_len, // Arguments size
u8* result, // BID-1 TLV result
size_t* result_len // Result size (in/out)
);
// Shutdown
void nyash_plugin_shutdown(void);
}
```
BID (Box Interface Definition) YAML
```yaml
version: 1 # BID-1 format
interfaces:
- name: env.console
box: Console
methods:
- name: log
params: [ {string: msg} ]
returns: void
effect: io
- name: env.canvas
box: Canvas
methods:
- name: fillRect
params:
- {string: canvas_id}
- {i32: x}
- {i32: y}
- {i32: w}
- {i32: h}
- {string: color}
returns: void
effect: io
- name: fillText
params:
- {string: canvas_id}
- {string: text}
- {i32: x}
- {i32: y}
- {string: font}
- {string: color}
returns: void
effect: io
- name: nyash.file
box: FileBox
type_id: 6
methods:
- name: open
method_id: 1
params:
- {string: path}
- {string: mode}
returns: {handle: FileBox}
effect: io
- name: read
method_id: 2
params:
- {handle: handle}
- {i32: size}
returns: {bytes: data}
effect: io
- name: write
method_id: 3
params:
- {handle: handle}
- {bytes: data}
returns: {i32: written}
effect: io
- name: close
method_id: 4
params:
- {handle: handle}
returns: void
effect: io
```
FileBox Plugin Example (BID-1)
```c
// plugins/nyash-file/src/lib.c
#include "nyash_plugin_api.h"
#include <stdio.h>
#include <stdlib.h>
static const NyashHostVtable* g_host = NULL;
// ABI version
u32 nyash_plugin_abi(void) {
return 1; // BID-1 support
}
// Method table
static const NyashMethodInfo FILE_METHODS[] = {
{0, "birth", 0xBEEFCAFE}, // birth(path: string, mode: string) - Constructor
{1, "open", 0x12345678}, // open(path: string, mode: string) -> Handle
{2, "read", 0x87654321}, // read(handle: Handle, size: i32) -> Bytes
{3, "write", 0x11223344}, // write(handle: Handle, data: Bytes) -> i32
{4, "close", 0xABCDEF00}, // close(handle: Handle) -> Void
{5, "fini", 0xDEADBEEF}, // fini() - Destructor
};
// Initialize
i32 nyash_plugin_init(const NyashHostVtable* host, NyashPluginInfo* info) {
info->type_id = 6; // FileBox
info->type_name = "FileBox";
info->method_count = 4;
info->methods = FILE_METHODS;
// Save host functions
g_host = host;
return NYB_SUCCESS;
}
// Method execution
i32 nyash_plugin_invoke(u32 type_id, u32 method_id, u32 instance_id,
const u8* args, size_t args_len,
u8* result, size_t* result_len) {
if (type_id != 6) return NYB_E_INVALID_TYPE;
switch (method_id) {
case 1: return file_open(args, args_len, result, result_len);
case 2: return file_read(instance_id, args, args_len, result, result_len);
case 3: return file_write(instance_id, args, args_len, result, result_len);
case 4: return file_close(instance_id, args, args_len, result, result_len);
default: return NYB_E_INVALID_METHOD;
}
}
// File open implementation
static i32 file_open(const u8* args, size_t args_len,
u8* result, size_t* result_len) {
// BID-1 TLV parsing
BidTLV* tlv = (BidTLV*)args;
if (tlv->version != 1 || tlv->argc != 2) {
return NYB_E_INVALID_ARGS;
}
// Extract arguments: path, mode
const char* path = extract_string_arg(tlv, 0);
const char* mode = extract_string_arg(tlv, 1);
// Open file
FILE* fp = fopen(path, mode);
if (!fp) return NYB_E_PLUGIN_ERROR;
// Generate handle
u32 handle_id = register_file_handle(fp);
// BID-1 result creation
if (!result) {
*result_len = sizeof(BidTLV) + sizeof(TLVEntry) + 8; // Handle
return NYB_SUCCESS;
}
// Return Handle{type_id: 6, instance_id: handle_id} in TLV
encode_handle_result(result, 6, handle_id);
return NYB_SUCCESS;
}
// birth implementation - Box constructor
static i32 file_birth(u32 instance_id, const u8* args, size_t args_len,
u8* result, size_t* result_len) {
// Parse constructor arguments
BidTLV* tlv = (BidTLV*)args;
const char* path = extract_string_arg(tlv, 0);
const char* mode = extract_string_arg(tlv, 1);
// Create instance
FileInstance* instance = malloc(sizeof(FileInstance));
instance->fp = fopen(path, mode);
instance->buffer = NULL;
// Register instance
register_instance(instance_id, instance);
// No return value for birth
*result_len = 0;
return NYB_SUCCESS;
}
// fini implementation - Box destructor
static i32 file_fini(u32 instance_id) {
FileInstance* instance = get_instance(instance_id);
if (!instance) return NYB_E_INVALID_HANDLE;
// Free plugin-allocated memory
if (instance->buffer) {
free(instance->buffer);
}
// Close file handle
if (instance->fp) {
fclose(instance->fp);
}
// Free instance
free(instance);
unregister_instance(instance_id);
return NYB_SUCCESS;
}
void nyash_plugin_shutdown(void) {
// Cleanup all remaining instances
cleanup_all_instances();
}
```
WASM Mapping (RuntimeImports)
- Import examples:
- `(import "env" "console_log" (func $console_log (param i32 i32)))` // (ptr,len)
- `(import "env" "canvas_fillRect" (func $canvas_fillRect (param i32 i32 i32 i32 i32 i32)))`
- `(import "env" "canvas_fillText" (func $canvas_fillText (param i32 i32 i32 i32 i32 i32 i32 i32)))` // two strings as (ptr,len) each
- Host responsibilities:
- Resolve strings from memory via `(ptr,len)` using TextDecoder('utf-8')
- Map to DOM/Canvas/Console as appropriate
- For plugins: use dlopen/dlsym to load and invoke nyash_plugin_* functions
WASM Mapping Rules (v0)
- String marshalling: UTF-8 `(ptr:i32, len:i32)`; memory exported as `memory`.
- Alignment: `ptr` 4-byte aligned is推奨必須ではないが実装簡素化のため)。
- Import naming: `env.<iface>_<method>` or nested `env` modules実装都合でどちらでも可)。
- 推奨: `env.console_log`, `env.canvas_fillRect`, `env.canvas_fillText`
- Argument order: 文字列は `(ptr,len)` を1引数扱いで連続配置複数文字列はその都度 `(ptr,len)`
- Return: v0では`void`または整数のみ複合戻りはout-paramに委譲)。
- Memory growth: ホストは`memory.buffer`の再割当を考慮必要に応じて毎回ビューを取り直す)。
RuntimeImportsとBIDの関係
- `RuntimeImports` ABI/BID をWASM向けに具体化した実装レイヤーWASM専用の橋渡し)。
- 生成方針: 将来的にBIDYAML/JSONから`importObject``(import ...)`宣言を自動生成する
- BIDWASM:
- `env.console.log(string msg)` `console_log(ptr:i32, len:i32)`
- `env.canvas.fillRect(string canvasId, i32 x, i32 y, i32 w, i32 h, string color)`
`canvas_fillRect(id_ptr, id_len, x, y, w, h, color_ptr, color_len)`
============================================================
ABIの確定事項BID-1, 日本語
============================================================
基本方針BID-1
- 文字列は UTF-8 `(ptr:usize, len:usize)` で受け渡すNUL終端不要内部NUL禁止)。
- 配列/バイト列は `(ptr:usize, len:usize)` とする
- 数値は WASM/LLVM と親和性の高い素のプリミティブi32/i64/f32/f64)。
- 真偽値は `i32` 0=false, 1=true。
- ポインタは `usize`WASM MVPは32bitネイティブx86-64は64bit)。
- エンディアンはリトルエンディアンWASM/一般的なネイティブと一致)。
- 呼出規約は単一エントリーポイント `nyash_plugin_invoke` + BID-1 TLV形式
- 戻り値はBID-1 TLV形式2回呼び出しパターンでサイズ取得)。
- メモリは `memory` をエクスポートWASM)。ホスト側で管理
- 効果effect BID に必須pure は再順序化可mut/io は順序保持
- 同期のみ非同期は将来拡張)。
- スレッド前提シングルスレッドPhase 1)。
メモリ管理戦略
- 2回呼び出しパターン
1. result=NULLでサイズ取得
2. ホストがallocateして結果取得
- 文字列エンコーディングUTF-8必須内部NUL禁止
- ハンドル再利用対策generation追加で ABA問題回避将来
Boxライフサイクル管理
- **birth/fini原則**
- method_id=0 は必ず`birth()`コンストラクタ
- method_id=最大値 は必ず`fini()`デストラクタ
- birthで割り当てたリソースはfiniで解放
- **メモリ所有権**
- プラグインがmalloc()したメモリ プラグインがfree()
- ホストが提供したバッファ ホストが管理
- 引数として渡されたメモリ read-onlyとして扱う
- **インスタンス管理**
- instance_idはホストが発行管理
- プラグインは内部マップでinstance_id 実装構造体を管理
- nyash_plugin_shutdown()で全インスタンスをクリーンアップ
型と表現BID-1
- `i32`: 32bit 符号付き整数
- `i64`: 64bit 符号付き整数WASMではJSブリッジ注意Host側はBigInt等
- `f32/f64`: IEEE 754
- `bool`: i320/1
- `string`: UTF-8 `(ptr:usize, len:usize)`
- `bytes`: バイナリデータ `(ptr:usize, len:usize)`
- `array<T>`: `(ptr:usize, len:usize)`
- `handle`: Box参照 `{type_id:u32, instance_id:u32}` または packed u64
- `void`: 戻り値なし
BidType Rust実装
```rust
#[derive(Clone, Debug, PartialEq)]
pub enum BidType {
// プリミティブFFI境界で値渡し
Bool, I32, I64, F32, F64,
String, Bytes,
// Handle設計
Handle { type_id: u32, instance_id: u32 },
// メタ型
Void,
// Phase 2予約
Option(Box<BidType>),
Result(Box<BidType>, Box<BidType>),
Array(Box<BidType>),
}
```
アラインメント/境界
- `ptr` 8byte アライン必須構造体の効率的アクセス)。
- 範囲外アクセスは未定義ではなくHostが防ぐ/検証する方針将来Verifier/境界チェック生成)。
- プラットフォーム依存
- Linux x86-64: 8バイト境界
- WASM MVP: 4バイト境界互換性のため
命名規約
- `env.console.log`, `env.canvas.fillRect` のように `<namespace>.<iface>.<method>`
- WASM import 名は `env.console_log` 等の平坦化でも可生成側で一貫)。
- プラグイン関数名: `nyash_plugin_*` プレフィックス必須
エラー/例外
- BID-1標準エラーコード使用NYB_*)。
- 失敗は整数ステータスで返却
- 例外/シグナルは範囲外
セキュリティ/権限将来
- BID に必要権限console/canvas/storage/net…)を記述HostはAllowlistで制御Phase 9.9)。
実装上の注意点
- ハンドル再利用/ABA: generation追加で回避
- スレッド前提: シングルスレッド前提を明記
- メソッドID衝突: ビルド時固定で回避
- エラー伝播: トランスポート/ドメインエラー分離
- 文字列エンコード: UTF-8必須内部NUL禁止
============================================================
BIDサンプルYAML, 日本語
============================================================
```yaml
version: 0
interfaces:
- name: env.console
box: Console
methods:
- name: log
params: [ { string: msg } ]
returns: void
effect: io
- name: env.canvas
box: Canvas
methods:
- name: fillRect
params:
- { string: canvas_id }
- { i32: x }
- { i32: y }
- { i32: w }
- { i32: h }
- { string: color }
returns: void
effect: io
- name: fillText
params:
- { string: canvas_id }
- { string: text }
- { i32: x }
- { i32: y }
- { string: font }
- { string: color }
returns: void
effect: io
```
ファイルとしてのサンプル同等内容
- `docs/nyir/bid_samples/console.yaml`
- `docs/nyir/bid_samples/canvas.yaml`
============================================================
Host側 importObject サンプルブラウザ, 日本語
============================================================
```js
// 文字列(ptr,len)の復元ヘルパ
function utf8FromMemory(memory, ptr, len) {
const u8 = new Uint8Array(memory.buffer, ptr, len);
return new TextDecoder('utf-8').decode(u8);
}
const importObject = {
env: {
print: (v) => console.log(v),
print_str: (ptr, len) => {
console.log(utf8FromMemory(wasmInstance.exports.memory, ptr, len));
},
console_log: (ptr, len) => {
console.log(utf8FromMemory(wasmInstance.exports.memory, ptr, len));
},
canvas_fillRect: (idPtr, idLen, x, y, w, h, colorPtr, colorLen) => {
const mem = wasmInstance.exports.memory;
const id = utf8FromMemory(mem, idPtr, idLen);
const color = utf8FromMemory(mem, colorPtr, colorLen);
const cv = document.getElementById(id);
if (!cv) return;
const ctx = cv.getContext('2d');
ctx.fillStyle = color;
ctx.fillRect(x, y, w, h);
},
canvas_fillText: (idPtr, idLen, textPtr, textLen, x, y, fontPtr, fontLen, colorPtr, colorLen) => {
const mem = wasmInstance.exports.memory;
const id = utf8FromMemory(mem, idPtr, idLen);
const text = utf8FromMemory(mem, textPtr, textLen);
const font = utf8FromMemory(mem, fontPtr, fontLen);
const color = utf8FromMemory(mem, colorPtr, colorLen);
const cv = document.getElementById(id);
if (!cv) return;
const ctx = cv.getContext('2d');
ctx.font = font;
ctx.fillStyle = color;
ctx.fillText(text, x, y);
}
}
};
```
============================================================
ExternCall WASM 呼び出しの例日本語
============================================================
Nyash コード概念:
```
console = new WebConsoleBox("output")
console.log("Hello Nyash!")
canvas = new WebCanvasBox("game-canvas", 400, 300)
canvas.fillRect(50, 50, 80, 60, "red")
```
MIRExternCall化のイメージ:
```
ExternCall { iface: "env.console", method: "log", args: [ string("Hello Nyash!") ] }
ExternCall { iface: "env.canvas", method: "fillRect", args: [ string("game-canvas"), 50, 50, 80, 60, string("red") ] }
```
WASM import 呼び出し概念:
```
call $console_log(msg_ptr, msg_len)
call $canvas_fillRect(id_ptr, id_len, 50, 50, 80, 60, color_ptr, color_len)
```
備考
- 文字列定数は data segment に配置し実行時に (ptr,len) を与える
- 動的文字列はランタイムでバッファ確保→(ptr,len) を渡す
VM Mapping (Stub v0)
- Maintain a registry of externs by FQN (e.g., env.console.log) function pointer.
- Console: print to stdout; Canvas: log params or no-op.
LLVM IR Mapping (Preview)
- Declare external functions with matching signatures (i32/i64/f32/f64/bool, i8* + i32 for strings).
- Example: `declare void @env_console_log(i8* nocapture, i32)`
- Strings allocated in data segment or heap; pass pointer + length.
Versioning
- `version: 0` for the first public draft.
- Backward-compatible extensions should add new methods/imports; breaking changes bump major.
Open Points (to validate post v0)
- Boxref passing across FFI boundaries (opaque handles vs pointers).
- Async externs and scheduling.
- Error model harmonization (status vs result-box).

View File

@ -0,0 +1,68 @@
FileBox × BID-FFI 対応表Nyash API ↔ Plugin ABI
概要
- 目的: Nyash言語における `FileBox` のAPIを、BID-FFIプラグイン実装C ABIと正確に対応付ける。
- 設置: C:\git\nyash-project\nyash\docs\説明書\reference\box-design\filebox-bid-mapping.mdWindowsパス例
前提
- BID-FFI v12段階応答/ShortBuffer=-1
- TLVヘッダ: `u16 version(=1)`, `u16 argc`
- TLVエントリ: `u8 tag`, `u8 reserved(0)`, `u16 size`, payload
- 主要タグ: 1=Bool, 2=I32, 3=I64, 4=F32, 5=F64, 6=String, 7=Bytes, 8=Handle(u64), 9=Void
メソッドIDプラグイン側
- 0: birth(instance生成) → 戻り値: u32 instance_id暫定
- 1: open(String path, String mode) → Void
- 2: read(I32 size) → Bytes
- 3: write(Bytes data) → I32書込バイト数
- 4: close() → Void
- 0xFFFF_FFFF: fini破棄
Nyash API ↔ Plugin ABI 対応
- 構築: `new FileBox(path: string)`
- 既定動作: プラグイン設定が有効な場合、birth→open(path, "rw") を内部実行
- フォールバック: プラグインが無効/未設定ならビルトインFileBoxを使用
- 書込: `FileBox.write(data: string)`
- 変換: String → BytesUTF-8
- 呼出: method_id=3write
- 戻り: I32 を受け取り、Nyash側は "ok" を返却(将来は書込サイズも返せる拡張余地)
- 読取: `FileBox.read([size: integer])`
- 変換: 省略時デフォルト 1MB1_048_576を指定
- 呼出: method_id=2read
- 戻り: Bytes → StringUTF-8として解釈、失敗時はlossy
- 閉じ: `FileBox.close()`
- 呼出: method_id=4close
- 戻り: Void → Nyash側は "ok"
エラーモデル(戻り値)
- 0: 成功
- -1: ShortBuffer2段階応答。副作用なしで必要サイズを *result_len に返却)
- -2: InvalidType
- -3: InvalidMethod
- -4: InvalidArgs
- -5: PluginError
- -8: InvalidHandle
Nyashコード
```
// プラグイン優先で FileBox を生成
local f
f = new FileBox("/tmp/nyash_example.txt")
f.write("Hello from Nyash via plugin!")
print("READ=" + f.read())
f.close()
```
実装メモ(現在の挙動)
- コンストラクタ: プラグイン有効時は birth→open("rw")。指定モードでの open は将来のAPI拡張候補例: `FileBox.open(mode)`)。
- read(size): Nyashからサイズを指定するAPIは次段で追加予定。現状は既定1MBで読み取り。
- write: 書込サイズはプラグインからI32で返るが、Nyash側APIは簡便化のため "ok" を返却(将来拡張余地)。
関連ドキュメント
- plugin-ABI: docs/説明書/reference/box-design/ffi-abi-specification.md
- plugin system: docs/説明書/reference/box-design/plugin-system.md
- plugin-tester: docs/説明書/reference/plugin-tester.md

View File

@ -0,0 +1,104 @@
# Plugin Documentation Migration Guide
## 🎯 概要
このガイドは、Nyashプラグインシステムの古いドキュメントから実際の実装に移行するためのものです。
## 📚 Documentation Status
### ✅ **Current Working Specification**
- **[BID-FFI v1 実装仕様書](./bid-ffi-v1-actual-specification.md)** - **RECOMMENDED**
- 実際に動作している実装をベースとした正確な仕様
- FileBoxプラグインで実証済み
- `plugin_loader_v2.rs`の実装に基づく
### ⚠️ **Deprecated Documentation**
- **[ffi-abi-specification.md](./ffi-abi-specification.md)** - ❌ DEPRECATED
- 理想的な設計案だが未実装
- MIR ExternCall設計が含まれているが、実際には使われていない
- **[plugin-system.md](./plugin-system.md)** - ❌ DEPRECATED
- YAML DSLを使った将来構想
- 現在の実装とは大きく異なる
- **[nyash-toml-v2-spec.md](./nyash-toml-v2-spec.md)** - ⚠️ PARTIALLY OUTDATED
- 基本構造は正しいが、実際の形式と部分的に異なる
### ✅ **Still Accurate Documentation**
- **[plugin-tester.md](./plugin-tester.md)** - ✅ CURRENT
- プラグイン診断ツールの使用方法
- 実際のツールと一致
- **[filebox-bid-mapping.md](./filebox-bid-mapping.md)** - ✅ USEFUL REFERENCE
- FileBox APIとプラグイン実装の対応表
- 開発時の参考資料として有効
## 🔄 Migration Steps
### For Plugin Developers
1. **Start with**: [BID-FFI v1 実装仕様書](./bid-ffi-v1-actual-specification.md)
2. **Refer to**: [実際のnyash.toml](../../../../nyash.toml) for configuration format
3. **Use**: [plugin-tester](../../../../tools/plugin-tester/) for testing
4. **Study**: [FileBox plugin](../../../../plugins/nyash-filebox-plugin/) as reference implementation
### For Nyash Core Developers
1. **Phase 1**: ✅ COMPLETED - Documentation cleanup with deprecation notices
2. **Phase 2**: ✅ COMPLETED - Accurate specification creation
3. **Phase 3**: 🚧 TODO - MIR ExternCall implementation to connect with plugin system
## 🎯 Key Differences
### Old Documentation vs Reality
| Aspect | Old Docs | Reality |
|--------|----------|---------|
| Configuration | YAML DSL | TOML format |
| API Design | Complex handle system | Simple TLV + method_id |
| MIR Integration | Fully designed | Stub only |
| ABI Version | Multiple versions | BID-FFI v1 only |
### Working Configuration Format
**Old (in deprecated docs)**:
```yaml
# filebox.plugin.yaml
schema: 1
apis:
- sig: "FileBox::open(path: string) -> FileBox"
```
**Current (actual)**:
```toml
[libraries."libnyash_filebox_plugin.so"]
boxes = ["FileBox"]
path = "./plugins/nyash-filebox-plugin/target/release/libnyash_filebox_plugin.so"
[libraries."libnyash_filebox_plugin.so".FileBox.methods]
birth = { method_id = 0 }
open = { method_id = 1 }
```
## 📞 FFI Interface
**Old (complex)**:
- Multiple entry points
- Complex handle management
- Dynamic type discovery
**Current (simple)**:
- Single entry point: `nyash_plugin_invoke`
- Fixed TLV protocol
- Static configuration in nyash.toml
## 🚀 Next Steps
1.**Documentation Cleanup**: Completed
2. 🚧 **MIR Integration**: Implement ExternCall → plugin system connection
3. 🔮 **Future**: Consider implementing some ideas from deprecated docs
---
**Last Updated**: 2025-08-20
**Status**: Documentation reorganization Phase 2 completed

View File

@ -0,0 +1,163 @@
# nyash.toml v2 仕様 - 究極のシンプル設計
> ⚠️ **PARTIALLY OUTDATED - 部分的に古い**
>
> この文書の基本構造は正しいですが、実際の形式と一部異なります。
>
> **最新の実際の設定形式については、以下を参照してください:**
> - [BID-FFI v1 実装仕様書](./bid-ffi-v1-actual-specification.md) - 現在動作中の仕様
> - [nyash.toml設定例](../../../../nyash.toml) - 実際の設定形式
## 🎯 概要
**革命的シンプル設計**: nyash.toml中心アーキテクチャ + 最小限FFI
## 📝 nyash.toml v2形式
### マルチBox型プラグイン対応
```toml
[libraries]
# ライブラリ定義1つのプラグインで複数のBox型を提供可能
"libnyash_filebox_plugin.so" = {
boxes = ["FileBox"],
path = "./target/release/libnyash_filebox_plugin.so"
}
# 将来の拡張例: 1つのプラグインで複数Box型
"libnyash_network_plugin.so" = {
boxes = ["SocketBox", "HTTPServerBox", "HTTPClientBox"],
path = "./target/release/libnyash_network_plugin.so"
}
# FileBoxの型情報定義
[libraries."libnyash_filebox_plugin.so".FileBox]
type_id = 6
abi_version = 1 # ABIバージョンもここに
[libraries."libnyash_filebox_plugin.so".FileBox.methods]
# method_id だけで十分(引数情報は実行時チェック)
birth = { method_id = 0 }
open = { method_id = 1 }
read = { method_id = 2 }
write = { method_id = 3 }
close = { method_id = 4 }
fini = { method_id = 4294967295 } # 0xFFFFFFFF
```
## 🚀 究極のシンプルFFI
### プラグインが実装する関数
#### 必須: メソッド実行エントリーポイント
```c
// 唯一の必須関数 - すべてのメソッド呼び出しはここから
extern "C" fn nyash_plugin_invoke(
type_id: u32, // Box型ID例: FileBox = 6
method_id: u32, // メソッドID0=birth, 0xFFFFFFFF=fini
instance_id: u32, // インスタンスID0=static/birth
args: *const u8, // TLVエンコード引数
args_len: usize,
result: *mut u8, // TLVエンコード結果バッファ
result_len: *mut usize // [IN/OUT]バッファサイズ
) -> i32 // 0=成功, 負=エラー
```
#### オプション: グローバル初期化
```c
// プラグインロード時に1回だけ呼ばれる実装は任意
extern "C" fn nyash_plugin_init() -> i32 {
// グローバルリソースの初期化
// 設定ファイルの読み込み
// ログファイルのオープン
// 0=成功, 負=エラー(プラグインは無効化される)
}
```
### 廃止されたAPI
```c
// ❌ これらは全部不要!
nyash_plugin_abi_version() // → nyash.tomlのabi_version
nyash_plugin_get_box_count() // → nyash.tomlのboxes配列
nyash_plugin_get_box_info() // → nyash.tomlから取得
NyashHostVtable // → 完全廃止!
```
## 📊 設計原則
### 1. **Single Source of Truth**
- すべてのメタ情報はnyash.tomlに集約
- プラグインは純粋な実装のみ
### 2. **Zero Dependencies**
- Host VTable廃止 = 依存関係ゼロ
- プラグインは完全に独立
### 3. **シンプルなライフサイクル**
- `init` (オプション): プラグインロード時の初期化
- `birth` (method_id=0): インスタンス作成
- 各種メソッド: インスタンス操作
- `fini` (method_id=0xFFFFFFFF): 論理的終了
### 4. **ログ出力**
```rust
// プラグインは自己完結でログ出力
eprintln!("[FileBox] Opened: {}", path); // 標準エラー
// または専用ログファイル
let mut log = File::create("plugin_debug.log")?;
writeln!(log, "{}: FileBox birth", chrono::Local::now())?;
```
### 5. **init関数の活用例**
```rust
static mut LOG_FILE: Option<File> = None;
#[no_mangle]
pub extern "C" fn nyash_plugin_init() -> i32 {
// ログファイルを事前に開く
match File::create("filebox.log") {
Ok(f) => {
unsafe { LOG_FILE = Some(f); }
0 // 成功
}
Err(_) => -1 // エラー → プラグイン無効化
}
}
```
## 🔧 実装の流れ
### Phase 1: nyash.toml v2パーサー
1. 新形式の読み込み
2. Box型情報の抽出
3. メソッドID管理
### Phase 2: プラグインローダー簡素化
1. `nyash_plugin_init`(オプション)と`nyash_plugin_invoke`(必須)をロード
2. nyash.tomlベースの型登録
3. Host VTable関連コードを削除
4. init関数が存在し失敗した場合はプラグインを無効化
### Phase 3: プラグイン側の対応
1. abi/get_box_count/get_box_info関数を削除
2. init関数は必要に応じて実装グローバル初期化
3. invoke関数でメソッド処理
4. ログ出力を自己完結に
## 🎉 メリット
1. **究極のシンプルさ** - 基本的にFFI関数1つinitはオプション
2. **保守性向上** - 複雑な相互依存なし
3. **テスト容易性** - モック不要
4. **移植性** - どの言語でも実装可能
5. **拡張性** - nyash.toml編集で機能追加
6. **初期化保証** - init関数で早期エラー検出可能
## 🚨 注意事項
- プラグインのログは標準エラー出力かファイル出力で
- メモリ管理はプラグイン内で完結
- 非同期処理はNyash側でFutureBoxラップ
---
**革命完了**: これ以上シンプルにできない究極の設計!

View File

@ -0,0 +1,55 @@
# nyash.toml v2.1 拡張仕様(最小)
目的: プラグインBoxのメソッド引数として、他のBoxを不透明参照BoxRefで安全に受け渡す。
## 変更点v2 → v2.1
- メソッド引数型に `kind = "box"` を追加(当面は `category = "plugin"` のみ)
- TLVに BoxRefHandleを追加tag = 8
- payload: `type_id: u32 (LE)`, `instance_id: u32 (LE)`合計8バイト
- 既存タグは不変1=Int64, 2=String(UTF-8), 3=Bool
## 例: libraries セクション
```toml
[libraries]
[libraries."libnyash_filebox_plugin.so"]
boxes = ["FileBox"]
path = "./target/release/libnyash_filebox_plugin.so"
[libraries."libnyash_filebox_plugin.so".FileBox]
type_id = 6
abi_version = 1
[libraries."libnyash_filebox_plugin.so".FileBox.methods]
# 既存
birth = { method_id = 0 }
open = { method_id = 1 }
close = { method_id = 4 }
# 追加例: Box引数を1つ受け取る
copyFrom = { method_id = 7, args = [ { kind = "box", category = "plugin" } ] }
```
備考:
- `args` を省略した場合は引数なしゼロ引数とみなすv2互換
- 複数引数は配列で列挙(例: 2引数なら2要素
- ユーザー定義Boxや複雑なビルトインBoxは当面対象外将来のvtable/retain-release設計で拡張
## 呼び出し時のTLVエンコード
- 先頭ヘッダ `[ver:1, argc:1, rsv:2]` の後、各引数を `tag + payload` で列挙
- `tag=8 (Handle/BoxRef)`: payload = `type_id(4) + instance_id(4)` LE
- 未対応Box種別に対してはエラーを返す開発時のみ toString フォールバックを許容可能)
## 戻り値v2.1→v2.2
- v2.1: Int/String/Bool1/6/3とVoid(9)
- v2.2: BoxRef(Handle, tag=8) の「返り値」対応を追加(同一/別Box型どちらも可
- payload: `type_id:u32` + `instance_id:u32`
- Loaderは `type_id` から `lib_name/box_name` を逆引きし、`PluginBoxV2` を生成して返す
## 互換性
- `args` 宣言がない既存v2設定はそのまま利用可
- BoxRefを使わないメソッドは従来通り Int/String/Bool のみで動作
## 実装メモ(参考)
- Loader: invoke時の引数エンコードに `tag=4` を追加(`category=plugin` のみ)
- プラグイン側: 受領した `type_id` と期待型を照合し、不一致ならエラー
- 所有権: 呼び出し中の一時借用(保持は将来の retain/release で対応)

View File

@ -0,0 +1,437 @@
# Nyash Box プラグインシステム設計
> ⚠️ **DEPRECATED - 将来構想**
>
> この文書はYAML DSLを使った将来的なプラグインシステム構想です。
> **現在の実装とは異なります。**
>
> **実際に動作している仕様については、以下を参照してください:**
> - [BID-FFI v1 実装仕様書](./bid-ffi-v1-actual-specification.md) - 現在動作中の仕様
> - [nyash.toml設定例](../../../../nyash.toml) - 実際の設定形式
> - [plugin_loader_v2.rs](../../../../src/runtime/plugin_loader_v2.rs) - 実装詳細
## 概要
Nyashの「Everything is Box」哲学を維持しながら、Boxの実装をプラグイン化できるシステム。ビルトインBoxとプラグインBoxを透過的に切り替え可能。
## 🎯 設計原則
1. **シンプル** - 設定ファイル1つで切り替え
2. **透過的** - Nyashコードの変更不要
3. **統一的** - ビルトインもプラグインも同じBox
## 📋 プラグイン定義YAML署名DSL
```yaml
# filebox.plugin.yaml
schema: 1
plugin:
name: filebox
version: 1
apis:
# 静的メソッド(::
- sig: "FileBox::open(path: string, mode?: string) -> FileBox"
doc: "Open a file with optional mode"
- sig: "FileBox::exists(path: string) -> bool"
doc: "Check if file exists"
# インスタンスメソッド(#
- sig: "FileBox#read(size?: int) -> string"
doc: "Read file content"
- sig: "FileBox#write(content: string) -> int"
doc: "Write to file"
- sig: "FileBox#close() -> void"
doc: "Close file handle"
```
### 署名DSL仕様
- **静的メソッド**: `Type::method()` - C++風の`::`記法
- **インスタンスメソッド**: `Type#method()` - Ruby風の`#`記法
- **オプショナル引数**: `arg?: type` - `?`サフィックス
- **戻り値**: `-> type` - 矢印記法
### 🔄 Boxライフサイクル管理
```yaml
lifecycle:
# コンストラクタ(生命を与える)
- sig: "FileBox#birth(path: string, mode?: string)"
doc: "Box creation - called after memory allocation"
# デストラクタ(生命を終える)
- sig: "FileBox#fini()"
doc: "Box destruction - called before memory deallocation"
```
**重要な原則**
- `birth()` - Boxインスタンス作成時に呼ばれるメモリ割り当て後
- `fini()` - Boxインスタンス破棄時に呼ばれるメモリ解放前
- プラグインが割り当てたメモリはプラグインが解放する責任を持つ
## 🔧 設定ファイルnyash.toml
### 基本形式v1 - 単一Box型プラグイン
```toml
# プロジェクトルートのnyash.toml
[plugins]
FileBox = "nyash-filebox-plugin" # FileBoxはプラグイン版を使用
# StringBox = "mystring" # コメントアウト = ビルトイン使用
# FileBoxの型情報定義
[plugins.FileBox.methods]
read = { args = [] }
write = { args = [{ from = "string", to = "bytes" }] }
open = { args = [
{ name = "path", from = "string", to = "string" },
{ name = "mode", from = "string", to = "string" }
] }
close = { args = [] }
exists = { args = [], returns = "bool" }
```
### 拡張形式v2 - マルチBox型プラグイン
```toml
# 1つのプラグインで複数のBox型を提供
[plugins.libraries]
"nyash-network" = {
plugin_path = "libnyash_network.so",
provides = ["SocketBox", "HTTPServerBox", "HTTPRequestBox", "HTTPResponseBox", "HttpClientBox"]
}
"nyash-stdlib" = {
plugin_path = "libnyash_stdlib.so",
provides = ["MathBox", "TimeBox", "RandomBox"]
}
# 各Box型の詳細定義
[plugins.types.SocketBox]
library = "nyash-network"
type_id = 100
methods = {
bind = { args = [
{ name = "address", from = "string", to = "string" },
{ name = "port", from = "integer", to = "u16" }
]},
connect = { args = [
{ name = "address", from = "string", to = "string" },
{ name = "port", from = "integer", to = "u16" }
]},
read = { args = [], returns = "string" },
write = { args = [{ from = "string", to = "bytes" }] },
close = { args = [] }
}
[plugins.types.HTTPServerBox]
library = "nyash-network"
type_id = 101
methods = {
bind = { args = [
{ name = "address", from = "string", to = "string" },
{ name = "port", from = "integer", to = "u16" }
]},
route = { args = [
{ name = "path", from = "string", to = "string" },
{ name = "method", from = "string", to = "string" }
]},
start = { args = [] }
}
[plugins.types.HttpClientBox]
library = "nyash-network"
type_id = 102
methods = {
get = { args = [{ name = "url", from = "string", to = "string" }], returns = "string" },
post = { args = [
{ name = "url", from = "string", to = "string" },
{ name = "body", from = "string", to = "string" }
], returns = "string" }
}
```
### 型マッピング仕様
#### 基本型
| Nyash型 | FFI型 | TLVタグ | 説明 |
|---------|-------|---------|------|
| `string` | `string` | 0x01 | UTF-8文字列 |
| `integer` | `i64` | 0x02 | 64ビット整数 |
| `float` | `f64` | 0x03 | 64ビット浮動小数点 |
| `bool` | `bool` | 0x04 | 真偽値 |
| `bytes` | `Vec<u8>` | 0x05 | バイト配列 |
### プラグイン検索パス
```toml
[plugin_paths]
search_paths = [
"./plugins/*/target/release", # 開発時リリースビルド
"./plugins/*/target/debug", # 開発時デバッグビルド
"/usr/local/lib/nyash/plugins", # システムインストール
"~/.nyash/plugins" # ユーザーローカル
]
```
## 🏗️ アーキテクチャ
### 1. Boxレジストリv2対応版
```rust
// 起動時の動作
let mut registry = HashMap::new();
let mut loaded_plugins = HashMap::new();
// 1. ビルトインBoxを登録
registry.insert("FileBox", BoxProvider::Builtin(native_filebox));
registry.insert("StringBox", BoxProvider::Builtin(native_stringbox));
// 2. nyash.toml読み込み
let config = parse_nyash_toml_v2()?;
// 3a. v1形式単一Box型プラグイン
for (box_name, plugin_name) in config.plugins {
registry.insert(box_name, BoxProvider::Plugin(plugin_name));
}
// 3b. v2形式マルチBox型プラグイン
if let Some(libraries) = config.libraries {
for (lib_name, lib_def) in libraries.libraries {
// プラグインを一度だけロード
let plugin = load_plugin(&lib_def.plugin_path)?;
loaded_plugins.insert(lib_name.clone(), plugin);
// 提供する全Box型を登録
for box_type in &lib_def.provides {
registry.insert(box_type, BoxProvider::MultiPlugin(lib_name.clone()));
}
}
}
```
### マルチBox型プラグインFFI
```c
// v2プラグインの追加エクスポート関数
// 提供するBox型の数を返す
extern "C" u32 nyash_plugin_get_box_count();
// 各Box型の情報を取得
extern "C" NyashPluginInfo* nyash_plugin_get_box_info(u32 index);
// Box型名からtype_idを解決
extern "C" u32 nyash_plugin_get_type_id(const char* box_name);
```
### 2. 透過的なディスパッチ
```nyash
# Nyashコード変更不要
local file = new FileBox("test.txt")
file.write("Hello, plugin!")
local content = file.read()
```
内部動作:
1. `new FileBox` → レジストリ検索
2. `BoxProvider::Plugin("filebox")` → プラグインロード
3. BID-FFI経由で実行
### 3. PluginBoxプロキシ
```rust
// すべてのプラグインBoxの統一インターフェース
pub struct PluginBox {
plugin_name: String,
handle: BidHandle, // プラグイン内のインスタンス
}
impl NyashBox for PluginBox {
// NyashBoxトレイトの全メソッドを
// FFI経由でプラグインに転送
}
```
## 📦 プラグイン実装例
```c
// plugins/filebox/src/filebox.c
#include "nyash_plugin_api.h"
// インスタンス管理
typedef struct {
FILE* fp;
char* buffer; // プラグインが管理するバッファ
} FileBoxInstance;
// birth - Boxに生命を与える
i32 filebox_birth(u32 instance_id, const u8* args, size_t args_len) {
// 引数からpath, modeを取得
const char* path = extract_string_arg(args, 0);
const char* mode = extract_string_arg(args, 1);
// インスタンス作成
FileBoxInstance* instance = malloc(sizeof(FileBoxInstance));
instance->fp = fopen(path, mode);
instance->buffer = NULL;
// インスタンスを登録
register_instance(instance_id, instance);
return NYB_SUCCESS;
}
// fini - Boxの生命を終える
i32 filebox_fini(u32 instance_id) {
FileBoxInstance* instance = get_instance(instance_id);
if (!instance) return NYB_E_INVALID_HANDLE;
// プラグインが割り当てたメモリを解放
if (instance->buffer) {
free(instance->buffer);
}
// ファイルハンドルをクローズ
if (instance->fp) {
fclose(instance->fp);
}
// インスタンス自体を解放
free(instance);
unregister_instance(instance_id);
return NYB_SUCCESS;
}
// read - バッファはプラグインが管理
i32 filebox_read(u32 instance_id, i32 size, u8** result, size_t* result_len) {
FileBoxInstance* instance = get_instance(instance_id);
// 既存バッファを解放して新規割り当て
if (instance->buffer) free(instance->buffer);
instance->buffer = malloc(size + 1);
// ファイル読み込み
size_t read = fread(instance->buffer, 1, size, instance->fp);
instance->buffer[read] = '\0';
// プラグインが所有するメモリを返す
*result = instance->buffer;
*result_len = read;
return NYB_SUCCESS;
}
```
## 🔐 メモリ管理の原則
### 所有権ルール
1. **プラグインが割り当てたメモリ**
- プラグインが`malloc()`したメモリはプラグインが`free()`する
- `fini()`メソッドで確実に解放する
- Nyash側は読み取りのみ書き込み禁止
2. **Nyashが割り当てたメモリ**
- Nyashが提供したバッファはNyashが管理
- プラグインは読み書き可能だが解放禁止
- 引数として渡されたメモリはread-only
3. **ライフサイクル保証**
- `birth()` → 各メソッド呼び出し → `fini()` の順序を保証
- `fini()`は必ず呼ばれるGC時またはプログラム終了時
- 循環参照による`fini()`遅延に注意
### Nyash側の実装
```rust
impl Drop for PluginBox {
fn drop(&mut self) {
// Boxが破棄される時、必ずfiniを呼ぶ
let result = self.plugin.invoke(
self.handle.type_id,
FINI_METHOD_ID, // 最大値のmethod_id
self.handle.instance_id,
&[], // no arguments
&mut []
);
if result.is_err() {
eprintln!("Warning: fini failed for instance {}", self.handle.instance_id);
}
}
}
```
## 🚀 段階的導入計画
### Phase 1: 基本実装(完了)
- [x] BID-FFI基盤
- [x] FileBoxプラグイン実装
- [x] nyash.toml v1パーサー
- [x] PluginBoxプロキシ
- [x] プラグインロード機能
### Phase 2: マルチBox型対応進行中
- [ ] nyash.toml v2パーサー実装
- [ ] マルチBox型プラグインFFI拡張
- [ ] plugin-testerの複数Box型対応
- [ ] ネットワーク系プラグイン統合
- HttpClientBox新規実装
- SocketBox既存移行
- HTTPServerBox既存移行
- HTTPRequestBox既存移行
- HTTPResponseBox既存移行
### Phase 3: 開発体験向上
- [ ] YAMLからFFIコード自動生成
- [ ] エラーメッセージ改善
- [ ] プラグインテンプレート
- [ ] ホットリロード対応
### Phase 4: エコシステム
- [ ] プラグインレジストリ
- [ ] バージョン管理
- [ ] 依存関係解決
- [ ] プラグイン間通信
## 🎉 利点
### v1形式の利点
1. **ビルド時間短縮** - 使わないBoxはコンパイル不要
2. **動的拡張** - 再コンパイルなしで新Box追加
3. **Everything is Box維持** - 哲学は変わらない
4. **段階的移行** - 1つずつBoxをプラグイン化
### v2形式の追加利点
5. **依存関係の解決** - 関連Box群を1つのプラグインに
6. **効率的な配布** - 複数Box型を1ライブラリで提供
7. **メモリ効率** - 共有ライブラリは1度だけロード
8. **内部連携** - 同一プラグイン内で直接通信可能
### 実例HTTPServerBoxの依存問題解決
```toml
# v1では困難だった構成
# HTTPServerBoxはSocketBoxに依存するが...
[plugins]
SocketBox = "socket-plugin" # 別プラグイン
HTTPServerBox = "http-plugin" # SocketBoxが使えない
# v2なら簡単に解決
[plugins.libraries]
"nyash-network" = {
plugin_path = "libnyash_network.so",
provides = ["SocketBox", "HTTPServerBox", "HTTPRequestBox", "HTTPResponseBox"]
}
# HTTPServerBoxは同じプラグイン内でSocketBoxを直接使用可能
```
## 📚 関連ドキュメント
- [BID-FFI仕様](./ffi-abi-specification.md)
- [Everything is Box哲学](./everything-is-box.md)
- [実装タスク](../../../予定/native-plan/issues/phase_9_75g_0_chatgpt_enhanced_final.md)

View File

@ -0,0 +1,66 @@
Nyash Plugin Tester - 開発者向けツールガイド
概要
- 目的: Nyash用プラグインBID-FFI準拠の基本健全性を素早く診断するツール。
- 実装場所: `tools/plugin-tester`
- 想定対象: C ABIで `nyash_plugin_*` をエクスポートする動的ライブラリ(.so/.dll/.dylib
ビルド
- コマンド: `cd tools/plugin-tester && cargo build --release`
- 実行ファイル: `tools/plugin-tester/target/release/plugin-tester`
サブコマンド
- `check <plugin>`: プラグインのロード、ABI確認、init呼び出し、型名・メソッド一覧の表示
- `lifecycle <plugin>`: birth→fini の往復テストインスタンスIDを返すことを確認
- `io <plugin>`: FileBox向けE2Eopen→write→close→open→readテスト
使用例
- チェック:
- `tools/plugin-tester/target/release/plugin-tester check plugins/nyash-filebox-plugin/target/release/libnyash_filebox_plugin.so`
- 期待出力例:
- `ABI version: 1`
- `Plugin initialized`
- `Box Type: FileBox (ID: 6)` と 6メソッドbirth/open/read/write/close/finiの列挙
- ライフサイクル:
- `tools/plugin-tester/target/release/plugin-tester lifecycle <path-to-plugin>`
- 期待出力例: `birth → instance_id=1`, `fini → instance 1 cleaned`
- ファイルI/O:
- `tools/plugin-tester/target/release/plugin-tester io <path-to-plugin>`
- 期待出力例: `open(w)`, `write 25 bytes`, `open(r)`, `read 25 bytes → 'Hello from plugin-tester!'`
BID-FFI 前提v1
- 必須シンボル: `nyash_plugin_abi`, `nyash_plugin_init`, `nyash_plugin_invoke`, `nyash_plugin_shutdown`
- 返却コード: 0=成功, -1=ShortBuffer2段階応答, -2=InvalidType, -3=InvalidMethod, -4=InvalidArgs, -5=PluginError, -8=InvalidHandle
- 2段階応答: `result`がNULLまたは小さい場合は `*result_len` に必要サイズを設定し -1 を返す(副作用なし)
TLVType-Length-Value概要簡易
- ヘッダ: `u16 version (=1)`, `u16 argc`
- エントリ: `u8 tag`, `u8 reserved(0)`, `u16 size`, `payload...`
- 主なタグ: 1=Bool, 2=I32, 3=I64, 4=F32, 5=F64, 6=String, 7=Bytes, 8=Handle(u64), 9=Void
- plugin-testerの `io` は最小限のTLVエンコード/デコードを内蔵
プラグイン例FileBox
- 実装場所: `plugins/nyash-filebox-plugin`
- メソッドID: 0=birth, 1=open, 2=read, 3=write, 4=close, 0xFFFF_FFFF=fini
- `open(path, mode)`: 引数は TLV(String, String)、返り値は TLV(Void)
- `read(size)`: 引数 TLV(I32)、返 TLV(Bytes)
- `write(bytes)`: 引数 TLV(Bytes)、返 TLV(I32: 書き込みバイト数)
- `close()`: 返 TLV(Void)
パスの指定(例)
- Linux: `plugins/nyash-filebox-plugin/target/release/libnyash_filebox_plugin.so`
- Windows: `plugins\nyash-filebox-plugin\target\release\nyash_filebox_plugin.dll`
- macOS: `plugins/nyash-filebox-plugin/target/release/libnyash_filebox_plugin.dylib`
トラブルシュート
- `nyash_plugin_abi not found`: ビルド設定cdylibやシンボル名を再確認
- `ShortBuffer`が返るのにデータが取れない: 2回目の呼び出しで `result``*result_len` を適切に設定しているか確認
- 読み出しサイズが0: 書き込み後に `close``open(r)` してから `read` を実行しているか確認
関連ドキュメント
- `docs/CURRENT_TASK.md`(現在の進捗)
- `docs/予定/native-plan/issues/phase_9_75g_bid_integration_architecture.md`(設計計画)
備考
- 本説明書は `C:\git\nyash-project\nyash\docs\説明書\reference\plugin-tester.md` に配置されますWindowsパス例

View File

@ -0,0 +1,503 @@
# 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構成ファイル全体から探索