Files
hakorune/docs/development/roadmap/phases/phase-12/EMBEDDED-VM-BOX-HANDLING.md
Moe Charm c13d9c045e 📚 Phase 12: Nyashスクリプトプラグインシステム設計と埋め込みVM構想
## 主な成果
- Nyashスクリプトでプラグイン作成可能という革命的発見
- C ABI制約の分析と埋め込みVMによる解決策
- MIR/VM/JIT層での箱引数サポートの詳細分析

## ドキュメント作成
- Phase 12基本構想(README.md)
- Gemini/Codex先生の技術分析
- C ABIとの整合性問題と解決策
- 埋め込みVM実装ロードマップ
- 箱引数サポートの技術詳細

## 重要な洞察
- 制約は「リンク時にC ABI必要」のみ
- 埋め込みVMでMIRバイトコード実行により解決可能
- Nyashスクリプト→C ABIプラグイン変換が実現可能

Everything is Box → Everything is Plugin → Everything is Possible!
2025-08-30 22:52:16 +09:00

4.8 KiB
Raw Blame History

埋め込みVMでのBox処理設計

🎯 核心MIRレベルでのBox処理を再現

現在のMIR/VMでのBox処理フロー

1. MIR: BoxCall/PluginInvoke命令
   ↓
2. VM: ValueId → VMValue変換
   ↓
3. VMValue → Box<dyn NyashBox> or TLVエンコード
   ↓
4. メソッド実行
   ↓
5. 結果をVMValueに戻す

📊 埋め込みVMの設計

1. 軽量VMValue定義

// 埋め込みVM用の値表現
typedef enum {
    NYVM_TYPE_INT,
    NYVM_TYPE_FLOAT,
    NYVM_TYPE_BOOL,
    NYVM_TYPE_STRING,
    NYVM_TYPE_HANDLE,  // Box参照ハンドル
    NYVM_TYPE_VOID
} NyVMType;

typedef struct {
    NyVMType type;
    union {
        int64_t i;
        double f;
        uint8_t b;
        struct { const char* data; size_t len; } s;
        uint64_t h;  // ハンドルBox参照
    } value;
} NyVMValue;

2. MIRバイトコード形式

// BoxCall命令のエンコード
enum {
    OP_BOXCALL = 0x20,
    OP_PLUGIN_INVOKE = 0x21,
    // ...
};

// BoxCall: [op:1] [dst:1] [box_val:1] [method_id:2] [argc:1] [args...]
// 例: BoxCall %2 = %1.toString()
// → 0x20 0x02 0x01 0x00 0x00 0x00

3. 埋め込みVMでのBoxCall実行

int nyvm_execute_boxcall(
    NyashEmbeddedVM* vm,
    uint8_t dst,
    uint8_t box_val,
    uint16_t method_id,
    uint8_t argc,
    uint8_t* args
) {
    // 1. レシーバー取得
    NyVMValue* recv = &vm->values[box_val];
    
    // 2. プリミティブ型の場合
    if (recv->type != NYVM_TYPE_HANDLE) {
        // プリミティブ→TLV変換
        uint8_t tlv_buf[256];
        size_t tlv_len = encode_primitive_to_tlv(recv, tlv_buf);
        
        // 組み込み実装を呼び出し
        return call_builtin_method(recv->type, method_id, tlv_buf, tlv_len);
    }
    
    // 3. Boxハンドルの場合
    uint64_t handle = recv->value.h;
    
    // 引数をTLVエンコード
    uint8_t args_tlv[1024];
    size_t args_len = 0;
    for (int i = 0; i < argc; i++) {
        NyVMValue* arg = &vm->values[args[i]];
        args_len += encode_value_to_tlv(arg, &args_tlv[args_len]);
    }
    
    // 4. プラグイン呼び出しC ABI
    uint8_t result[4096];
    size_t result_len = sizeof(result);
    
    int rc = nyash_plugin_invoke(
        get_type_id_from_handle(handle),
        method_id,
        get_instance_id_from_handle(handle),
        args_tlv, args_len,
        result, &result_len
    );
    
    // 5. 結果をVMValueに変換
    if (rc == 0) {
        decode_tlv_to_value(result, result_len, &vm->values[dst]);
    }
    
    return rc;
}

🔄 Box引数の処理

現在のVMRust

// VMValue → Box<dyn NyashBox>変換
let val = self.get_value(*arg)?;
Ok(val.to_nyash_box())

埋め込みVMC

// NyVMValue → TLVエンコード
switch (value->type) {
    case NYVM_TYPE_INT:
        encode_i64(tlv, value->value.i);
        break;
    case NYVM_TYPE_HANDLE:
        encode_handle(tlv, 
            get_type_id_from_handle(value->value.h),
            get_instance_id_from_handle(value->value.h)
        );
        break;
    // ...
}

💡 実装のポイント

1. ハンドル管理

// グローバルハンドルテーブル
typedef struct {
    uint32_t type_id;
    uint32_t instance_id;
    void* native_ptr;  // 実際のBoxポインタ必要な場合
} HandleEntry;

static HandleEntry g_handles[MAX_HANDLES];
static uint64_t g_next_handle = 1;

uint64_t register_handle(uint32_t type_id, uint32_t instance_id) {
    uint64_t h = g_next_handle++;
    g_handles[h].type_id = type_id;
    g_handles[h].instance_id = instance_id;
    return h;
}

2. 組み込みメソッド

// 頻出メソッドは埋め込みVMに直接実装
int call_builtin_method(NyVMType type, uint16_t method_id, ...) {
    switch (type) {
        case NYVM_TYPE_INT:
            if (method_id == 0) { // toString
                // 整数→文字列変換
            }
            break;
        // ...
    }
}

3. プラグインとの統合

// 生成されるCコード
extern "C" int32_t nyplug_mybox_invoke(...) {
    // MIRバイトコード実行
    NyashEmbeddedVM vm;
    nyvm_init(&vm, BYTECODE, sizeof(BYTECODE));
    
    // 引数をVMスタックに設定
    nyvm_decode_args(&vm, args, args_len);
    
    // メソッド実行
    nyvm_execute_method(&vm, method_id);
    
    // 結果をTLVエンコード
    return nyvm_encode_result(&vm, result, result_len);
}

🎯 結論

埋め込みVMは

  1. MIRのBoxCall/PluginInvoke命令を忠実に実装
  2. TLVエンコード/デコードでC ABIと通信
  3. ハンドルでBox参照を管理
  4. 頻出処理は最適化実装

これにより、Nyashスクリプトで書いたプラグインも、ネイティブプラグインと同じC ABIで動作します