# 機能設計書 145-IL出力

## 概要

本ドキュメントでは、RoslynのIL出力（Emit）機能について設計を記載する。この機能は、コンパイル結果として生成されるアセンブリファイル（DLL/EXE）およびPDBファイルへのアクセスを抽象化し、Edit and ContinueやデバッグなどのIDE機能から利用できるようにする。

### 本機能の処理概要

**業務上の目的・背景**：
Visual Studioのデバッグ機能やEdit and Continue機能は、コンパイル結果のアセンブリとPDBにアクセスする必要がある。本機能は、これらのファイルへのアクセスを`CompilationOutputs`抽象クラスで統一的に提供し、ファイルシステム上の実ファイルだけでなく、メモリ上のストリームなど様々なソースからのアクセスを可能にする。

**機能の利用シーン**：
- Edit and Continue機能でのアセンブリ差分適用
- デバッグ時のMVID（Module Version ID）取得
- PDBからのデバッグ情報読み取り
- 埋め込みPDB（Embedded Portable PDB）の抽出

**主要な処理内容**：
1. アセンブリストリームのオープン
2. メタデータセクションの読み取り
3. PDBストリームのオープン（外部PDBまたは埋め込みPDB）
4. MVIDの読み取り
5. コンテンツのストリームコピー

**関連システム・外部連携**：
- MSBuildビルドシステムとの連携
- Visual Studioデバッガーとの統合
- Edit and Continue機能との連携

**権限による制御**：
特定の権限による制御は行わない。ファイルシステムへのアクセス権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | デバッガー | 参照画面 | デバッグ情報の取得元 |
| - | 出力ウィンドウ | 結果表示画面 | ビルド結果の表示 |

## 機能種別

ファイルアクセス / コンパイル出力 / デバッグ支援

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| assemblyFilePath | string | No | アセンブリファイルパス | 絶対パス |
| pdbFilePath | string | No | PDBファイルパス | 絶対パス |
| prefetch | bool | No | メタデータプリフェッチフラグ | - |
| projectId | ProjectId | Yes | プロジェクトID | null不可 |

### 入力データソース

- ファイルシステム上のアセンブリファイル
- ファイルシステム上のPDBファイル
- MSBuildビルド出力

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| MetadataReaderProvider | MetadataReaderProvider | メタデータ読み取りプロバイダー |
| DebugInformationReaderProvider | DebugInformationReaderProvider | PDB読み取りプロバイダー |
| Mvid | Guid | モジュールバージョンID |
| AssemblyDisplayPath | string | アセンブリ表示パス |
| PdbDisplayPath | string | PDB表示パス |

### 出力先

- デバッガー
- Edit and Continue機能

## 処理フロー

### 処理シーケンス

```
1. パス検証
   └─ アセンブリ/PDBファイルパスの絶対パス検証
2. ストリームオープン
   └─ ファイルストリームを読み取りモードでオープン
3. メタデータ読み取り
   └─ PEReaderでPEヘッダーを解析しメタデータセクションを特定
4. PDB処理
   └─ 外部PDBファイルまたは埋め込みPDBを検出して読み取り
5. リソース返却
   └─ 呼び出し元が破棄責任を持つプロバイダーを返却
```

### フローチャート

```mermaid
flowchart TD
    A[CompilationOutputs取得] --> B{アセンブリパスあり?}
    B -->|Yes| C[OpenAssemblyStream]
    B -->|No| D[null返却]
    C --> E[PEReader生成]
    E --> F[メタデータセクション特定]
    F --> G{PDBパスあり?}
    G -->|Yes| H[OpenPdbStream]
    G -->|No| I{埋め込みPDB?}
    I -->|Yes| J[ReadEmbeddedPortablePdb]
    I -->|No| K[PDBなし]
    H --> L[DebugInformationReaderProvider返却]
    J --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-145-01 | 絶対パス必須 | ファイルパスは絶対パスでなければならない | パス指定時 |
| BR-145-02 | 埋め込みPDBフォールバック | 外部PDBがない場合は埋め込みPDBを検索 | PDB読み取り時 |
| BR-145-03 | リソース解放責任 | 返却されたプロバイダーは呼び出し元が破棄 | プロバイダー取得後 |
| BR-145-04 | プリフェッチモード | prefetch=trueでメタデータを事前読み込みしストリームを閉じる | メモリ効率重視時 |

### 計算ロジック

- MVIDはModuleDefinitionテーブルのMvidハンドルから取得

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

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

データベース操作は行わない。

### テーブル別操作詳細

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ERR-145-01 | BadImageFormatException | 無効なPE/PDB形式 | 例外をスロー |
| ERR-145-02 | InvalidOperationException | ストリームがシーク不可 | 例外をスロー |
| ERR-145-03 | ファイル不在 | 指定パスにファイルなし | null返却 |

### リトライ仕様

リトライは行わない。

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

トランザクション管理は不要（読み取り専用処理）。

## パフォーマンス要件

- ファイルオープンは即時完了すること
- プリフェッチモードでメモリ使用量を最適化

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

- ファイルシステムアクセス権限の確認
- 読み取り専用アクセスのみ

## 備考

- CompilationOutputFilesはファイルシステムベースの具体実装
- CompilationOutputFilesWithImplicitPdbPathはPDBパス自動解決版

---

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

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

### 推奨読解順序

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

コンパイル出力アクセスの抽象化を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | CompilationOutputs.cs | `src/Features/Core/Portable/Emit/CompilationOutputs.cs` | 抽象基底クラス（19-177行目） |
| 1-2 | ICompilationOutputsProviderService.cs | `src/Features/Core/Portable/Emit/ICompilationOutputsProviderService.cs` | サービスインターフェース（9-12行目） |

**読解のコツ**: `CompilationOutputs`はabstractクラスで、`OpenAssemblyStream`と`OpenPdbStream`が派生クラスで実装される。メタデータ読み取りやPDB処理のロジックは基底クラスで提供される。

#### Step 2: 具体実装を理解する

ファイルシステムベースの実装を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | CompilationOutputFiles.cs | `src/Features/Core/Portable/Emit/CompilationOutputFiles.cs` | ファイルベース実装（12-53行目） |
| 2-2 | CompilationOutputFilesWithImplicitPdbPath.cs | `src/Features/Core/Portable/Emit/CompilationOutputFilesWithImplicitPdbPath.cs` | 暗黙PDBパス版 |

**主要処理フロー**:
- **22-36行目**: コンストラクタでパスの絶対パス検証
- **41-42行目**: `OpenAssemblyStream`でFileUtilities.OpenRead呼び出し
- **51-52行目**: `OpenPdbStream`でFileUtilities.OpenRead呼び出し

#### Step 3: メタデータ読み取りを理解する

PEファイルからのメタデータ抽出を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | CompilationOutputs.cs | `src/Features/Core/Portable/Emit/CompilationOutputs.cs` | OpenAssemblyMetadataメソッド（45-64行目） |

**主要処理フロー**:
- **47-52行目**: ストリームを開きnullチェック
- **53-57行目**: PEReaderでPEヘッダーを読み取り
- **59-63行目**: メタデータオフセットとサイズでMetadataReaderProvider生成

#### Step 4: PDB読み取りを理解する

外部PDBと埋め込みPDBの処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | CompilationOutputs.cs | `src/Features/Core/Portable/Emit/CompilationOutputs.cs` | OpenPdbメソッド（97-118行目） |

**主要処理フロー**:
- **99-103行目**: 外部PDBストリームがあればそれを使用
- **106-116行目**: なければ埋め込みPDBを探索

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

```
ICompilationOutputsProviderService
    │
    └─ GetCompilationOutputs(ProjectId)
           │
           ▼
    CompilationOutputs (abstract)
           │
           ├─ OpenAssemblyMetadata(prefetch)
           │      └─ OpenAssemblyStream() (abstract)
           │             └─ PEReader → MetadataReaderProvider
           │
           ├─ OpenPdb()
           │      ├─ OpenPdbStream() (abstract)
           │      │      └─ DebugInformationReaderProvider
           │      │
           │      └─ (fallback) OpenAssemblyStream()
           │             └─ PEReader.ReadEmbeddedPortablePdb()
           │
           ├─ ReadAssemblyModuleVersionId()
           │      └─ OpenAssemblyMetadata()
           │             └─ MetadataReader.GetModuleDefinition().Mvid
           │
           └─ TryCopyAssemblyToAsync() / TryCopyPdbToAsync()

    CompilationOutputFiles (concrete)
           │
           ├─ AssemblyFilePath
           ├─ PdbFilePath
           ├─ OpenAssemblyStream() → FileUtilities.OpenRead
           └─ OpenPdbStream() → FileUtilities.OpenRead
```

### データフロー図

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

アセンブリパス ───────▶ CompilationOutputFiles ───────▶ CompilationOutputs
    +                         │
PDBパス                       ▼
                    OpenAssemblyStream()
                              │
                              ▼
                         PEReader
                              │
                    ┌─────────┴─────────┐
                    ▼                   ▼
           メタデータセクション    埋め込みPDB検出
                    │                   │
                    ▼                   ▼
        MetadataReaderProvider   DebugInformationReaderProvider
                    │                   │
                    ▼                   ▼
              MVID取得          デバッグ情報読み取り
                    │                   │
                    └─────────┬─────────┘
                              ▼
                    Edit and Continue / デバッガー
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| CompilationOutputs.cs | `src/Features/Core/Portable/Emit/` | ソース | 抽象基底クラス |
| CompilationOutputFiles.cs | `src/Features/Core/Portable/Emit/` | ソース | ファイルベース実装 |
| CompilationOutputFilesWithImplicitPdbPath.cs | `src/Features/Core/Portable/Emit/` | ソース | 暗黙PDBパス実装 |
| ICompilationOutputsProviderService.cs | `src/Features/Core/Portable/Emit/` | インターフェース | サービス契約 |
