Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@
*.jpeg binary
*.png binary
*.zip binary

# Preserve raw disassembler output while keeping whitespace checks enabled elsewhere.
FolderDiffIL4DotNet.Tests/Fixtures/ILCorpus/*.il whitespace=-blank-at-eol,-blank-at-eof
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

### [Unreleased]

#### Added

- **Configurable IL substring normalization** — Added `ShouldILNormalizeContainingConfiguredStrings` and `ILNormalizeContainingStrings`. When enabled, every occurrence of each configured substring is replaced with one comparison-local marker absent from both raw IL inputs (normally `<nildiff:normalized:configured-value>`, otherwise the first available numeric suffix), while all surrounding text on the same IL line remains comparable. Streaming comparison, fallback comparison, and materialized IL output share the selected marker. Both configured normalization and configured line exclusion apply across every IL instruction context, including program string literals loaded by `ldstr`. Leading and trailing whitespace in non-empty normalization values remains part of the exact match; whitespace-only values are ignored. This is intentionally separate from `ILIgnoreLineContainingStrings`, which continues to remove an entire matching line from comparison. Creator mode enables configured substring normalization automatically. Normalization values shorter than four characters now produce the same console/report safety warning class as short ignore values, with wording specific to the risk of normalizing legitimate IL content.
- **Real disassembler IL corpus** — Added reproducible source and pinned golden IL from `dotnet-ildasm 0.12.2.0` and `ilspycmd 9.1.0.7988`, the fallback baseline for inspecting assemblies targeting .NET 8/9/10. The signed test assembly covers ordinary classes and multiple methods, instance/static constructors, async and iterator state machines, lambdas and compiler-generated types, generic types and methods, properties, events, fields, signed AssemblyRefs, and reproducible COM interop metadata. A clearly identified test-only strong-name key keeps the sample assembly and public key token reproducible. ActiveX wrapper output is documented but excluded because portable `aximp` tooling and reproducible input are not available in the cross-platform .NET SDK. Corpus regeneration requires PowerShell 7 (`pwsh`); its .NET major-version roll-forward policy now covers tool version probes as well as build/generation and restores the caller's environment afterward.

#### Changed

- **Build-variant IL values are normalized instead of discarded** — MVID, method RVA, code-size, and WinForms `AxHost.TypeLibraryTimeStampAttribute` values are replaced with rule-specific stable markers in every mode. Each matching line prefix is preserved while the build-variant portion that follows it is replaced. For ILSpy's multiline type-library timestamp form, the complete byte blob through its closing `)` is collapsed into one marker. Built-in rules cover the actual comment and custom-attribute forms emitted by both supported disassemblers, including ILSpy's custom-attribute syntax without dotnet-ildasm's `class ` token, and are applied to every IL text regardless of which disassembler produced it; the disassembler recorded in reports identifies pattern provenance only. Normalization still occurs after IL cache retrieval, with no cache format or key change, so compatible existing cache entries remain reusable.
- **Creator defaults preserve more meaningful differences** — Renamed the built-in profile from `buildserver-winforms` to `creator-default`. Its predefined build-server/path substrings now populate `ILNormalizeContainingStrings` instead of `ILIgnoreLineContainingStrings`, preserving non-variant content on matching lines. Creator-profile values form the baseline and are applied first in profile order; user-configured values follow in their configured array order. The `.publickeytoken = (` suppression entry was removed and is not replaced by normalization, so AssemblyRef public key token changes remain visible in creator mode.
- **Normalization reporting and tool guidance are more explicit** — Markdown and HTML reports show built-in normalization rules, configured normalization substrings, and configured ignore substrings in that order. Built-in and configured-normalization tables state that their row order is the application order. The tables distinguish `Line Prefix Pattern`, `Substring to Normalize`, and `Substring to Ignore`; configured values are displayed without decorative quotes, while an enabled setting with no effective values is reported without rendering an empty table. The built-in table also shows each pattern's replacement marker and observed disassembler. Duplicate and containment-related normalization values now produce console/report warnings, including relationships that arise after creator-profile values are prepended to configured values. Guidance clarifies sequential raw-text matching order and inserted-marker protection, why the current built-in and creator rules are order-independent, that `nildiff` itself targets `net8.0`, and that `ilspycmd 9.1.0.7988` is pinned as the fallback baseline for inspecting assemblies targeting .NET 8/9/10. English/Japanese configuration documentation, schema, guides, and report samples are aligned with the new behavior.
- **Normalization configuration and report output are bounded and unambiguous** — The combined configured/profile normalization list is limited to 256 entries of at most 4096 Unicode characters each; creator-profile and duplicate entries count toward the limit. Per line, all configured rules together are limited to 65,536 non-overlapping replacements and a normalized result of at most 4,194,304 UTF-16 code units; either excess throws `InvalidDataException` before the expanded result is allocated. Relationship diagnostics emit at most 100 details plus a suppressed-count summary and are skipped when configured normalization is disabled. Configured substring values embedded in safety warnings use a single-line visible escape form for line breaks, other whitespace, control/format characters, combining marks, lone UTF-16 surrogates, and backslashes while keeping CommonMark punctuation inert, preventing configuration text from altering console/log or report structure. Markdown report tables use reversible escaped values so backslashes, invisible characters, and CommonMark punctuation remain distinguishable without becoming inline syntax; HTML reports keep their ordinary pre-wrapped display.

#### Removed

- **`ShouldIgnoreMVID` configuration setting and API** — Removed as a breaking change; MVID values are now always normalized to `<nildiff:normalized:mvid>`. Supplying the former JSON key or `FOLDERDIFF_SHOULDIGNOREMVID` environment variable fails with an explicit migration error instead of being accepted and ignored.
- **`buildserver-winforms` creator profile name** — Removed as a breaking change. Use `creator-default`; the former name follows the normal unknown-profile path and exits with invalid arguments.

### [1.24.0] - 2026-07-29

#### Added
Expand Down Expand Up @@ -1719,6 +1736,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

### [Unreleased]

#### 追加

- **設定可能な IL 部分文字列正規化** — `ShouldILNormalizeContainingConfiguredStrings` と `ILNormalizeContainingStrings` を追加しました。有効時は、old/new双方のraw ILに存在しない比較ローカルマーカー(通常は `<nildiff:normalized:configured-value>`、衝突時は未使用の最初の数値suffix)を1個選び、設定した各部分文字列のすべての出現箇所をそのマーカーへ置換します。同じIL行にある周辺テキストは比較可能な状態で残し、ストリーミング比較、fallback比較、実体化IL出力で同じマーカーを共有します。設定正規化と設定行除外はどちらも、`ldstr` が読み込むプログラム文字列リテラルを含む、すべての IL 命令文脈へ適用します。空でない正規化値の前後空白は正確な一致条件の一部として保持し、空白だけの値は無視します。これは、一致した行全体を比較から除外する従来の `ILIgnoreLineContainingStrings` とは独立した機能です。creatorモードでは設定文字列の正規化を自動的に有効化します。4文字未満の正規化値は、短い無視値と同じコンソール/レポート安全性警告の対象とし、正規のIL内容まで正規化するリスクが分かる文言で表示します。
- **実逆アセンブラ IL コーパス** — 再生成可能なソースと、`dotnet-ildasm 0.12.2.0` および.NET 8/9/10をtarget frameworkとするAssemblyを調査するfallback baselineである `ilspycmd 9.1.0.7988` から取得したgolden ILを追加しました。署名済みテストAssemblyは、通常classと複数method、instance/static constructor、async/iterator state machine、lambda/compiler-generated type、generic type/method、property/event/field、署名済みAssemblyRef、再現可能なCOM interop metadataを網羅します。用途を明記したテスト専用strong-name鍵により、サンプルAssemblyとpublic key tokenの再現性を維持します。ActiveX wrapper出力は、cross-platform .NET SDKでportableな `aximp` ツールと再現可能な入力を用意できないため、除外理由のみ文書化しています。コーパス再生成にはPowerShell 7(`pwsh`)が必要で、.NET major-version roll-forwardポリシーはtool version probeからbuild/生成までを包含し、終了後に呼び出し元環境を復元します。

#### 変更

- **ビルド依存 IL 値を破棄せず正規化** — MVID、method RVA、code-size、WinFormsの `AxHost.TypeLibraryTimeStampAttribute` 値を、全モードで規則別の安定したマーカーへ置換するようにしました。照合した行接頭辞は保持し、それに続くビルド依存部分だけを置換します。ILSpyの複数行type-library timestamp形式では、閉じ `)` までのbyte blob全体を1個のマーカーへ縮約します。組み込み規則は、ILSpyのcustom attribute構文がdotnet-ildasmの `class ` tokenを含まない違いも含め、両対応逆アセンブラが実際に出力するコメント/custom attribute形式を網羅し、生成元の逆アセンブラに関係なくすべてのIL textへ適用します。レポート上の逆アセンブラ名はパターンの由来を示すだけです。正規化は従来どおりILキャッシュ取得後に行い、キャッシュ形式とキーは変更していないため、互換性のある既存キャッシュエントリは引き続き利用できます。
- **creator既定値で意味のある差分をより多く保持** — 組み込みプロファイル名を `buildserver-winforms` から `creator-default` へ変更しました。既定のbuild-server/path部分文字列は `ILIgnoreLineContainingStrings` ではなく `ILNormalizeContainingStrings` へ設定し、一致行にあるビルド依存でない内容を保持します。creatorプロファイル値を基盤としてプロファイル内の順序で先に適用し、その後にユーザー設定値を設定配列順で適用します。`.publickeytoken = (` の抑制エントリは削除し、正規化対象にもしていないため、AssemblyRefのpublic key token変更はcreatorモードでも差分として残ります。
- **正規化レポートとツール案内を明確化** — Markdown/HTMLレポートには、組み込み正規化規則、設定正規化部分文字列、設定無視部分文字列の順で表示します。組み込み表と設定正規化表には、行の並びが適用順であることを明記します。表では `Line Prefix Pattern`、`Substring to Normalize`、`Substring to Ignore` を区別し、設定値へ装飾的なダブルクオートを付けません。有効だが実効値が0件の場合は空の表を出さず、設定値がないことだけを明示します。組み込み表には各パターンの置換マーカーと確認元逆アセンブラも表示します。重複または包含関係にある正規化値は、creatorプロファイル値を設定値より前へ追加した結果生じた関係も含めてコンソール/レポート警告の対象にしました。raw textへの逐次一致順と挿入済みマーカーの保護、現行の組み込み規則とcreator規則で順序が結果へ影響しない理由、`nildiff` 本体が `net8.0` をtargetとすること、`ilspycmd 9.1.0.7988` を.NET 8/9/10をtarget frameworkとするAssembly調査の固定fallback baselineとすることを案内に明記しました。英日両方の設定文書、schema、guide、レポートサンプルを新しい挙動へ揃えました。
- **正規化設定とレポート出力を有界かつ明確に** — 設定値とプロファイル値の結合後の正規化リストを最大256件、各値を最大4096 Unicode文字に制限し、creatorプロファイル値と重複値も件数上限へ算入します。設定した全規則を合わせて、1行の非重複置換を最大65,536件、正規化後の結果を最大4,194,304 UTF-16コード単位に制限し、いずれかを超える場合は展開後の結果を割り当てる前に `InvalidDataException` をスローします。関係診断は最大100件の詳細と超過分の抑制件数を示す要約に制限し、設定正規化が無効な場合は省略します。安全性警告へ埋め込む設定部分文字列値は、改行、その他の空白、control/format文字、combining mark、単独UTF-16 surrogate、backslashを1行の可視escapeにし、CommonMark記号を不活性化して、設定文字列がconsole/logやレポート構造を変更できないようにします。Markdownレポート表は可逆なescape表現を使い、backslash、不可視文字、CommonMark記号をinline構文にせず区別できます。HTMLレポートは通常の空白に対する既存のpre-wrap表示を維持します。

#### 削除

- **`ShouldIgnoreMVID` 設定とAPI** — 破壊的変更として削除しました。MVID値は常に `<nildiff:normalized:mvid>` へ正規化します。旧JSON keyまたは環境変数 `FOLDERDIFF_SHOULDIGNOREMVID` を指定すると、受理して無視せず明示的な移行エラーで失敗します。
- **creatorプロファイル名 `buildserver-winforms`** — 破壊的変更として削除しました。`creator-default` を使用してください。旧名は通常の未知profile経路でinvalid argumentsとして終了します。

### [1.24.0] - 2026-07-29

#### 追加
Expand Down
43 changes: 41 additions & 2 deletions Common/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ public static class Constants
/// <summary>Name of the ILSpy command-line tool. / ILSpy コマンドラインツールの名前。</summary>
public const string ILSPY_CMD = "ilspycmd";

/// <summary>Supported ilspycmd version. / サポート対象の ilspycmd バージョン。</summary>
public const string ILSPY_CMD_VERSION = "9.1.0.7988";

/// <summary>Error message for invalid parallelism values. / 不正な並列度の値に対するエラーメッセージ。</summary>
public const string ERROR_MAX_PARALLEL = "The maximum degree of parallelism must be 1 or greater.";

Expand Down Expand Up @@ -89,13 +92,49 @@ public static class Constants
public const int IL_CACHE_TIME_TO_LIVE_DEFAULT_HOURS = 12;

/// <summary>
/// Line prefix used to identify and exclude MVID lines from IL output during comparison.
/// Line prefix used to identify MVID values for normalization during IL comparison.
/// MVID (Module Version ID) is metadata that can change on every rebuild and does not
/// represent a meaningful IL difference.
/// IL 出力から比較時に除外する MVID 行の接頭辞
/// IL 比較時に正規化する MVID 値を識別するための行接頭辞
/// MVID は再ビルドごとに変わり得る Module Version ID メタデータで、実行される IL 差分を直接意味しない。
/// </summary>
public const string IL_MVID_LINE_PREFIX = "// MVID:";

/// <summary>
/// dotnet-ildasm line prefix used to identify RVA values for normalization during IL comparison.
/// IL 比較時に正規化する dotnet-ildasm の RVA 値を識別するための行接頭辞。
/// </summary>
public const string IL_RVA_LINE_PREFIX = "// Method begins at Relative Virtual Address (RVA) 0x";

/// <summary>
/// ilspycmd line prefix used to identify RVA values for normalization during IL comparison.
/// IL 比較時に正規化する ilspycmd の RVA 値を識別するための行接頭辞。
/// </summary>
public const string IL_ILSPY_RVA_LINE_PREFIX = "// Method begins at RVA 0x";

/// <summary>
/// dotnet-ildasm line prefix used to identify code-size values for normalization during IL comparison.
/// IL 比較時に正規化する dotnet-ildasm のコードサイズ値を識別するための行接頭辞。
/// </summary>
public const string IL_CODE_SIZE_LINE_PREFIX = "// Code size ";

/// <summary>
/// ilspycmd line prefix used to identify code-size values for normalization during IL comparison.
/// IL 比較時に正規化する ilspycmd のコードサイズ値を識別するための行接頭辞。
/// </summary>
public const string IL_ILSPY_CODE_SIZE_LINE_PREFIX = "// Code size: ";

/// <summary>
/// dotnet-ildasm line prefix used to identify WinForms type-library timestamps for normalization during IL comparison.
/// IL 比較時に正規化する dotnet-ildasm の WinForms タイプライブラリタイムスタンプを識別するための行接頭辞。
/// </summary>
public const string IL_TYPE_LIBRARY_TIMESTAMP_LINE_PREFIX = ".custom instance void class [System.Windows.Forms]System.Windows.Forms.AxHost/TypeLibraryTimeStampAttribute::.ctor(string) = ( ";

/// <summary>
/// ilspycmd line prefix used to identify WinForms type-library timestamps for normalization during IL comparison.
/// IL 比較時に正規化する ilspycmd の WinForms タイプライブラリタイムスタンプを識別するための行接頭辞。
/// </summary>
public const string IL_ILSPY_TYPE_LIBRARY_TIMESTAMP_LINE_PREFIX = ".custom instance void [System.Windows.Forms]System.Windows.Forms.AxHost/TypeLibraryTimeStampAttribute::.ctor(string) = (";

}
}
4 changes: 2 additions & 2 deletions FolderDiffIL4DotNet.Tests/CliOptionsTests.Combined.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void ParseCliOptions_AllFlagsCombined_ParsedCorrectly()
{
"/old", "/new", "lbl",
"--no-pause", "--no-il-cache", "--skip-il",
"--no-timestamp-warnings", "--creator", "--creator-il-ignore-profile", "buildserver-winforms", "--print-config", "--dry-run",
"--no-timestamp-warnings", "--creator", "--creator-il-ignore-profile", "creator-default", "--print-config", "--dry-run",
"--fail-on-diff",
"--coffee", "--bell", "--wizard",
"--log-format", "json",
Expand All @@ -31,7 +31,7 @@ public void ParseCliOptions_AllFlagsCombined_ParsedCorrectly()
Assert.True(opts.SkipIL);
Assert.True(opts.NoTimestampWarnings);
Assert.True(opts.Creator);
Assert.Equal("buildserver-winforms", opts.CreatorIlIgnoreProfile);
Assert.Equal("creator-default", opts.CreatorIlIgnoreProfile);
Assert.True(opts.PrintConfig);
Assert.True(opts.DryRun);
Assert.True(opts.FailOnDiff);
Expand Down
Loading
Loading