Skip to content

Annotate Core/Reflection reflective surface for AOT (closes #252)#259

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

Annotate Core/Reflection reflective surface for AOT (closes #252)#259
jeremydmiller merged 1 commit into
mainfrom
feature/aot-core-reflection-252

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Largest AOT-pillar slice (#213) — closes #252.

64 IL warnings per TFM across 6 files in Core/Reflection → 0, plus propagated annotations to keep EnumerableTypeExtensions clean.

Annotations summary

GenericFactoryCache.cs

All 8 BuildAs<T> overloads carry [RequiresDynamicCode] from PR #191. Adds matching [UnconditionalSuppressMessage("Trimming", "IL2055")] to suppress the MakeGenericType-in-lambda warnings — the method-level [RequiresDynamicCode] already documents the contract; source-generated callers supply an AOT-safe factoryFactory and never reach MakeGenericType.

TypeExtensions.cs

Member Annotation
ImplementsInterfaceTemplate, FindInterfaceThatCloses, Closes, FindParameterTypeTo, IsAnEnumerationOf [DAM(Interfaces)]
IsConcreteWithDefaultCtor [DAM(PublicConstructors)]
Create<T>(), Create [DAM(PublicParameterlessConstructor)]
IsGenericEnumerable suppress IL3050 (closes well-known IEnumerable<T> only)
Closes suppress IL2072 (recursive interface walk)

ReflectionExtensions.cs

Member Annotation
HasDefaultConstructor, HasConstructorsWithArguments [DAM(PublicConstructors | NonPublicConstructors)]
TryFindConstructor [DAM(PublicConstructors)]
TryFindMethod, TryFindStaticMethod [DAM(PublicMethods | NonPublicMethods)]
IsAsync suppress IL2072 (checks well-known Task / ValueTask)

EnumerableTypeExtensions.cs

Member Annotation
IsEnumerable [DAM(Interfaces)] (propagated from TypeExtensions.Closes)

LambdaBuilder.cs

[RequiresUnreferencedCode] on every public method: GetProperty, SetProperty, GetField, SetField, Getter, Setter. All compile expression trees via FastExpressionCompiler (itself RUC-annotated). Honest characterization: this whole class is for runtime expression compilation; AOT consumers should source-generate accessor delegates.

ValueTypeInfo.cs

[RequiresUnreferencedCode] on public ForType(Type), CreateWrapper<TOuter,TInner>, UnWrapper<TOuter,TInner> plus [DAM(PublicProperties | PublicConstructors | PublicMethods)] on ForType(Type). Strong-typed-id value-type discovery + Expression compilation — both fundamentally trim-hostile.

ReflectionHelper.cs

Member Annotation
MeetsSpecialGenericConstraints [DAM(PublicParameterlessConstructor)]
VisitNew suppress IL2026 (visits existing NewExpression; trim invariant owned by tree builder)
VisitNewArray suppress IL3050 (same — visits existing NewArrayExpression)

Effect on the punch list

Before:  JasperFx total per TFM  188
After:   JasperFx total per TFM  140  (-48)

The slice's direct 64-warning count was higher than the net delta because some warnings propagated up cascade chains (ReflectionExtensions inherited 4 from TypeExtensions DAM annotations, etc.) — each propagation got its own annotation, so the net result is 0 in the slice plus the surrounding cleanup.

Remaining (none introduced by this PR):

Issue Area Warnings
#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 #252.

🤖 Generated with Claude Code

Largest AOT-pillar slice (#213). 64 IL warnings per TFM across
6 files in Core/Reflection → 0, plus propagated annotations to keep
EnumerableTypeExtensions clean.

GenericFactoryCache.cs

  All 8 BuildAs<T> overloads carry [RequiresDynamicCode] from PR #191.
  Adds matching [UnconditionalSuppressMessage("Trimming", "IL2055")]
  to suppress the MakeGenericType-in-lambda warnings — the method-level
  [RequiresDynamicCode] already documents the contract; source-generated
  callers supply an AOT-safe factoryFactory and never reach MakeGenericType.

TypeExtensions.cs

  [DAM(Interfaces)]  on ImplementsInterfaceTemplate, FindInterfaceThatCloses,
                     Closes, FindParameterTypeTo, IsAnEnumerationOf
  [DAM(PublicConstructors)] on IsConcreteWithDefaultCtor
  [DAM(PublicParameterlessConstructor)] on Create<T>(), Create
  [UnconditionalSuppressMessage(IL3050)] on IsGenericEnumerable
                     (closes well-known IEnumerable<T> only)
  [UnconditionalSuppressMessage(IL2072)] on Closes (recursive interface walk)

ReflectionExtensions.cs

  [DAM(PublicConstructors | NonPublicConstructors)] on HasDefaultConstructor,
                     HasConstructorsWithArguments
  [DAM(PublicConstructors)] on TryFindConstructor
  [DAM(PublicMethods | NonPublicMethods)] on TryFindMethod, TryFindStaticMethod
  [UnconditionalSuppressMessage(IL2072)] on IsAsync
                     (checks well-known Task/ValueTask types)

EnumerableTypeExtensions.cs

  [DAM(Interfaces)] on IsEnumerable (propagated from TypeExtensions.Closes)

LambdaBuilder.cs

  [RequiresUnreferencedCode] on every public method:
    GetProperty, SetProperty, GetField, SetField, Getter, Setter
  All compile expression trees via FastExpressionCompiler (itself RUC-annotated).
  Honest characterization: this whole class is for runtime expression
  compilation; AOT consumers should source-generate accessor delegates.

ValueTypeInfo.cs

  [RequiresUnreferencedCode] on public ForType(Type), CreateWrapper<TOuter,TInner>,
                                    UnWrapper<TOuter,TInner>
  [DAM(PublicProperties | PublicConstructors | PublicMethods)] on ForType(Type)
  Strong-typed-id value-type discovery + Expression compilation; both
  fundamentally trim-hostile.

ReflectionHelper.cs

  [DAM(PublicParameterlessConstructor)] on MeetsSpecialGenericConstraints
  [UnconditionalSuppressMessage(IL2026/IL3050)] on VisitNew / VisitNewArray
                     (visits existing expression trees; trim invariant
                     owned by whoever built the input)

Effect on the punch list

  JasperFx total per TFM: 188 → 140  (-48)

  The slice's direct 64-warning count was higher than the net delta because
  some warnings propagated up cascade chains (ReflectionExtensions inherited
  4 from TypeExtensions DAM annotations, etc.) — each propagation got its
  own annotation, so the net result is the same 0 in the slice plus the
  surrounding cleanup.

  Remaining (all pre-existing, not introduced by this PR):
    #254 ServiceContainer.cs            16  (PR #256 in flight)
    #255 CodeGeneration/Services        22  (PR #257 in flight) — already
                                              addressed by ripple suppression
    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 #252.
@jeremydmiller jeremydmiller merged commit b4c25d7 into main May 13, 2026
1 check passed
thechucklingatom pushed a commit to thechucklingatom/jasperfx that referenced this pull request May 19, 2026
Final AOT-pillar cleanup pass for JasperFx (JasperFx#213). After PRs
JasperFx#256JasperFx#259 closed the four largest slices (ServiceContainer,
CodeGeneration/Services, Core/IoC, Core/Reflection), 74 warnings per
TFM remained across the long tail. This PR brings the JasperFx project
to **0 IL warnings** with `IsAotCompatible=true` enabled.

Annotated reflective entry points

  AssemblyFinder.FindAssemblies (3 overloads)   — [RUC]
  IJasperFxAssemblyLoadContext + impl            — [RUC]
  AssemblyTypes(Assembly)                        — [RUC]
  TypeRepository.ForAssembly / FindTypes (3x)    — [RUC]
  TypeQuery.Find(IEnumerable<Assembly>)          — [RUC]
  AssemblyScanner.Start + assembly-scan APIs     — [RUC] (matches
                                                   IAssemblyScanner)
  IAssemblyScanner.AssembliesFrom*               — [RUC] on interface
  CommandLineHostingExtensions.ApplyJasperFx*    — already RUC; cascades
  JasperFxOptions.HasReferenceToJasperFxTool     — [RUC]
  JasperFxOptions.DetermineCallingAssembly       — [RUC]
  JasperFxOptions.establishApplicationAssembly   — [RUC]
  JasperFxOptions.ReadHostEnvironment            — [RUC]
  JasperFxServiceCollectionExtensions.AddJasperFx + CritterStackDefaults
                                                  — [RUC]
  EnvironmentCheckExtensions (5 overloads)       — [RUC]
  SnapshotGate.Read / SnapshotGate.Write         — [RUC] + [RDC]
                                                   (STJ JsonSerializer)
  ISystemPart.WriteToConsole                     — [RUC] on interface
  DescribeCommand.Execute                        — suppress + #pragma
                                                   on async-state-machine
                                                   WriteToConsole call
  CodeGeneration/Frames/MethodCall(Type,string)  — [RUC] + [DAM(PublicMethods)]
  CodeGeneration/Frames/MethodCall.correctedReturnType — suppress IL2067
                                                   (well-known Task types)
  CodeGeneration/Frames/MethodCall.returnsValueTask — suppress IL2072
  CodeGeneration/GeneratedAssembly.AddType       — [DAM(PublicCtors|PublicMethods|NonPublicMethods)]
  CodeGeneration/GeneratedAssembly.AttachAssembly — [RUC]
  CodeGeneration/GeneratedType.CompiledType property — [DAM(PublicCtors)]
  CodeGeneration/GeneratedType.InheritsFrom<T>/<Type> — [DAM(PublicCtors|Methods)]
  CodeGeneration/GeneratedType.Implements<T>/(Type) — [DAM(PublicMethods)]
  CodeGeneration/GeneratedType.FindType + ApplySetterValues — [RUC]/suppress
  CodeGeneration/Model/Setter.SetInitialValue    — [RUC]
  CodeGeneration/Model/Variable.VariablesForProperties<T> — [DAM(PublicProperties)]
  CodeGeneration/Model/Variable.DefaultArgName   — suppress (cosmetic)
  CodeGeneration/Expressions/LambdaDefinition.Compile<TFunc> — [RUC]
  ServiceCollectionServerVariableSource.Matches  — suppress (IVariableSource
                                                   contract doesn't carry DAM)
  CodeGeneration/CodeGenerationExtensions.BuildExportedTypeIndex — suppress
  CodeGeneration/Services findFamily             — re-applied IL2067 suppression
                                                   (was lost in JasperFx#256 + JasperFx#257 merge)
  ServiceContainer.CouldResolve(Type)            — [DAM(PublicCtors)]
  ServiceContainer.findFamily                    — re-applied IL2067 suppression
                                                   (was lost in PR JasperFx#256 + JasperFx#257
                                                   merge resolution)
  Descriptors/OptionsDescription                 — [RUC] on ctor / For() / readProperties
  Descriptors/DatabaseDescriptor ctors           — [RUC] (inherits OptionsDescription)
  CommandLine/Descriptions/ConfigurationPreview.WriteToConsole — [RUC]
  CommandLine/Descriptions/DescribeCommand.WriteToConsole (×2 overrides) — [RUC]
  CommandLine/CommandFactory.IsJasperFxCommandType — [DAM(Interfaces)]
  CommandLine/CommandFactory.TryRegisterFromGeneratedManifest — IL2072 suppress added
  Resources/ResourcesCommand.ExecuteOnEach        — suppress IL3050
                                                   (Spectre WriteException
                                                   on error-display path)
  JasperFxAssemblyAttribute ctor                 — [DAM(PublicCtors|NonPublicCtors)]

Effect on the punch list

  Before:  JasperFx total per TFM  74 warnings
  After:   JasperFx total per TFM   0 warnings

  Cumulative since JasperFx#213 flag-flip (PR JasperFx#247):
    236 (initial fallout) → 0  (all addressed)

Note on JasperFx.Events

  The propagation of new annotations into the JasperFx.Events compilation
  surfaces ~246 warnings per TFM that aren't addressed here. That deserves
  its own focused PR + likely an issue under JasperFx#213.

Verification

  CoreTests       407/407 pass on net9.0 + net10.0
  CommandLineTests 280/280 pass on net9.0 + net10.0
  CodegenTests    366/366 pass on net9.0 + net10.0
  SmokeTestAot    build clean, exits 0

Closes (most) the AOT pillar JasperFx#213 for JasperFx itself. Events follow-up
deferred to a separate PR.
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 CodeGeneration/Services reflective surface

1 participant