Skip to content

Annotate Core/IoC reflective surface for AOT (closes #253)#258

Merged
jeremydmiller merged 1 commit into
mainfrom
feature/aot-core-ioc-253
May 13, 2026
Merged

Annotate Core/IoC reflective surface for AOT (closes #253)#258
jeremydmiller merged 1 commit into
mainfrom
feature/aot-core-ioc-253

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Second AOT-pillar slice (#213) — closes #253.

30 IL warnings per TFM across 7 files in Core/IoC0, plus matching propagation to AssemblyScanner / ScanningExploder / ServiceCollectionExtensions.Scan to keep IL2026 quiet across the public convention-registration surface.

Why annotate (not just suppress)

Convention-scanning is fundamentally trim-hostile: TypeSet enumerates types from loaded assemblies, conventions register types by naming / interface patterns, and ServiceDescriptors are constructed from runtime-discovered Types. AOT-publishing apps should either avoid convention-based registration entirely (use explicit services.AddSingleton<TService, TImpl>()) or substitute a source-generated registration manifest.

Public surface annotated

Member Annotation
IRegistrationConvention.ScanTypes [RUC] + [RDC]
ServiceCollectionExtensions.Scan(IServiceCollection, Action<IAssemblyScanner>) [RUC] + [RDC]
ServiceCollectionExtensions.AddType(IServiceCollection, Type, Type, ServiceLifetime) [DAM(PublicConstructors)] on implementationType
AssemblyScanner.ApplyRegistrations [RUC] + [RDC]
FindAllTypesFilter.IRegistrationConvention.ScanTypes [RUC] + [RDC] (matches interface)

Implementations annotated (match interface)

DefaultConventionScanner.ScanTypes, FirstInterfaceConvention.ScanTypes, ImplementationMap.ScanTypes, GenericConnectionScanner.ScanTypes — all carry the matching [RequiresUnreferencedCode] + [RequiresDynamicCode].

Internal helpers annotated

Member Annotation
TypeExtensions.CanBeCreated(this Type) [DAM(PublicConstructors)]
TypeExtensions.FindFirstInterfaceThatCloses(this Type, Type) [DAM(Interfaces)]
TypeExtensions.FindInterfacesThatClose(this Type, Type) [DAM(Interfaces)]

Internal propagation

ScanningExploder.Explode + ExplodeSynchronously annotated with [RUC] + [RDC] to keep the convention-scan dispatch site quiet.

Suppressed with justification

Site Why
DefaultConventionScanner.FindServiceType IL2070 on GetInterfaces(). Reached only from ScanTypes which is annotated; user invariant for convention scanning is that registered types' interfaces survive trimming.
GenericConnectionScanner.addConcretionsThatCouldBeClosed IL2055 + IL2067 + IL3050 on MakeGenericType + ServiceDescriptor ctor. The caller (ScanTypes) is annotated [RDC].
FindAllTypesFilter.Matches + determineLeastSpecificButValidType IL2070 + IL2067 on type.GetConstructors() + FindFirstInterfaceThatCloses. Convention-scan discovery surface.
TypeExtensions.rawFindInterfacesThatCloses IL2070 on the recursive BaseType walk. Trim impact bounded by the entry-point [DAM(Interfaces)].

Effect on the punch list

Before:  JasperFx total per TFM  188
After:   JasperFx total per TFM  158  (-30, this slice)

Remaining slices: #252 Core/Reflection (64), #254 ServiceContainer.cs (16, PR #256 in flight), #255 CodeGeneration/Services (22, PR #257 in flight), plus smaller tails.

Verification

  • CoreTests — 407/407 pass on net9.0 + net10.0
  • SmokeTestAot — build clean, exits 0

Closes #253.

🤖 Generated with Claude Code

Second AOT-pillar slice (#213). 30 IL warnings per TFM across
7 files in Core/IoC → 0, plus matching propagation to AssemblyScanner /
ScanningExploder / ServiceCollectionExtensions.Scan to keep IL2026 quiet
across the public convention-registration surface.

Convention-scanning is fundamentally trim-hostile: TypeSet enumerates
types from loaded assemblies, conventions register types by naming /
interface patterns, and ServiceDescriptors are constructed from runtime-
discovered Types. AOT-publishing apps should either avoid convention-based
registration entirely (use explicit services.AddSingleton<T, TImpl>())
or substitute a source-generated registration manifest.

Public surface annotated

  IRegistrationConvention.ScanTypes               — [RUC] + [RDC]
  ServiceCollectionExtensions.Scan(IServiceCollection, Action<IAssemblyScanner>)
                                                  — [RUC] + [RDC]
  ServiceCollectionExtensions.AddType(IServiceCollection, Type, Type, ServiceLifetime)
                                                  — [DAM(PublicConstructors)] on implementationType
  AssemblyScanner.ApplyRegistrations              — [RUC] + [RDC]
  FindAllTypesFilter.IRegistrationConvention.ScanTypes
                                                  — [RUC] + [RDC] (matches interface)

Implementations annotated (match interface)

  DefaultConventionScanner.ScanTypes              — [RUC] + [RDC]
  FirstInterfaceConvention.ScanTypes              — [RUC] + [RDC]
  ImplementationMap.ScanTypes                     — [RUC] + [RDC]
  GenericConnectionScanner.ScanTypes              — [RUC] + [RDC]

Internal helpers annotated

  TypeExtensions.CanBeCreated(this Type)          — [DAM(PublicConstructors)]
  TypeExtensions.FindFirstInterfaceThatCloses(this Type, Type)
                                                  — [DAM(Interfaces)]
  TypeExtensions.FindInterfacesThatClose(this Type, Type)
                                                  — [DAM(Interfaces)]

Internal propagation

  ScanningExploder.Explode + ExplodeSynchronously — [RUC] + [RDC]

Suppressed with justification

  DefaultConventionScanner.FindServiceType — IL2070 on GetInterfaces()
  GenericConnectionScanner.addConcretionsThatCouldBeClosed — IL2055/IL2067/IL3050 on MakeGenericType + ServiceDescriptor
  FindAllTypesFilter.Matches + determineLeastSpecificButValidType — IL2070/IL2067
  TypeExtensions.rawFindInterfacesThatCloses — IL2070 on recursive BaseType walk

Effect on the punch list

  JasperFx total per TFM: 188 → 158  (-30, Core/IoC slice)

  Remaining slices:
    #252 Core/Reflection            64
    #254 ServiceContainer.cs        16   (PR #256 in flight)
    #255 CodeGeneration/Services    22   (PR #257 in flight)
    Core/TypeScanning               10
    CodeGeneration/Snapshots         8
    CodeGeneration/GeneratedType.cs  8
    JasperFxOptions.cs               6
    misc tail                       ~24

Verification

  CoreTests       407/407 pass on net9.0 + net10.0
  SmokeTestAot    build clean, exits 0

Closes #253.
@jeremydmiller jeremydmiller merged commit 9e3655e into main May 13, 2026
1 check passed
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 (#213): annotate Core/IoC reflective surface

1 participant