Skip to content

Add ECMA-335 EnC metadata delta writer#20019

Open
NatElkins wants to merge 6 commits into
dotnet:mainfrom
NatElkins:enc-metadata-delta-writer
Open

Add ECMA-335 EnC metadata delta writer#20019
NatElkins wants to merge 6 commits into
dotnet:mainfrom
NatElkins:enc-metadata-delta-writer

Conversation

@NatElkins

@NatElkins NatElkins commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds an internal, standalone ECMA-335 Edit-and-Continue metadata delta writer to AbstractIL, with focused System.Reflection.Metadata-readable metadata tests. It has no behavior change and no in-tree semantic consumer yet; the consumer is the F# hot reload work in #19941. This follows the same upstreaming pattern as #20017 and #20018: land isolated infrastructure first, with deterministic byte and layout evidence, then wire the feature in a later PR.

What

Ten new internal AbstractIL files (file names kept from the development branch for diffability):

  • ILDeltaHandles - offset/handle primitives, coded-index discriminated unions, delta tokens
  • ILMetadataHeaps - heap sizes and offset bookkeeping
  • IlxDeltaStreams - user-string and standalone-signature token calculators (continue numbering from a baseline)
  • DeltaMetadataEncoding - ECMA-335 II.24.2.6 coded-index encoding
  • DeltaMetadataTypes - the writer's input model: plain row-description records (method/parameter/property/event rows, method-semantics updates, custom attribute rows) and the definition key types
  • DeltaMetadataTables / DeltaTableLayout / DeltaIndexSizing / DeltaMetadataSerializer - delta #- table stream, heap construction, layout and index sizing, EncLog/EncMap emission, generation GUID chaining
  • FSharpDeltaMetadataWriter - the coordinating writer

The writer's inputs are row records (names, tokens, signature blobs, RVAs) plus baseline heap offsets and row counts. It imports nothing from any diffing, baseline, or session machinery; a grep gate for such imports is part of how the extraction was validated. One line of ilwrite.fsi exposes the pre-existing markerForUnicodeBytes so the delta writer reuses the full writer's exact string-marker logic.

Tests

130 tests in tests/FSharp.Compiler.Service.Tests/DeltaMetadata/:

  • coded-index encodings asserted against the production Encoding.CodedIndices definitions, with ECMA-335 II.24.2.6 table order cross-checked against System.Reflection.Metadata's TableIndex as an independent reference;
  • System.Reflection.Metadata reader parity over emitted deltas (SrmReaderParityTests);
  • EncLog/EncMap correctness, stream layout, heap and index sizing;
  • multi-generation chaining with generation-2 heap offsets asserted against computed expected values;
  • standalone-signature delta rows asserted at baseline + 1 from a real seeded baseline;
  • serializer failure paths.

All additions are internal; no public surface area change.

Sequencing

This PR is part of splitting the F# hot reload work (#19941) into small, independently reviewable PRs. The planned order:

  1. Wave 1 (independent of each other, no cross dependencies): Add ResetCompilerGeneratedNameState to compiler-generated name generators #20017 (generated-name counter reset), Add Roslyn-format EnC CustomDebugInformation codec and portable PDB method CDI emission #20018 (EnC CustomDebugInformation codec and method CDI emission), Add ECMA-335 EnC metadata delta writer #20019 (EnC metadata delta writer), plus two upcoming slices: stable synthesized-name replay and the typed-tree differ with rude-edit classification.
  2. Wave 2: baseline reading and recorded CDI state (depends on Add Roslyn-format EnC CustomDebugInformation codec and portable PDB method CDI emission #20018 and the naming slice).
  3. Wave 3: the delta emitter and symbol matcher.
  4. Wave 4: the hot reload session, FCS surface, and the --test:HotReloadDeltas capture hook (F# hot reload: Edit-and-Continue delta emission behind --test:HotReloadDeltas #19941 in its final, much smaller form).
  5. Last, explicitly experimental: Add an experimental flag-gated in-process compile path for hot reload sessions #20031 (the flag-gated in-process compile perf path).

Each slice is rebased on current main, carries its own tests, and is flag-off zero-cost. Everything stays draft until reviewed in order.
Role in the train: the hot reload session emits its metadata deltas through this writer.

Refresh status (2026-07-17)

  • Refreshed against dotnet/fsharp main at 5928e91; the current reviewed head is e7d79c0.
  • A dedicated review pass completed for this PR, its findings were fixed in the lowest owning slice, and the PR has no unresolved review threads.
  • The downstream session, in-process compiler, umbrella, and SDK branches were restacked after the fixes, so this slice remains part of the decomposed review train.
  • The complete compiler stack passed the 11-step hot reload verifier, 456 service tests, 243 component tests with 2 expected skips, and 1411 EmittedIL tests with 3 expected skips. Replacement CI passed on this exact head.

Refresh status (2026-07-22)

  • Current head: 33fceef, refreshed against dotnet/fsharp main 69fca7f.
  • Stack position: independent wave-1 metadata-writer infrastructure; consumed by Add the hot reload delta emitter and symbol matcher #20027 and every later composite slice.
  • Both July 21 review findings are fixed and resolved: one shared ECMA-335 #US encoder and cached trace configuration.
  • Local verification: full Debug build passed with 0 warnings/errors and all 134 DeltaMetadata tests passed.
  • Exact-head Azure compiler CI completed with 45/46 jobs passing. The sole failure occurred after clean repository builds when the policy-injected Component Detection task hit a Node/libuv agent assertion; a privileged rerun has been requested. The base-branch check_release_notes workflow still fails before analysis because pull_request_target refuses fork checkout.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

❗ Release notes required

You can open this PR in browser to add release notes: open in github.dev


✅ Found changes and release notes in following paths:

Change path Release notes path Description
src/Compiler docs/release-notes/.FSharp.Compiler.Service/11.0.100.md

@azure-pipelines

Copy link
Copy Markdown
Commenter does not have sufficient privileges for PR 20019 in repo dotnet/fsharp

@NatElkins
NatElkins marked this pull request as ready for review July 18, 2026 04:09
@NatElkins
NatElkins requested a review from a team as a code owner July 18, 2026 04:09
@github-actions github-actions Bot added the ⚠️ Affects-Compiler-Output Tooling check: PR touches IL emission or codegen label Jul 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Tooling Safety Check — Affects-Compiler-Output
Affects-Compiler-Output: modifies ilwrite.fsi (delta metadata writer)

Generated by PR Tooling Safety Check · opus46 5.4M ·

@T-Gro T-Gro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 This review was generated by AI (@expert-reviewer agent). Findings may contain inaccuracies — please verify independently.

Summary

Reviewed the ten new AbstractIL delta-writer sources and the two heap/exposure edits (skipped the ~5k lines of test files beyond spot-checking their intent). The ECMA-335 encoding is careful and internally consistent, and the design leans hard on System.Reflection.Metadata reader-parity tests, which is the right way to pin byte layout. I verified the pieces most prone to silent corruption and found them correct:

  • Coded-index tag/table definitions (DeltaMetadataEncoding, ILDeltaHandles.*.CodedTag) match ECMA-335 II.24.2.6, and the sizing math (codedBigness limit = 2^(16-tagBits), simple-index >= 0x10000) is right.
  • Fat method-body header (flags byte + 0x30 size nibble), and both small (12-byte clause / 1-byte DataSize / <=0xFF cutoff) and fat (24-byte clause / 3-byte DataSize) exception sections are encoded per II.25.4.
  • Compressed-integer length prefixes (1/2/4-byte forms) and the metadata-root / #~ stream header layout match the spec, and Valid/Sorted 64-bit masks correctly cover the PDB tables at 0x30–0x37 (TableCount = 64).
  • EncLog parent/member Add* pairing stays adjacent and EncMap is token-sorted.

No correctness, security, or error-handling blockers found. The two inline notes below are non-blocking maintainability/hygiene items.

Comment thread src/Compiler/AbstractIL/IlxDeltaStreams.fs Outdated
Comment thread src/Compiler/AbstractIL/DeltaMetadataSerializer.fs Outdated
@T-Gro T-Gro added the AI-reviewed PR reviewed by AI review council label Jul 21, 2026
@T-Gro
T-Gro self-requested a review July 21, 2026 11:00
Adds an internal, standalone ECMA-335 Edit-and-Continue metadata delta writer to
AbstractIL: delta #- table stream and heap construction (DeltaMetadataTables,
DeltaMetadataSerializer, DeltaTableLayout, DeltaIndexSizing), ECMA-335 II.24.2.6
coded-index encoding (DeltaMetadataEncoding), EncLog/EncMap emission, generation GUID
chaining, user-string and standalone-signature token calculators (IlxDeltaStreams),
and the coordinating writer (FSharpDeltaMetadataWriter) over a plain row-description
input model (DeltaMetadataTypes, ILDeltaHandles, ILMetadataHeaps).

The writer's inputs are row records (names, tokens, signatures, RVAs) plus heap
offsets; it has no dependency on any semantic diffing or session machinery. It
compiles with no in-tree consumer by design: the consumer is the F# hot reload work
in dotnet#19941, following the same upstreaming pattern as dotnet#20017 and dotnet#20018
(land isolated, test-covered infrastructure first, wire the feature in a later PR).

One line of ilwrite.fsi is touched to expose the pre-existing markerForUnicodeBytes
so the delta writer reuses the exact string-marker logic of the full writer. No
behavior change for any existing code path.

Tests (130): coded-index encodings asserted against the production definitions and
ECMA-335 II.24.2.6 order, System.Reflection.Metadata reader parity over emitted
deltas, EncLog/EncMap correctness, stream layout, heap and index sizing,
multi-generation heap-offset chaining asserted against computed expected values,
standalone-signature rows asserted at baseline+1 from a real seeded baseline, and
serializer failure paths.
Verified the repository-wide Fantomas check, the service test project build with zero warnings and errors, and all 134 DeltaMetadata tests.
@NatElkins
NatElkins force-pushed the enc-metadata-delta-writer branch from e7d79c0 to 33fceef Compare July 22, 2026 18:05
@NatElkins

Copy link
Copy Markdown
Contributor Author

The only Azure failure in this run was WindowsStrictIndentation. Both repository builds completed with zero warnings and zero errors, and the validation steps passed. The failure came afterward in the policy-injected Component Detection task when Node hit the libuv assertion new_time >= loop->time and exited with code 57005. All other 45 jobs passed. Could someone with pipeline permissions rerun the failed job?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚠️ Affects-Compiler-Output Tooling check: PR touches IL emission or codegen AI-reviewed PR reviewed by AI review council

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

2 participants