Skip to content

Annotate Serialization/Serializer.cs reflective surface for AOT (closes #70)#74

Merged
jeremydmiller merged 1 commit into
mainfrom
feature/aot-serializer-70
May 13, 2026
Merged

Annotate Serialization/Serializer.cs reflective surface for AOT (closes #70)#74
jeremydmiller merged 1 commit into
mainfrom
feature/aot-serializer-70

Conversation

@jeremydmiller
Copy link
Copy Markdown
Member

First Polecat AOT-pillar slice (closes #70), mirroring the methodology that closed JasperFx slices #252-#255 + the tail in JasperFx/jasperfx#260.

ISerializer interface annotated

Member Annotation
ToJson(object) [RequiresUnreferencedCode] + [RequiresDynamicCode]
FromJson<T>(string | Stream | DbDataReader) (×3) [RUC] + [RDC]
FromJson(Type, string | Stream | DbDataReader) (×3) [RUC] + [RDC]
FromJsonAsync<T>(Stream, CancellationToken) [RUC] + [RDC]
FromJsonAsync(Type, Stream, CancellationToken) [RUC] + [RDC]

XML docs updated with the AOT story: AOT-publishing apps should supply a custom ISerializer implementation backed by a System.Text.Json source-generator JsonSerializerContext rather than the reflection-based default.

Serializer impl

Matching [RequiresUnreferencedCode] + [RequiresDynamicCode] on every override (required to keep IL2046 quiet across the impl boundary).

Suppressed with justification

Site Why
ApplyEnumStorage IL3050 on JsonStringEnumConverter(JsonNamingPolicy, bool). The non-generic enum converter requires runtime code gen; AOT consumers use the generic JsonStringEnumConverter<TEnum>.
ApplyNonPublicMembers IL2026 + IL3050 + IL2075 on DefaultJsonTypeInfoResolver + the fallback GetProperties scan. The NonPublicMembers feature is reflection-driven by intent; opting in is opting into reflection.

Effect on the punch list

Serializer.cs slice:  36 → 0     (closed)
Polecat total:        270 → 422  (+152 cascade)

The +152 cascade is honest propagation, not regression. With the un-annotated Serializer, every caller hid behind it; now the trim-impact surface is explicit at the call sites. The cascade lands in files already targeted by other AOT-pillar issues:

Plus new cascade in transitive callers (Patching/PatchExpression.cs, Events/EventOperations.cs, Internal/QuerySession.cs, Internal/DocumentProvider.cs) that will be absorbed into a future Polecat tail-cleanup PR mirroring JasperFx#260.

Verification

  • Polecat.csproj + Polecat.Tests.csproj build clean (0 errors)

Closes #70.

🤖 Generated with Claude Code

#70)

First Polecat AOT-pillar slice (polecat#70), mirroring the methodology
that closed JasperFx slices #252-#255 + the tail in JasperFx#260.

ISerializer interface annotated

  ToJson(object)                       — [RUC] + [RDC]
  FromJson<T>(string|Stream|DbDataReader)  — [RUC] + [RDC]
  FromJson(Type, string|Stream|DbDataReader)  — [RUC] + [RDC]
  FromJsonAsync<T>(Stream, CancellationToken)  — [RUC] + [RDC]
  FromJsonAsync(Type, Stream, CancellationToken)  — [RUC] + [RDC]

  Interface XML docs updated with the AOT story: AOT-publishing apps
  should supply a custom ISerializer implementation backed by a
  System.Text.Json source-generator JsonSerializerContext rather than
  the reflection-based default.

Serializer impl

  Matching [RequiresUnreferencedCode] + [RequiresDynamicCode] on every
  override (required to keep IL2046 quiet).

Suppressed with justification

  ApplyEnumStorage      — IL3050 on JsonStringEnumConverter
                          (non-generic enum converter; AOT consumers use
                           the generic JsonStringEnumConverter<TEnum>)
  ApplyNonPublicMembers — IL2026 + IL3050 + IL2075 on
                          DefaultJsonTypeInfoResolver + fallback
                          GetProperties scan. The NonPublicMembers
                          feature is reflection-driven by intent;
                          opting into it is opt-in to reflection.

Effect on the punch list (per TFM)

  Serializer.cs slice:  36 → 0 (closed)
  Polecat total:        270 → 422 (+152 cascade)

The +152 cascade is honest propagation, not regression. With the un-annotated
Serializer, every caller hid behind it; now the trim-impact surface is
explicit at the call sites. The cascade lands in files already targeted
by other AOT-pillar issues:

  DocumentStore.ProjectionReplay.cs — covered by polecat#71
  Linq/PolecatQueryableExtensions.cs / LinqQueryProvider.cs — covered by polecat#72
  Storage/DocumentMapping.cs + DocumentProviderRegistry — covered by polecat#73

Plus some new cascade in transitive callers (Patching/PatchExpression.cs,
Events/EventOperations.cs, Internal/QuerySession.cs, Internal/DocumentProvider.cs)
that will be absorbed into a future Polecat tail-cleanup PR mirroring
JasperFx#260.

Verification

  Polecat.csproj  + Polecat.Tests.csproj   build clean (0 errors)

Closes #70.
@jeremydmiller jeremydmiller merged commit be4b8f4 into main May 13, 2026
6 checks passed
jeremydmiller added a commit that referenced this pull request May 13, 2026
Two updates to keep the migration-guide accurate as the 2026-wave alphas
roll forward:

- Bump the foundation-pin table to current Polecat 4 alpha versions:
  JasperFx alpha.8 → alpha.11, JasperFx.Events alpha.3 → alpha.4,
  Weasel.* alpha.2 → alpha.3. Note that the alpha line is still rolling
  forward and consumers should pin all five together.

- Rewrite the AOT / codegen posture section to reflect the actual landed
  state: IsAotCompatible=true is set on the Polecat assembly (#67), and
  the reflective surfaces have been progressively annotated through #74
  (serialization), #75 (projection replay), #76 (LINQ extension/provider),
  and #77 (storage / registry / event-store explorer). Link out to the
  cross-stack "Publishing AOT with JasperFx" guide on jasperfx.github.io
  for the end-to-end walkthrough.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jeremydmiller added a commit that referenced this pull request May 13, 2026
Fifth slice in Polecat's AOT-pillar (jasperfx#213) cleanup, following the
Serializer (#74), ProjectionReplay (#75), LINQ (#76), and Storage/Registry
(#77) slices that have landed on main.

The patching subsystem reflects on the document's property expressions to
build JSON path strings and feeds the resulting patch values through
ISerializer.ToJson when emitting JSON_MODIFY() SQL. JsonPathHelper also
compiles small Expression.Lambda delegates to evaluate constant sub-
expressions during path resolution. Document types T flow in from
Schema.For<T>() / IDocumentSession.Patch<T>() at the registration boundary
and are preserved per the AOT publishing guide; AOT consumers supply a
source-generator-backed ISerializer impl.

Apply class-level [UnconditionalSuppressMessage] with justifications:

- Patching/PatchExpression<T>: IL2026/IL3050 (ISerializer.ToJson on
  patch values)
- Patching/PatchOperation: IL2026/IL3050 (ISerializer.ToJson when
  building JSON_MODIFY commands)
- Patching/JsonPathHelper: IL3050 (Expression.Lambda for constant
  sub-expression evaluation)

194 → 152 unique IL warnings (-42) in Polecat.csproj.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jeremydmiller jeremydmiller deleted the feature/aot-serializer-70 branch May 14, 2026 15:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AOT pillar: annotate Serialization/Serializer.cs reflective surface

1 participant