# 機能設計書 1-仮想マシン（VM）

## 概要

本ドキュメントは、mrubyの仮想マシン（VM）の機能設計を記述する。mruby VMはスタックベースのバイトコードインタプリタであり、コンパイルされたmrubyバイトコード（irep）を実行する中核コンポーネントである。

### 本機能の処理概要

mruby VMは、コンパイラが生成したバイトコード命令列（iseq）をフェッチ・デコード・実行するスタックベースの仮想マシンである。オペコードのディスパッチ、値スタックの管理、コールスタック（callinfo）の管理、メソッド呼び出し、例外処理など、mrubyプログラムの実行に必要なすべての処理を担う。

**業務上の目的・背景**：mrubyは組み込みシステム向けの軽量Ruby実装であり、VMはそのプログラム実行エンジンの中核である。スタックベースの設計はレジスタベースに比べて命令列がコンパクトになり、メモリ制約のある組み込み環境に適している。mruby VMはレジスタ方式のスタックマシンで、各関数フレームごとにレジスタウィンドウを持ち、効率的なローカル変数アクセスを実現している。

**機能の利用シーン**：Rubyスクリプトの実行時に常に使用される。`mrb_load_irep`でバイナリをロードした後の実行、`mrb_funcall`によるC言語からのRubyメソッド呼び出し、対話型シェル（mirb）でのコード評価など、あらゆるRubyコード実行の基盤となる。

**主要な処理内容**：
1. バイトコード命令のフェッチ・デコード・実行ループ（`mrb_vm_exec`）
2. 値スタックの初期化・拡張・クリア（`stack_init`, `stack_extend`）
3. コールスタック（callinfo）のプッシュ・ポップ（`cipush`, `cipop`）
4. メソッド探索とディスパッチ（`mrb_vm_find_method`, `prepare_missing`）
5. C関数呼び出しとmrubyメソッド呼び出しの統合処理
6. 例外処理（try/catch/raise）
7. 環境（REnv）とクロージャの管理

**関連システム・外部連携**：GC（ガベージコレクション）との連携（GCアリーナの保存・復元）、コンパイラが生成するirep構造体の消費、Fiberによるコンテキスト切り替え。

**権限による制御**：メソッドの可視性（public/protected/private）に基づくアクセス制御を行う。`ci->vis`フィールドで現在の可視性コンテキストを管理し、`OP_SEND`系命令でメソッド呼び出し時に可視性チェックを実施する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面機能マッピングにデータなし |

## 機能種別

計算処理（バイトコード実行エンジン）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| mrb | mrb_state* | Yes | mrubyインタプリタ状態 | NULLでないこと |
| proc | const struct RProc* | Yes | 実行するプロシージャ | CFUNC_Pでないこと（mrb_vm_exec） |
| self | mrb_value | Yes | レシーバオブジェクト | - |
| iseq | const mrb_code* | Yes | 実行開始命令アドレス | irep->iseq範囲内 |
| stack_keep | mrb_int | No | スタック保持数（mrb_vm_run） | 0以上 |

### 入力データソース

コンパイラが生成したirep（命令表現）構造体。irep内のiseq（命令列）、pool（定数プール）、syms（シンボルテーブル）、reps（サブirep）を参照する。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 実行結果 | mrb_value | 最後に評価された式の値 |
| 例外 | mrb->exc | 未捕捉例外が発生した場合にセット |

### 出力先

呼び出し元に戻り値として返却。例外発生時は`mrb->exc`にセットされ、`MRB_TRY/MRB_CATCH`によるlongjmpで呼び出し元のエラーハンドラに制御が移る。

## 処理フロー

### 処理シーケンス

```
1. mrb_vm_run() - VM実行のエントリーポイント
   └─ スタック初期化・拡張、self設定、mrb_vm_exec()呼び出し
2. mrb_vm_exec() - メインバイトコード実行ループ
   ├─ 命令フェッチ（pc++）
   ├─ 命令デコード（FETCH_B/S/Wマクロ）
   ├─ 命令ディスパッチ（gotoテーブルまたはswitch文）
   │   ├─ OP_MOVE, OP_LOADL等 - スタック操作
   │   ├─ OP_SEND, OP_SSEND等 - メソッド呼び出し
   │   ├─ OP_RETURN等 - メソッドからの復帰
   │   ├─ OP_JMP, OP_JMPIF等 - 制御フロー
   │   ├─ OP_ENTER - 引数セットアップ
   │   └─ OP_STOP - 実行終了
   └─ 例外処理（MRB_TRY/MRB_CATCH）
3. cipush() - メソッド呼び出し時のcallinfo確保
   └─ コールスタック拡張、引数・ブロック設定
4. cipop() - メソッド復帰時のcallinfo解放
   └─ 環境（REnv）のアンシェア処理
```

### フローチャート

```mermaid
flowchart TD
    A[mrb_vm_run] --> B[スタック初期化・拡張]
    B --> C[mrb_vm_exec]
    C --> D{命令フェッチ}
    D --> E[命令デコード]
    E --> F{オペコード判定}
    F -->|OP_SEND| G[メソッド探索]
    G --> H{C関数?}
    H -->|Yes| I[C関数呼び出し]
    H -->|No| J[cipush + 再帰的VM実行]
    F -->|OP_RETURN| K[cipop + 呼び出し元に戻る]
    F -->|OP_STOP| L[実行終了]
    F -->|OP_JMP系| M[PC更新]
    F -->|その他| N[スタック操作]
    I --> D
    J --> D
    K --> D
    M --> D
    N --> D
    L --> O[結果返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 最大再帰深度制限 | コールスタックがMRB_CALL_LEVEL_MAX（デフォルト512）を超えるとSystemStackError | メソッド呼び出し時 |
| BR-02 | 最大スタックサイズ制限 | スタックサイズがMRB_STACK_MAX（約256K）を超えるとSystemStackError | スタック拡張時 |
| BR-03 | メソッド可視性チェック | private/protectedメソッドの不正呼び出しをNoMethodErrorで拒否 | OP_SEND系命令実行時 |
| BR-04 | method_missing | メソッドが見つからない場合にmethod_missingを呼び出す | メソッド探索失敗時 |

### 計算ロジック

スタック拡張時の成長戦略：デフォルトでは1.5倍成長（`MRB_STACK_EXTEND_DOUBLING`定義時は2倍成長）。最小成長量はMRB_STACK_GROWTH（128）。

## データベース操作仕様

### 操作別データベース影響一覧

該当なし（VMはインメモリ処理のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| SystemStackError | スタックオーバーフロー | コールスタックがMRB_CALL_LEVEL_MAX超過 | 再帰呼び出しの見直し |
| SystemStackError | スタックオーバーフロー | 値スタックがMRB_STACK_MAX超過 | メモリ割り当て設定の見直し |
| NoMethodError | メソッド未定義 | 対象オブジェクトにメソッドが存在しない | method_missingまたはメソッド定義 |
| NoMethodError | アクセス制御違反 | private/protectedメソッドの不正呼び出し | 呼び出し方法の修正 |
| TypeError | クラス不正 | target_classがNULL | メソッド定義コンテキストの確認 |

### リトライ仕様

リトライ機構なし。例外発生時はMRB_TRY/MRB_CATCHによるlongjmpで呼び出し元に制御が移る。

## トランザクション仕様

該当なし（VMレベルでのトランザクション管理はない）

## パフォーマンス要件

- gotoテーブルベースのディスパッチ（`MRB_USE_VM_SWITCH_DISPATCH`未定義時）により高速な命令実行
- GCアリーナの保存・復元による効率的なメモリ管理
- メソッドキャッシュ（`MRB_METHOD_CACHE_SIZE`、デフォルト256エントリ）によるメソッド探索の高速化
- スタック成長は1.5倍方式でメモリ効率と性能のバランスを確保

## セキュリティ考慮事項

- メソッド可視性（public/protected/private）による実行時アクセス制御
- スタックサイズ制限によるスタックオーバーフロー攻撃の防止
- `mrb_protect_error`によるC言語からの安全なmrubyコード実行

## 備考

- vm.cは約3696行の大規模ファイルであり、mrubyの中核をなす
- オペコードの定義は`include/mruby/ops.h`に分離されている
- コンパイル時オプション`MRB_USE_VM_SWITCH_DISPATCH`でswitch文ベースのディスパッチに切り替え可能
- AddressSanitizer有効時はMRB_CALL_LEVEL_MAXが128に減少

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、VMが操作する基本データ構造を把握することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | mruby.h | `include/mruby.h` | mrb_callinfo構造体（165-181行目）: n, nk, cci, mid, proc, blk, stack, pc, uの各フィールド |
| 1-2 | mruby.h | `include/mruby.h` | mrb_context構造体（196-207行目）: stbase/stend（値スタック）、ci/cibase/ciend（コールスタック） |
| 1-3 | mruby.h | `include/mruby.h` | mrb_state構造体（263-340行目）: c（現在のコンテキスト）、root_c、exc、jmp等 |
| 1-4 | irep.h | `include/mruby/irep.h` | mrb_irep構造体（55-80行目）: nlocals, nregs, iseq, pool, syms, reps |

**読解のコツ**: mrb_callinfo内のuはunion型で、envまたはtarget_classのどちらかを保持する。`CI_ENV`マクロと`CI_TARGET_CLASS`マクロで適切に判別される。

#### Step 2: オペコード体系を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ops.h | `include/mruby/ops.h` | 全オペコード定義（1-134行目）: 各命令のオペランド型とセマンティクス |
| 2-2 | opcode.h | `include/mruby/opcode.h` | オペランドのフェッチマクロ（26-71行目）: FETCH_B, FETCH_BB, FETCH_BS等 |

**読解のコツ**: オペランド型はZ（なし）, B（8bit）, BB（8+8bit）, BS（8+16bit）, W（24bit）等。EXT1/EXT2/EXT3命令で16bit拡張が可能。

#### Step 3: VM実行エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | vm.c | `src/vm.c` | mrb_vm_run()（1587-1615行目）: スタック初期化、レジスタ数計算、mrb_vm_exec呼び出し |
| 3-2 | vm.c | `src/vm.c` | mrb_vm_exec()（1672-1699行目）: gotoテーブル初期化、ジャンプバッファ設定、メインループ開始 |

**主要処理フロー**:
1. **1596-1598行目**: スタック未初期化時のstack_init呼び出し
2. **1608行目**: stack_extendでレジスタ分のスタック確保
3. **1610行目**: self値をスタックの先頭に設定
4. **1611行目**: mrb_vm_execでバイトコード実行開始

#### Step 4: スタック管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | vm.c | `src/vm.c` | stack_init()（113-132行目）: スタック初期化（STACK_INIT_SIZE=128スロット） |
| 4-2 | vm.c | `src/vm.c` | stack_extend_alloc()（157-196行目）: スタック拡張とMRB_STACK_MAXチェック |
| 4-3 | vm.c | `src/vm.c` | envadjust()（134-153行目）: スタック再割り当て時のポインタ調整 |

**主要処理フロー**:
- **35-36行目**: STACK_INIT_SIZE=128, CALLINFO_INIT_SIZE=32の定数定義
- **50-56行目**: MRB_CALL_LEVEL_MAXの定義（デフォルト512、ASAN時128）
- **176行目**: 1.5倍成長戦略

#### Step 5: メソッド呼び出し機構を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | vm.c | `src/vm.c` | cipush()（356-387行目）: callinfo確保、MRB_CALL_LEVEL_MAXチェック |
| 5-2 | vm.c | `src/vm.c` | cipop()（479-497行目）: callinfo解放、環境アンシェア |
| 5-3 | vm.c | `src/vm.c` | mrb_funcall_with_block()（778-847行目）: C言語からのメソッド呼び出しAPI |
| 5-4 | vm.c | `src/vm.c` | prepare_missing()（680-719行目）: method_missing処理 |

**主要処理フロー**:
- **369-371行目**: コールスタック深度がMRB_CALL_LEVEL_MAXを超えた場合のSystemStackError
- **376-384行目**: callinfo各フィールドの設定
- **486-494行目**: cipop時の環境アンシェア失敗処理

### プログラム呼び出し階層図

```
mrb_funcall() / mrb_funcall_argv()
    │
    └─ mrb_funcall_with_block()
           │
           ├─ mrb_vm_find_method() ─── メソッド探索
           │       └─ prepare_missing() ─── method_missing
           │
           ├─ [C関数の場合] MRB_METHOD_CFUNC(m)(mrb, self)
           │
           └─ [Ruby関数の場合] mrb_run() / mrb_vm_run()
                  │
                  └─ mrb_vm_exec() ─── メインバイトコード実行ループ
                         │
                         ├─ cipush() ─── メソッド呼び出し
                         ├─ cipop() ─── メソッド復帰
                         ├─ stack_extend() ─── スタック拡張
                         └─ exec_irep() ─── irep実行
```

### データフロー図

```
[入力]                     [処理]                         [出力]

irep (iseq/pool/syms) ──▶ mrb_vm_exec()                ──▶ mrb_value (実行結果)
                           │
mrb_value self ──────────▶ 値スタック (stbase..stend)
                           │
mrb_callinfo ────────────▶ コールスタック (cibase..ciend)
                           │
RProc / REnv ────────────▶ クロージャ環境管理
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| vm.c | `src/vm.c` | ソース | VM本体（メインループ、スタック管理、メソッド呼び出し） |
| mruby.h | `include/mruby.h` | ヘッダ | mrb_state, mrb_callinfo, mrb_context等の構造体定義 |
| ops.h | `include/mruby/ops.h` | ヘッダ | オペコード定義（OPCODE(x,_)マクロ） |
| opcode.h | `include/mruby/opcode.h` | ヘッダ | オペコード列挙型、フェッチマクロ |
| irep.h | `include/mruby/irep.h` | ヘッダ | mrb_irep構造体定義 |
| proc.h | `include/mruby/proc.h` | ヘッダ | RProc構造体、MRB_PROC_CFUNC_Pマクロ等 |
| gc.h | `include/mruby/gc.h` | ヘッダ | GC関連構造体（mrb_gc）定義 |
| throw.h | `include/mruby/throw.h` | ヘッダ | MRB_TRY/MRB_CATCH例外処理マクロ |
| value_array.h | `src/value_array.h` | ヘッダ | 値配列操作ユーティリティ |
