# 機能設計書 20-例外処理

## 概要

本ドキュメントは、mrubyにおける例外処理機能の設計を記述する。例外クラスの定義・例外の発生（raise）・捕捉（rescue）・バックトレース生成を提供するコアランタイム機能である。

### 本機能の処理概要

例外処理機能は、mrubyの実行時エラーハンドリングの基盤を提供する。ISO 15.2.22（Exception）に準拠し、Exceptionクラスを頂点とした例外クラス階層の定義、例外オブジェクトの生成、raise/rescueメカニズム、バックトレースの生成・管理を行う。内部的にはsetjmp/longjmpベースの非ローカルジャンプ機構を使用し、mrb_state構造体のexcフィールドで現在の例外状態を管理する。RException構造体はメッセージ（mesg）とバックトレース（backtrace）をRBasicポインタで保持し、GC管理対象となる。

**業務上の目的・背景**：例外処理はプログラムの堅牢性を確保するための基本機能であり、エラーの検出・通知・回復を構造的に行う仕組みを提供する。C言語レベルでの例外発生（mrb_raise）とRubyレベルでの例外発生（raise）の両方をサポートし、C拡張とRubyコードの間でシームレスなエラーハンドリングを実現する。

**機能の利用シーン**：引数検証エラー（ArgumentError）、型エラー（TypeError）、ゼロ除算（ZeroDivisionError）、インデックス範囲外（IndexError）、メモリ不足（NoMemoryError）、スタックオーバーフロー（SystemStackError）等のランタイムエラーの処理。begin/rescue/ensure構文によるエラー回復。

**主要な処理内容**：
1. 例外クラス階層の定義（Exception, StandardError, ArgumentError等）
2. 例外オブジェクトの生成（mrb_exc_new, mrb_exc_new_str）
3. 例外の発生（mrb_raise, mrb_exc_raise, mrb_raisef）
4. 例外メッセージの管理（mrb_exc_mesg_set, mrb_exc_mesg_get）
5. バックトレースの生成・保存（pack_backtrace, mrb_keep_backtrace）
6. 例外の文字列表現（to_s, inspect）
7. エラー状態のクリア（mrb_clear_error, mrb_check_error）
8. 保護実行（mrb_protect_error, MRB_ENSURE マクロ）
9. 書式付き例外メッセージ（mrb_raisef, mrb_vformat）

**関連システム・外部連携**：VMのコールスタック管理、setjmp/longjmpメカニズム、GC（例外オブジェクトの管理）、デバッグ情報（バックトレース用のirep/行番号）。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | CLIベースのランタイム機能のため関連画面なし |

## 機能種別

エラーハンドリング / 制御フロー

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| c | struct RClass* | Yes（mrb_raise） | 例外クラス | MRB_TT_EXCEPTION型 |
| msg | const char* | Yes（mrb_raise） | エラーメッセージ | NULL不可 |
| str | mrb_value | Yes（mrb_exc_new_str） | メッセージ文字列 | String型であること |
| exc | mrb_value | Yes（mrb_exc_raise） | 例外オブジェクト | Exception型であること |

### 入力データソース

C APIからの直接呼び出し、Rubyレベルのraise構文、VMのエラー検出。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 例外オブジェクト | Exception | 生成された例外 |
| メッセージ | String | to_s, inspectの文字列結果 |
| バックトレース | Array | backtrace メソッドの結果（文字列の配列） |

### 出力先

mrb_state->excへの設定、呼び出し元への戻り値。

## 処理フロー

### 処理シーケンス

```
1. 例外オブジェクトの生成
   └─ mrb_exc_new_str: RException構造体を割り当て、メッセージを設定
2. 例外の発生
   └─ mrb_raise: メッセージからException生成し、mrb_exc_raiseを呼び出し
   └─ mrb_exc_raise: 例外をmrb->excに設定
       └─ mrb_exc_set: バックトレースの保存
       └─ exc_throw: MRB_THROW (longjmp) による非ローカルジャンプ
3. 例外の捕捉（VM側）
   └─ rescue ハンドラへの制御移行
   └─ mrb_clear_error: エラー状態のクリア
4. バックトレース生成
   └─ pack_backtrace: コールスタックから位置情報を収集
   └─ irep->debug_info から行番号を取得
```

### フローチャート

```mermaid
flowchart TD
    A[エラー検出] --> B[mrb_raise / raise]
    B --> C[mrb_exc_new_str: 例外オブジェクト生成]
    C --> D[mrb_exc_set: mrb->exc に設定]
    D --> E[mrb_keep_backtrace: バックトレース保存]
    E --> F[exc_throw: MRB_THROW longjmp]
    F --> G{rescue ハンドラあり?}
    G -->|Yes| H[rescue ブロック実行]
    G -->|No| I{上位の jmp あり?}
    I -->|Yes| J[上位へ伝播]
    I -->|No| K[mrb_print_error + abort]
    H --> L[処理続行]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-20-01 | 例外階層 | Exception→StandardError→各種エラーの継承階層 | 常時 |
| BR-20-02 | 非ローカルジャンプ | 例外発生はsetjmp/longjmpで実装 | mrb_raise/mrb_exc_raise呼び出し時 |
| BR-20-03 | バックトレース保存 | out_of_memory時やfrozen時はバックトレースをスキップ | 例外設定時 |
| BR-20-04 | jmp未設定時 | mrb->jmpがNULLの場合はエラー出力後にabort | 例外throw時 |
| BR-20-05 | break例外 | breakオブジェクトは特別扱い（mrb_exc_setを通さない） | mrb_exc_raise時 |
| BR-20-06 | 事前確保エラー | stack_err, nomem_err, arena_errは初期化時に事前生成 | mrb_init_exception時 |

### 計算ロジック

- **バックトレース生成**: コールスタックをciidxから逆順に辿り、各フレームのirep debug_infoから行番号を取得
- **書式フォーマット**: mrb_vformatで %v, %s, %d, %i, %C等の書式指定子をサポート

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

該当なし（インメモリ操作のみ）。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Exception | 全例外の基底クラス | rescue Exceptionで捕捉 |
| - | StandardError | 一般的なランタイムエラーの基底 | rescue（デフォルト）で捕捉 |
| - | ArgumentError | 引数の数や値が不正 | 引数を修正する |
| - | TypeError | 型が不正 | 型を確認する |
| - | RangeError | 値が範囲外 | 値を確認する |
| - | ZeroDivisionError | ゼロ除算 | 除数を確認する |
| - | IndexError | インデックス範囲外 | インデックスを確認する |
| - | KeyError | ハッシュキーが不存在 | キーを確認する |
| - | RuntimeError | 一般的なランタイムエラー | 処理を修正する |
| - | FrozenError | 凍結オブジェクトの変更 | 非frozen オブジェクトを使用する |
| - | ScriptError | スクリプト関連エラーの基底 | コードを修正する |
| - | SyntaxError | 構文エラー | 構文を修正する |
| - | NotImplementedError | 未実装の機能 | 実装する |
| - | NoMemoryError | メモリ不足 | メモリ使用量を削減する |
| - | SystemStackError | スタックオーバーフロー | 再帰を減らす |
| - | LocalJumpError | 不正な制御フロージャンプ | return/break/nextの使用場所を確認する |
| - | NoMatchingPatternError | パターンマッチ失敗 | パターンを修正する |

### リトライ仕様

mrb_protect_error / MRB_ENSURE マクロにより保護実行が可能。エラー発生後もensureブロック内で後処理を実行できる。

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

該当なし。ただし、begin/rescue/ensure構文によりトランザクション的なエラー回復パターンが実現可能。

## パフォーマンス要件

- 例外の発生はsetjmp/longjmpを使用するため、通常のメソッド呼び出しより高コスト
- バックトレースの生成はコールスタックの深さに比例
- 事前確保エラーオブジェクト（stack_err, nomem_err）により、メモリ不足時でも確実にエラーを発生可能

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

- バックトレースにファイル名と行番号が含まれるため、本番環境では情報漏洩のリスクがある
- mrb->jmpが未設定でabortが呼ばれる場合のプロセス停止リスク

## 備考

- ISO 15.2.22（Exception）、15.2.23（StandardError）等に準拠
- FloatDomainError は RangeError のサブクラス
- FrozenError は RuntimeError のサブクラス
- KeyError は IndexError のサブクラス

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | error.h | `include/mruby/error.h` | RException構造体、RBreak構造体、保護実行API |

**読解のコツ**: RExceptionは`mesg`（メッセージ、RStringへのポインタ）と`backtrace`（バックトレース、RArrayまたはRDataへのポインタ）をRBasic*として保持する。MRB_EXC_EXITフラグはexit用の特殊フラグ。RBreakはreturn/breakの非ローカルジャンプ用の構造体で、ci_break_indexでジャンプ先を管理する。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | error.c | `src/error.c` | `mrb_init_exception()`関数（906行目）で例外クラス階層定義 |

**主要処理フロー**:
1. **906-937行目**: 例外クラス階層の定義
   - **908行目**: Exception クラス定義（MRB_TT_EXCEPTION）
   - **913行目**: StandardError（Exceptionのサブクラス）
   - **914行目**: ArgumentError
   - **915行目**: LocalJumpError
   - **916行目**: RangeError → FloatDomainError
   - **918行目**: RegexpError
   - **919行目**: RuntimeError → FrozenError
   - **921行目**: TypeError
   - **922行目**: ZeroDivisionError
   - **923行目**: ScriptError → NotImplementedError, SyntaxError
   - **926行目**: IndexError → KeyError
   - **928行目**: NoMatchingPatternError
   - **929-930行目**: SystemStackError（事前確保）
   - **932-933行目**: NoMemoryError（事前確保）

#### Step 3: 例外の発生メカニズムを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | error.c | `src/error.c` | mrb_raise, mrb_exc_raise, mrb_exc_set, exc_throw |

**主要処理フロー**:
- **21-29行目**: mrb_exc_mesg_set - メッセージの設定（GCバリア付き）
- **38-47行目**: mrb_exc_new_str - 例外オブジェクト生成
- **63-72行目**: exc_initialize - Exception#initializeのC実装
- **182-198行目**: mrb_exc_set - mrb->excへの設定とバックトレース保存
- **200-208行目**: exc_throw - longjmpによる非ローカルジャンプ（jmpなしならabort）
- **223-236行目**: mrb_exc_raise - 例外オブジェクトの発生（break特殊処理あり）
- **248-252行目**: mrb_raise - 文字列メッセージからの簡便な例外発生

#### Step 4: バックトレース生成を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | backtrace.c | `src/backtrace.c` | pack_backtrace, copy_backtrace |

**主要処理フロー**:
- **20-22行目**: 定数定義（MAX_IREP_REFCNT, UNKNOWN_LINENO, UNKNOWN_LOCATION）
- **24-39行目**: copy_backtrace - バックトレースエントリのコピー（irep参照カウント管理）
- **51-97行目**: pack_backtrace - コールスタックから位置情報を収集
  - **56行目**: ciidxから逆順にコールスタックを走査
  - **64-71行目**: Rubyメソッドの場合: irepとpcから位置を算出
  - **74-91行目**: C関数の場合: 呼び出し元のRubyフレームから位置を推定

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

```
mrb_init_exception()
    |
    +-- Exception クラス定義
    |       +-- StandardError
    |       |       +-- ArgumentError, TypeError, RangeError等
    |       +-- ScriptError
    |       |       +-- SyntaxError, NotImplementedError
    |       +-- SystemStackError (事前確保)
    |       +-- NoMemoryError (事前確保)

例外発生フロー:
    mrb_raise(mrb, class, msg)
        |
        +-- mrb_exc_new_str (RException生成)
        +-- mrb_exc_raise
                |
                +-- mrb_exc_set
                |       +-- mrb_keep_backtrace
                |               +-- pack_backtrace
                +-- exc_throw
                        +-- MRB_THROW (longjmp)
```

### データフロー図

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

エラー検出           mrb_raise                          RException オブジェクト
(C/Ruby)      ---->   mrb_exc_new_str             ---->  mrb->exc に設定
                       |
                     mrb_exc_set
                       +-- pack_backtrace          ---->  バックトレース
                       |   (コールスタック走査)           (配列/文字列)
                       |
                     exc_throw
                       +-- MRB_THROW (longjmp)     ---->  rescue ハンドラへ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| error.c | `src/error.c` | ソース（C） | 例外クラス定義と例外発生メカニズム（938行） |
| backtrace.c | `src/backtrace.c` | ソース（C） | バックトレースの生成と管理 |
| error.h | `include/mruby/error.h` | ヘッダ | RException/RBreak構造体と例外API |
| throw.h | `include/mruby/throw.h` | ヘッダ | setjmp/longjmpラッパーマクロ |
| debug.c | `src/debug.c` | ソース（C） | デバッグ情報（行番号取得）の管理 |
| debug.h | `include/mruby/debug.h` | ヘッダ | デバッグ情報構造体 |
