-
Notifications
You must be signed in to change notification settings - Fork 0
JA 03_pipeline_architecture
🇺🇸 English | 🇯🇵 日本語 | Introduction
Roslyn Incremental Source Generator (ISG) は、コンパイラからのイベントを入力として受け取り、LINQ のようなパイプラインを介してソースコードを出力へと変換する。本アーキテクチャは Kassyi.Generators.Extensions のパイプラインヘルパーを利用し、スリムかつゼロアロケーションの変換を強制する。
以下のシーケンス図は、システム全体のパイプライン構成を示す。Roslyn が提供する IncrementalValuesProvider<T> API の連鎖を図示している。クラス間の具体的な相互作用の詳細については、本章の「Ⅲ. クラス関係と詳細データフロー」を参照のこと。
sequenceDiagram
autonumber
participant Compiler as Roslyn Compiler
participant SP as SyntaxProvider (ISG)
participant Prepare as PrepareData (抽出)
participant Model as DTO (ClassData/DPData)
participant Source as Sources.* (生成)
Compiler->>SP: 構文変更の通知
SP->>SP: ForAttributeWithMetadataName...<br/>(属性付きクラスをフィルタリング)
SP->>SP: Combine(Framework, Version)
SP->>Prepare: Select(PrepareData)
Note over Prepare: ISymbolやSyntaxNodeから<br/>プリミティブなデータのみを抽出<br/>(NamedArguments辞書キャッシュ等)
Prepare-->>Model: (ClassData, DependencyPropertyData) を構築
SP->>SP: WhereNotNull()
Note over SP: 前回のコンパイル時と等価(Equals)なら<br/>ここで処理を打ち切りキャッシュを使う
SP->>Source: Select(Generate)
Source-->>SP: 生成したC#35;ソースコード文字列
SP->>Compiler: AddSource()
パイプラインの実行は、以下のフェーズに厳密に従って進行する。
-
構文のフィルタリング: パイプラインは Roslyn 4.3.0 以降の API (
ForAttributeWithMetadataName) を活用し、特定の属性が付与されたクラスおよびレコード宣言のみを厳密にフィルタリングする。 -
データの抽出:
PrepareData.csおよびDependencyPropertyDataBuilderコンポーネントが、生のAttributeDataやINamedTypeSymbolインスタンスを構造化された DTO へと投影する。このフェーズでは、辞書ルックアップによるNamedArgumentsのキャッシュ化や構文検索の重複排除を行い、抽出速度を最大化する。 -
等価性の評価とキャッシュ: Roslyn ISG ドライバーは
Selectフェーズの出力を評価する。出力が前回のコンパイルステップと厳密に一致する場合(Equalsがtrueを返す場合)、後続のソース生成処理をバイパスし、インクリメンタルキャッシュを利用する。 -
ソースコードの生成: ジェネレーターはキャッシュミスが発生した場合にのみこのフェーズを呼び出す。DTO を
.g.csのソース文字列へと変換する際、SourceWriterのスコープ管理を利用してゼロアロケーションのフォーマットを強制する。
インクリメンタルジェネレーターにおける最重要パフォーマンス指標は、インクリメンタルキャッシュのヒット率である。このヒット率を最適化するため、データモデル(DependencyPropertyData, ClassData, EventData およびサブレコード)には厳格な値の等価性セマンティクス(readonly record struct の採用や EquatableArray<T> によるコレクションラップ)が強制される。
Note
等価性キャッシュ戦略、ゼロアロケーション生成、および Roslyn 構文ツリーの早期切り離しに関する詳細なアーキテクチャ制約については、05. コード生成とパフォーマンス最適化 (Ⅳ. パフォーマンス最適化ルール) を参照のこと。
このセクションでは、ジェネレーターの内部クラスの具体的な責務を規定し、Roslyn パイプラインを通るデータフローの制約を定義する。
ジェネレーターの内部アーキテクチャは、以下の4つの主要なレイヤーに分割される。
- Generators (ジェネレーター層): 実行フローを調整するため Roslyn パイプラインに登録される。
- Data Extraction (データ抽出層): Syntax および Semantic モデルから必要不可欠なメタデータのみを抽出する責務を負う。
- Models (モデル・DTO層): 抽出されたデータを永続化する、等価性を持つ値型レコード。
- Sources (ソース生成層): DTO を受け取り、合成された C# ソースコード文字列を出力する。
classDiagram
%% Generators
class AttributeGeneratorBase~TData~ {
<<abstract>>
+Initialize(IncrementalGeneratorInitializationContext)
#PrepareData(GeneratorAttributeContext) TData?
#GenerateSource(TData) string
#GetHintName(TData) string
#SupportedFrameworks IReadOnlyList~Framework~
}
class DependencyPropertyGenerator {
#PrepareData() Tuple~ClassData, DPData~
#GenerateSource() string
}
class RoutedEventGenerator {
#PrepareData() Tuple~ClassData, EventData~
}
AttributeGeneratorBase <|-- DependencyPropertyGenerator
AttributeGeneratorBase <|-- RoutedEventGenerator
classDiagram
class MultiAttributeGeneratorBase~TData~ {
<<abstract>>
+Initialize(IncrementalGeneratorInitializationContext)
#PrepareData(GeneratorMultiAttributeContext) TData?
#GenerateSource(TData) string
#GetHintName(TData) string
#SupportedFrameworks IReadOnlyList~Framework~
#SelectMany bool
}
class AttachedDependencyPropertyGenerator {
#PrepareData() Tuple~ClassData, DPData~
}
class WeakEventGenerator {
#PrepareData() Tuple~ClassData, EventData~
}
MultiAttributeGeneratorBase <|-- AttachedDependencyPropertyGenerator
MultiAttributeGeneratorBase <|-- WeakEventGenerator
classDiagram
%% Data Extraction
class PrepareData {
<<static>>
+GetDependencyPropertyData(GeneratorAttributeContext) DependencyPropertyData
+GetClassData(INamedTypeSymbol, ...) ClassData
}
class DependencyPropertyDataBuilder {
+WithCoreProperties()
+WithMetadata()
+WithDefaultValues()
+WithCallbacks()
+Build() DependencyPropertyData
}
class DependencyPropertyMetadataExtractor {
<<static>>
+GetFrameworkMetadata() FrameworkMetadataData
}
%% Models (DTOs)
class ClassData {
<<readonly record struct>>
}
class DependencyPropertyData {
<<readonly record struct>>
}
%% Source Generation
class SourceGenerationHelper {
<<static>>
+GenerateDependencyPropertySource(ClassData, DPData) string
}
%% Relationships
DependencyPropertyGenerator --> PrepareData : パイプラインから呼び出し
PrepareData --> DependencyPropertyDataBuilder : データ構築を委譲
DependencyPropertyDataBuilder --> DependencyPropertyMetadataExtractor : メタデータ解析
DependencyPropertyDataBuilder ..> DependencyPropertyData : 生成
PrepareData ..> ClassData : 生成
DependencyPropertyGenerator --> SourceGenerationHelper : DTOを渡す
SourceGenerationHelper ..> ClassData : 読み取り
SourceGenerationHelper ..> DependencyPropertyData : 読み取り
-
AttributeGeneratorBase<TData>/MultiAttributeGeneratorBase<TData>: インクリメンタルジェネレーターのコア基盤。構文フィルタリング、ターゲットフレームワークの事前検証(SupportedFrameworks)、コンテキストのカプセル化(GeneratorAttributeContext)、およびソース出力を含む標準ロジックをカプセル化する。 -
PrepareData: 抽出プロセスのエントリーポイント。INamedTypeSymbolなどの複雑な Roslyn オブジェクトから純粋なデータを分離するための拡張メソッドを公開する。 -
DependencyPropertyDataBuilder: 依存関係プロパティ特有の抽出(コールバックシグネチャの照合や XML ドキュメントの抽出など)を段階的に実行する内部ビルダー。 -
ClassData/DependencyPropertyData: 抽出されたメタデータを永続化するデータモデル。キャッシュパフォーマンスを最大化するため、構造的にreadonly record structとして実装される。 -
SourceGenerationHelper: データモデルを消費し、SourceWriterを利用して最終的な C# ソースコードを組み立てる静的ヘルパー。
以下のシーケンス図は、内部の具体的な実行フローをトレースする。[DependencyProperty] 属性を検知してから最終的な C# コードが生成されるまでの順序を示し、インスタンス化されるクラスと呼び出されるメソッドを明示的に詳述する。
sequenceDiagram
autonumber
participant Roslyn as ISG Pipeline
participant DPG as Generator
participant PD as PrepareData
participant Builder as DPDataBuilder
participant Models as DTOs
%% 構文解析フェーズ
Roslyn->>DPG: 構文変更通知<br/>(属性付きクラス検知)
%% データ抽出フェーズ
DPG->>PD: GetClassData(classSymbol)
Note over PD: 修飾子、名前空間等取得
PD-->>Models: ClassData 生成
DPG->>PD: GetDependencyPropertyData(attribute)
PD->>Builder: new Builder()
Note over Builder: 属性引数や構文ツリーから<br/>段階的にメタデータを抽出
Builder->>Builder: WithCoreProperties()
Builder->>Builder: WithMetadata()
Builder->>Builder: WithDefaultValues()
Builder->>Builder: WithCallbacks()
Builder-->>Models: DPData 生成
DPG-->>Roslyn: タプル (ClassData, DPData) 返却
sequenceDiagram
autonumber
participant Roslyn as ISG Pipeline
participant DPG as Generator
participant Helper as SourceGenerationHelper
%% キャッシュ判定フェーズ
Note over Roslyn: 【重要】モデルの Equals() で等価性判定。<br/>前回コンパイル時から変化がなければ<br/>ここで処理を打ち切り、キャッシュを使う。
%% コード生成フェーズ
Roslyn->>DPG: キャッシュミス時、生成処理要求
DPG->>Helper: GenerateDependencyPropertySource(Class, DP)
Note over Helper: SourceWriterを使用して<br/>C#35;コードを文字列結合(ゼロアロケ)
Helper-->>DPG: 生成されたソースコード (string)
DPG-->>Roslyn: AddSource() でコンパイラへ登録
Note
パフォーマンス最適化原則の集約 Roslyn 型(Symbol/Syntax)の早期切り離しや、ゼロアロケーション生成フェーズに関する具体的な禁止事項・ベストプラクティスについては、05. コード生成とパフォーマンス最適化 (Ⅳ. パフォーマンス最適化ルール) を参照のこと。
拡張性と関心事の分離
フレームワーク固有のマッピングロジック(DependencyPropertyDataBuilder 内)をソース生成ロジック(SourceGenerationHelper)から隔離することで、パースロジックの変更がゼロアロケーションの生成レイヤーを汚染しないアーキテクチャが保証される。
This wiki is automatically synchronized from spec/ in the repository.
- Introduction
- 01. FAQ & Design Rationale
- 02. Foundation & Domain
- 03. Pipeline Architecture
- 04. Framework Strategies
- 05. Synthesis & Performance
- 06. Complexity Model
- 07. Test Specification
- 08. Diagnostics Reference
- 概要
- 01. 設計思想とFAQ
- 02. 基盤とドメイン
- 03. パイプライン構造
- 04. フレームワーク別生成仕様
- 05. コード生成と最適化
- 06. 計算量モデル
- 07. テスト仕様書
- 08. 診断機能リファレンス