# 機能設計書 83-エラーハンドリング拡張

## 概要

本ドキュメントは、mrubyのmruby-error gemが提供する拡張的なエラーハンドリングC APIの機能設計を記述する。mrb_protect、mrb_ensure、mrb_rescue、mrb_rescue_exceptionsの4つのC API関数を提供し、C言語からRubyのbegin/rescue/ensure相当の例外処理を行えるようにする。

### 本機能の処理概要

**業務上の目的・背景**：mrubyをC言語に組み込む際、Ruby関数の呼び出し中に発生する例外をC側で適切にハンドリングする必要がある。本gemは、Rubyのbegin/rescue/ensureブロックに相当する機能をC APIとして提供し、組み込みアプリケーションの堅牢性を高める。

**機能の利用シーン**：C拡張からRubyコードを安全に呼び出す場合、例外発生時にリソースの解放を保証する場合、特定の例外クラスのみをキャッチして処理する場合に使用される。

**主要な処理内容**：
1. `mrb_protect` - 関数実行を例外保護下で行い、例外発生の有無をboolで返す
2. `mrb_ensure` - 関数実行後、例外の有無に関わらずクリーンアップ関数を実行する
3. `mrb_rescue` - StandardErrorとそのサブクラスを捕捉するrescue処理
4. `mrb_rescue_exceptions` - 指定した例外クラスのみを捕捉するrescue処理

**関連システム・外部連携**：mrubyのVM例外処理システム（mrb_protect_error）、GCアリーナ管理

**権限による制御**：特になし。C APIとして任意のC拡張から利用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | C API専用。画面なし |

## 機能種別

エラーハンドリング処理（C API）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| body | mrb_func_t | Yes | 保護下で実行する関数ポインタ | NULL不可 |
| data / b_data | mrb_value | Yes | body関数に渡すデータ | - |
| state (protect) | mrb_bool* | Yes | 例外発生フラグの格納先 | NULL不可 |
| ensure / rescue | mrb_func_t | Yes | 後処理/例外処理関数ポインタ | NULL不可 |
| e_data / r_data | mrb_value | Yes | ensure/rescue関数に渡すデータ | - |
| len (rescue_exceptions) | mrb_int | Yes | 例外クラス配列の長さ | 1以上 |
| classes (rescue_exceptions) | struct RClass** | Yes | 捕捉する例外クラスの配列 | NULL不可 |

### 入力データソース

C言語関数呼び出しの引数

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 (protect) | mrb_value | body関数の戻り値、または例外オブジェクト |
| 戻り値 (ensure) | mrb_value | body関数の戻り値（例外時は再送出） |
| 戻り値 (rescue) | mrb_value | body関数の戻り値、または rescue関数の戻り値 |

### 出力先

C関数の戻り値

## 処理フロー

### 処理シーケンス

```
1. mrb_protect(mrb, body, data, &state)
   ├─ protect_data構造体にbodyとdataをセット
   ├─ mrb_protect_error(mrb, protect_body, &protect_data, &state)を呼び出し
   └─ 結果をそのまま返却

2. mrb_ensure(mrb, body, b_data, ensure, e_data)
   ├─ GCアリーナ保存
   ├─ mrb_protect_errorでbody実行
   ├─ ensure関数を必ず実行
   ├─ GCアリーナ復元
   ├─ 例外発生時はmrb_exc_raiseで再送出
   └─ 正常時はbodyの戻り値を返却

3. mrb_rescue(mrb, body, b_data, rescue, r_data)
   └─ mrb_rescue_exceptionsを StandardErrorクラスで呼び出し

4. mrb_rescue_exceptions(mrb, body, b_data, rescue, r_data, len, classes)
   ├─ GCアリーナ保存
   ├─ mrb_protect_errorでbody実行
   ├─ 例外発生時:
   │   ├─ 指定クラスとmrb_obj_is_kind_ofでマッチング
   │   ├─ マッチしない場合: mrb_exc_raiseで再送出
   │   └─ マッチした場合: mrb->exc=NULL, rescue関数実行
   └─ 正常時はbodyの戻り値を返却
```

### フローチャート

```mermaid
flowchart TD
    A[C API呼び出し] --> B{API種別}
    B -->|protect| C[mrb_protect_error実行]
    C --> D[結果とstateフラグ返却]
    B -->|ensure| E[mrb_protect_error実行]
    E --> F[ensure関数実行]
    F --> G{例外あり?}
    G -->|Yes| H[例外再送出]
    G -->|No| I[結果返却]
    B -->|rescue| J[rescue_exceptions呼び出し]
    J --> K[StandardErrorで呼び出し]
    B -->|rescue_exceptions| L[mrb_protect_error実行]
    L --> M{例外あり?}
    M -->|No| N[結果返却]
    M -->|Yes| O{指定クラスにマッチ?}
    O -->|No| P[例外再送出]
    O -->|Yes| Q[rescue関数実行]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-83-1 | ensure保証 | ensure関数は例外の有無に関わらず必ず実行される | mrb_ensure使用時 |
| BR-83-2 | 例外再送出 | ensureで例外が発生していた場合、ensure実行後に再送出する | mrb_ensure使用時 |
| BR-83-3 | クラスマッチング | mrb_obj_is_kind_ofで例外クラスの継承関係を考慮してマッチング | mrb_rescue_exceptions使用時 |
| BR-83-4 | rescue対象 | mrb_rescueはStandardErrorとそのサブクラスのみ捕捉 | mrb_rescue使用時 |

### 計算ロジック

特になし

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 任意の例外 | body関数内で例外発生 | protect: stateフラグで通知、ensure: 再送出、rescue: 捕捉または再送出 |

### リトライ仕様

該当なし

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

GCアリーナのsave/restoreにより、GC保護範囲のトランザクション的管理を行う。

## パフォーマンス要件

特になし

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

特になし

## 備考

- 本gemはRuby側のメソッドを追加しない（gem_init/gem_finalは空）
- C API専用のユーティリティgemである

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | exception.c | `mrbgems/mruby-error/src/exception.c` | `struct protect_data`（5-8行目）でbody関数とデータをパッケージングする構造を理解 |

**読解のコツ**: `mrb_func_t` は `mrb_value (*)(mrb_state*, mrb_value)` の関数ポインタ型。`protect_body` ヘルパー関数（13-18行目）がこの構造体を展開してユーザ関数を呼び出す。

#### Step 2: mrb_protectを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | exception.c | `mrbgems/mruby-error/src/exception.c` | `mrb_protect`（33-38行目）で基本的な例外保護の仕組みを確認 |

**主要処理フロー**:
- **36行目**: `protect_data` 構造体にbodyとdataをセット
- **37行目**: `mrb_protect_error` で保護実行（内部実装はsrc/error.c）

#### Step 3: mrb_ensureを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | exception.c | `mrbgems/mruby-error/src/exception.c` | `mrb_ensure`（55-69行目）でensure処理のパターンを確認 |

**主要処理フロー**:
- **58行目**: `mrb_gc_arena_save` でGCアリーナ保存
- **61行目**: `mrb_protect_error` でbody実行
- **62行目**: ensure関数を必ず実行
- **63行目**: GCアリーナ復元
- **64行目**: `mrb_gc_protect` で結果をGC保護
- **65-66行目**: 例外があれば再送出

#### Step 4: mrb_rescue / mrb_rescue_exceptionsを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | exception.c | `mrbgems/mruby-error/src/exception.c` | `mrb_rescue`（85-90行目）と`mrb_rescue_exceptions`（108-133行目）を確認 |

**主要処理フロー（mrb_rescue_exceptions）**:
- **115行目**: `mrb_protect_error` でbody実行
- **118-123行目**: 例外クラスのマッチングループ（`mrb_obj_is_kind_of`使用）
- **125行目**: マッチしない場合は `mrb_exc_raise` で再送出
- **127-128行目**: マッチした場合、`mrb->exc = NULL` でクリアしてrescue関数実行

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

```
mrb_protect ─────────────▶ mrb_protect_error (src/error.c)
                               └─ protect_body
                                    └─ body(mrb, data) [ユーザ関数]

mrb_ensure ──────────────▶ mrb_protect_error
                               └─ protect_body → body
                           ensure(mrb, e_data) [クリーンアップ]
                           mrb_exc_raise [例外時]

mrb_rescue ──────────────▶ mrb_rescue_exceptions
                               └─ mrb_protect_error
                                    └─ protect_body → body
                               rescue(mrb, r_data) [エラーハンドラ]

mrb_rescue_exceptions ───▶ mrb_protect_error
                               └─ protect_body → body
                           mrb_obj_is_kind_of [クラスマッチング]
                           rescue(mrb, r_data) or mrb_exc_raise
```

### データフロー図

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

body関数 + data ───▶ mrb_protect_error ───▶ 結果 or 例外オブジェクト
                         │
                         ├─ protect: state=true/false
                         ├─ ensure: ensure実行 → 再送出 or 結果
                         └─ rescue: マッチ判定 → rescue実行 or 再送出
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| exception.c | `mrbgems/mruby-error/src/exception.c` | ソース | mrb_protect/ensure/rescue実装 |
| mrbgem.rake | `mrbgems/mruby-error/mrbgem.rake` | 設定 | gem仕様定義 |
| exception.c | `mrbgems/mruby-error/test/exception.c` | テスト | Cテストヘルパー（ExceptionTestクラス定義） |
| exception.rb | `mrbgems/mruby-error/test/exception.rb` | テスト | Rubyテストケース |
