Skip to content

docs(stjm): document trimming/NativeAOT limits, annotate reflection entry points, then add trim-safe explicit registration #210

Closed as not planned

Description

@egil

docs/recipes/aot-source-gen.md states that PublishAot / PublishTrimmed are not supported and that "trim-safe explicit registration is tracked as a follow-up". This issue is that follow-up. Start with the documentation and the annotations; the API comes second and the source generator (#ISSUE5) later.

Why it breaks today

Discovery is reflection over members the trimmer removes or cannot see:

  • StaticMigratorContracts.GetSourceTypes / JsonMigratableConverterFactory.ResolveStaticTryMigrateMethod: GetInterfaces(), GetMethods(...), MethodInfo.CreateDelegate.
  • MigratorInvokerFactory: MakeGenericMethod for the invoker factories.
  • JsonMigratableConverterFactory.CreateConverterCore: typeof(JsonMigratableConverter<>).MakeGenericType(...) + Activator.CreateInstance (IL3050 for struct targets under NativeAOT).
  • ExternalMigratorInvoker.CreateFallbackMigrator: Activator.CreateInstance(migratorType).
  • JsonMigrationBuilder.RegisterMigratorsFromAssembly: Assembly.GetTypes().
  • UnionCaseRouting.RefusedInsideOwnMigration.For: MakeGenericType on a marker; replace with a non-generic marker record.
  • JsonMigratableTypes.IsUnionWithMigratableCase: GetConstructors(); fine for source-generated unions (the ctors are referenced) but should be verified.

Attribute reads (GetCustomAttribute, GetCustomAttributesData) and options.GetTypeInfo are trim-safe.

Documentation first

  • Expand the NativeAOT section of docs/recipes/aot-source-gen.md with the list above: what is removed, what the failure looks like ("No migrator was found for discriminator '…'"), and that the workaround today is publishing untrimmed. Link this issue.
  • Annotate the reflection entry points with [RequiresUnreferencedCode] / [RequiresDynamicCode] so a consumer with PublishTrimmed gets IL2026/IL3050 warnings at their AddJsonMigrationSupport() call instead of a silent runtime failure. This is the cheapest correctness win and needs no API design.
  • Decide whether the package sets IsTrimmable / IsAotCompatible now (with the warnings) or only once the explicit API exists.

Explicit registration API (second step)

Generic methods let the compiler root everything the trimmer would otherwise drop, and give the generator in #ISSUE5 something to emit into:

builder.AddMigratable<UserV2>()                              // converter instantiated as new JsonMigratableConverter<UserV2>(...)
       .AddStaticMigration<UserV1, UserV2>()                 // where UserV2 : IMigrateFrom<UserV1, UserV2>; calls the static abstract directly
       .AddExternalMigration<UserV1, UserV2, UserMigrator>() // where UserMigrator : IMigrate<UserV1, UserV2>, new()
  • When any explicit registration exists for a target, skip reflection discovery for that target (or require explicit registration for every target when a TrimSafe mode is on; pick one and document it).
  • Keep RegisterMigrator* / RegisterMigratorsFromAssembly with the [RequiresUnreferencedCode] annotation.
  • Add a PublishAot sample project under samples/ that CI publishes on at least one platform, so the guarantee is tested rather than asserted.

Activity

  1. added
    documentationImprovements or additions to documentation
    stjmEgil.SystemTextJson.Migration scope
    on Sep 21, 2026
  2. egil commented on Sep 24, 2026

    @egil
    OwnerAuthor

    Superseded by #256, which makes actionable build diagnostics the first deliverable and preserves AOT-safe explicit registration as follow-up work. Users should receive the limitation and corrective action at their public API call without needing to read documentation.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationstjmEgil.SystemTextJson.Migration scope

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions