# 機能設計書 53-ILリーダー

## 概要

本ドキュメントは、F#コンパイラにおける.NETアセンブリ（PE/CLI形式）のバイナリ読み込み機能について記述する。

### 本機能の処理概要

.NETアセンブリファイル（.dll/.exe）をバイナリレベルで解析し、ILモジュール定義（型、メソッド、フィールド、メタデータ等）を抽出するリーダー機能を提供する。

**業務上の目的・背景**：F#コンパイラは参照アセンブリのメタデータを読み込んで型チェックを行う必要がある。本機能はCLI/PE形式のバイナリファイルを直接解析し、必要な型情報を内部表現（ILModuleDef）に変換する。これにより、外部ライブラリの型情報をコンパイル時に利用可能とする。

**機能の利用シーン**：
- コンパイル時の参照アセンブリメタデータ読み込み
- F# Interactiveでの動的アセンブリロード
- Visual Studioでのメタデータ表示
- IntelliSenseの型情報取得

**主要な処理内容**：
1. PEヘッダーの解析（DOS/PE/Optional Header）
2. セクションテーブルの解析
3. CLIヘッダーの解析
4. メタデータストリームの解析（#~, #Strings, #Blob, #GUID, #US）
5. メタデータテーブルの読み込み（TypeDef, MethodDef, FieldDef等）
6. ILコードの逆アセンブル
7. カスタム属性の解析
8. アセンブリ参照の解決

**関連システム・外部連携**：
- .NETランタイムのアセンブリローダー
- PDBファイル（デバッグ情報）
- Roslyn（Visual Studio統合時）

**権限による制御**：ファイルシステムアクセス権限のみ

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | オブジェクトブラウザ | 参照画面 | 型定義の表示 |
| - | F# Interactive | 参照画面 | アセンブリ参照の読み込み |

## 機能種別

データ変換 / バイナリ解析

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| fileName | string | Yes | アセンブリファイルパス | 有効なパス |
| pdbDirPath | string option | No | PDBファイルディレクトリ | - |
| reduceMemoryUsage | ReduceMemoryFlag | No | メモリ使用量削減フラグ | Yes/No |
| metadataOnly | MetadataOnlyFlag | No | メタデータのみ読み込み | Yes/No |
| tryGetMetadataSnapshot | (string -> obj option) | No | Roslyn統合用スナップショット取得 | - |

### 入力データソース

- .NETアセンブリファイル（.dll/.exe）
- メモリ上のアセンブリバイト配列
- Roslynメタデータスナップショット

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ILModuleDef | record | ILモジュール定義 |
| ILAssemblyRef list | list | 参照アセンブリ一覧 |

### 出力先

- コンパイラの内部型システム
- IntelliSenseのメタデータキャッシュ

## 処理フロー

### 処理シーケンス

```
1. ファイルアクセス方式の決定
   └─ メモリマップ / バイト配列 / ストリーム

2. PEヘッダー解析
   └─ DOSヘッダー読み込み（MZマジック検証）
   └─ PEシグネチャ検証
   └─ Optionalヘッダー読み込み

3. セクションテーブル解析
   └─ .text, .rsrc, .reloc等のセクション情報取得
   └─ RVAからファイルオフセットへの変換テーブル構築

4. CLIヘッダー解析
   └─ メタデータルートの位置特定
   └─ StrongNameSignatureの位置取得

5. メタデータストリーム解析
   └─ #~（テーブルストリーム）
   └─ #Strings（文字列ヒープ）
   └─ #Blob（バイナリヒープ）
   └─ #GUID（GUIDヒープ）
   └─ #US（ユーザー文字列）

6. メタデータテーブル読み込み
   └─ TypeDef, TypeRef, TypeSpec
   └─ MethodDef, MemberRef
   └─ FieldDef, PropertyDef, EventDef
   └─ AssemblyRef, ModuleRef

7. ILModuleDef構築
   └─ 型定義の構築
   └─ メソッド本体の遅延読み込み設定
   └─ カスタム属性の解析
```

### フローチャート

```mermaid
flowchart TD
    A[開始: OpenILModuleReader] --> B{キャッシュ存在?}
    B -->|Yes| C[キャッシュから返却]
    B -->|No| D[BinaryFile取得]
    D --> E[PEヘッダー解析]
    E --> F[CLIヘッダー解析]
    F --> G[メタデータストリーム解析]
    G --> H[テーブル読み込み]
    H --> I[ILModuleDef構築]
    I --> J[キャッシュに保存]
    J --> K[ILModuleReader返却]
    C --> K
    K --> L[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-53-01 | キャッシュ | 同一ファイル（パス+タイムスタンプ）は結果をキャッシュ | reduceMemoryUsage = Yes |
| BR-53-02 | メモリマップ | 大きなファイルはメモリマップを使用 | alwaysMemoryMapFSC設定 |
| BR-53-03 | 弱参照 | 安定したファイルは弱参照でバイト配列を保持 | noStableFileHeuristic = false |
| BR-53-04 | メタデータのみ | metadataOnly = Yesの場合、ILコードは読み込まない | metadataOnly = Yes |

### 計算ロジック

**RVA（相対仮想アドレス）変換**:
- セクションテーブルから対応セクションを検索
- RVA - セクションVirtualAddress + セクションPointerToRawData

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

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| FSComp.SR.ilreadFileChanged | エラー | ファイルが変更された | ファイル更新後に再読み込み |
| badFormatMsg | エラー | PE形式が不正 | 有効なアセンブリを使用 |
| badMagic | エラー | マジックナンバー不正 | ファイル破損確認 |

### リトライ仕様

本機能にリトライ機構はない。

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

該当なし

## パフォーマンス要件

- ILModuleReaderCache1: 強参照キャッシュ（デフォルト30エントリ）
- ILModuleReaderCache2: 弱参照キャッシュ（無制限）
- 遅延読み込み: メソッド本体、カスタム属性は必要時に読み込み

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

- アセンブリの署名検証は別機能（ILSigner）が担当
- メモリマップファイルはプロセス間で共有可能だが読み取り専用

## 備考

- 環境変数`FSharp_StronglyHeldBinaryReaderCacheSize`でキャッシュサイズ変更可能
- 環境変数`FSharp_NoStableFileHeuristic`で弱参照キャッシュ無効化可能

---

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

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

### 推奨読解順序

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

まず、ILモジュールの内部表現を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | il.fs | `src/Compiler/AbstractIL/il.fs` | ILModuleDef型（モジュール定義の構造）を理解 |
| 1-2 | il.fs | `src/Compiler/AbstractIL/il.fs` | ILTypeDef型（型定義の構造）を理解 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ilread.fs | `src/Compiler/AbstractIL/ilread.fs` | ILModuleReaderインターフェース（4993-5000行目） |
| 2-2 | ilread.fs | `src/Compiler/AbstractIL/ilread.fs` | OpenILModuleReader関数（5095-5220行目）がメインエントリーポイント |

**主要処理フロー**:
1. **5095-5120行目**: キャッシュキーの計算とキャッシュ検索
2. **5130-5180行目**: BinaryFileの取得（メモリマップ/バイト配列）
3. **5179-5189行目**: ILModuleReaderImplのインスタンス化

#### Step 3: バイナリファイル抽象化を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ilread.fs | `src/Compiler/AbstractIL/ilread.fs` | BinaryFileインターフェース（144-145行目）でファイルアクセス抽象化 |
| 3-2 | ilread.fs | `src/Compiler/AbstractIL/ilread.fs` | RawMemoryFile（150-175行目）でRoslynメモリ統合 |
| 3-3 | ilread.fs | `src/Compiler/AbstractIL/ilread.fs` | WeakByteFile（221-257行目）で弱参照キャッシュ |
| 3-4 | ilread.fs | `src/Compiler/AbstractIL/ilread.fs` | PEFile（194-215行目）でPEReader統合 |

#### Step 4: バイナリ読み取りプリミティブを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ilread.fs | `src/Compiler/AbstractIL/ilread.fs` | seekReadByte/Int32/UInt16（259-263行目）で基本読み取り |
| 4-2 | ilread.fs | `src/Compiler/AbstractIL/ilread.fs` | seekReadCompressedUInt32（287-301行目）で圧縮整数読み取り |
| 4-3 | ilread.fs | `src/Compiler/AbstractIL/ilread.fs` | seekReadUTF8String（315-317行目）で文字列読み取り |

**読解のコツ**: `mdv: BinaryView`は読み取り専用バイトメモリ。`addr`はオフセット。

#### Step 5: メタデータテーブル読み取りを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | ilread.fs | `src/Compiler/AbstractIL/ilread.fs` | TaggedIndex型（79-106行目）でコード化されたトークンを扱う |
| 5-2 | BinaryConstants.fs | `src/Compiler/AbstractIL/BinaryConstants.fs` | TableName列挙でメタデータテーブル名を定義 |

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

```
OpenILModuleReader
    │
    ├─ キャッシュ検索
    │      ├─ ilModuleReaderCache1.TryGet (強参照)
    │      └─ ilModuleReaderCache2.TryGetValue (弱参照)
    │
    ├─ BinaryFile取得
    │      ├─ tryGetMetadataSnapshot (Roslyn統合)
    │      ├─ WeakByteFile (弱参照キャッシュ)
    │      ├─ PEFile (PEReader使用)
    │      └─ ByteFile (バイト配列)
    │
    └─ openPE
           │
           ├─ seekReadPEHeader (PEヘッダー解析)
           │
           ├─ seekReadOptionalHeader (オプションヘッダー)
           │
           ├─ seekReadSectionHeaders (セクションテーブル)
           │
           ├─ seekReadCLIHeader (CLIヘッダー)
           │
           ├─ openMetadataReader (メタデータ解析)
           │      ├─ readTablesBasic
           │      ├─ readStringsStream
           │      ├─ readBlobStream
           │      └─ readGUIDStream
           │
           └─ mkILModuleDef (ILModuleDef構築)
```

### データフロー図

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

アセンブリファイル    ──▶  BinaryFile作成          ──▶  BinaryView
       │
       ▼
BinaryView           ──▶  PEヘッダー解析          ──▶  PEHeader情報
       │
       ▼
セクションテーブル    ──▶  RVA変換テーブル構築     ──▶  AddressMap
       │
       ▼
CLIヘッダー          ──▶  メタデータルート取得     ──▶  MetadataRoot
       │
       ▼
メタデータストリーム  ──▶  テーブル/ヒープ読み込み  ──▶  ctxt (リーダーコンテキスト)
       │
       ▼
ctxt                 ──▶  mkILModuleDef           ──▶  ILModuleDef
       │
       ▼
ILModuleDef          ──▶  ILModuleReaderImpl      ──▶  ILModuleReader
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ilread.fs | `src/Compiler/AbstractIL/ilread.fs` | ソース | ILバイナリリーダーの中核実装 |
| il.fs | `src/Compiler/AbstractIL/il.fs` | ソース | IL抽象構文（ILModuleDef等） |
| il.fsi | `src/Compiler/AbstractIL/il.fsi` | シグネチャ | IL抽象構文の公開API |
| BinaryConstants.fs | `src/Compiler/AbstractIL/BinaryConstants.fs` | ソース | PE/CLIバイナリ定数 |
| Support.fs | `src/Compiler/AbstractIL/Support.fs` | ソース | バイナリ読み取りサポート関数 |
