Annotate CodeGeneration/Services reflective surface for AOT (closes #255)#257
Merged
Conversation
) Third AOT-pillar slice (#213). 22 IL warnings per TFM across 4 files in CodeGeneration/Services → 0, plus one small ripple suppression at ServiceContainer.findFamily's open-generic call into ServiceFamily.Close. Annotations ConstructorPlan.FindPublicConstructorCandidates(Type implementationType) [DynamicallyAccessedMembers(PublicConstructors)] on implementationType Satisfies IL2070 at the GetConstructors() call. Call site in TryBuildPlan passes descriptor.ImplementationType / KeyedImplementationType which already carry the matching DAM from MS DI. ServiceFamily.Close(Type[] parameterTypes) [RequiresUnreferencedCode] + [RequiresDynamicCode] Honest characterization: open-generic close uses MakeGenericType on ServiceType + each ImplementationType. AOT-publishing apps should register closed generics or use source-generated registration. Suppressed with justification ServiceProviderFamily.BuildDefaultPlan / ServiceScopeFactoryFamily.BuildDefaultPlan IL2111 at `new ServiceDescriptor(typeof(IServiceProvider), typeof(ServiceDescriptor), ...)` The placeholder typeof(ServiceDescriptor) is never actually instantiated — ServiceProviderPlan.CreateVariable throws NotImplementedException, the real IServiceProvider/IServiceScopeFactory comes from the host's DI. ArrayFamily(Type serviceType) IL2067 at the placeholder ServiceDescriptor for collection-shaped types (T[] / IEnumerable<T> / IList<T> / IReadOnlyList<T>). No constructor is ever invoked — CreateArrayFrame emits `new T[]{...}` source literal. ServiceContainer.findFamily(Type) — IL2026 + IL3050 Inherited from the now-annotated Close() call at line 241 (open-generic close path). Open-generic registration is itself a dynamic-code feature; AOT-publishing apps avoid it. Effect on the punch list JasperFx total per TFM: 188 → 166 (-22, CodeGeneration/Services slice) Remaining slices: #252 Core/Reflection 64 #253 Core/IoC 30 #254 ServiceContainer.cs 16 (PR #256 in flight) Core/TypeScanning 10 CodeGeneration/Snapshots 8 CodeGeneration/GeneratedType.cs 8 JasperFxOptions.cs 6 misc tail ~24 Overlap with #254 This PR adds [UnconditionalSuppressMessage] for IL2026 + IL3050 to ServiceContainer.findFamily. PR #256 (closes #254) adds a separate [UnconditionalSuppressMessage] for IL2067 to the same method. Whichever PR merges second will need a one-attribute textual rebase — the attributes themselves are non-conflicting. Verification CoreTests 407/407 pass on net9.0 + net10.0 SmokeTestAot build clean, exits 0
This was referenced May 13, 2026
thechucklingatom
pushed a commit
to thechucklingatom/jasperfx
that referenced
this pull request
May 19, 2026
Second AOT-pillar slice (JasperFx#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: JasperFx#252 Core/Reflection 64 JasperFx#254 ServiceContainer.cs 16 (PR JasperFx#256 in flight) JasperFx#255 CodeGeneration/Services 22 (PR JasperFx#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 JasperFx#253.
thechucklingatom
pushed a commit
to thechucklingatom/jasperfx
that referenced
this pull request
May 19, 2026
) Largest AOT-pillar slice (JasperFx#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 JasperFx#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): JasperFx#254 ServiceContainer.cs 16 (PR JasperFx#256 in flight) JasperFx#255 CodeGeneration/Services 22 (PR JasperFx#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 JasperFx#252.
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#256–JasperFx#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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Third AOT-pillar slice (#213) — closes #255.
22 IL warnings per TFM across 4 files in
CodeGeneration/Services→ 0, plus one small ripple suppression atServiceContainer.findFamilyfor the open-generic call into the now-annotatedServiceFamily.Close.Annotations
ConstructorPlan.FindPublicConstructorCandidates(Type implementationType)[DAM(PublicConstructors)]onimplementationType— satisfies IL2070 at theGetConstructors()call. The call site inTryBuildPlanpassesdescriptor.ImplementationType/KeyedImplementationType, which already carry the matching DAM from MS DI.ServiceFamily.Close(Type[] parameterTypes)[RequiresUnreferencedCode]+[RequiresDynamicCode]— open-generic close usesMakeGenericTypeonServiceType+ eachImplementationType. Honest characterization.Suppressed with justification
ServiceProviderFamily.BuildDefaultPlan/ServiceScopeFactoryFamily.BuildDefaultPlan(IL2111)typeof(ServiceDescriptor)is never actually instantiated —ServiceProviderPlan.CreateVariablethrowsNotImplementedException; the realIServiceProvider/IServiceScopeFactorycomes from the host's DI.ArrayFamily(Type serviceType)(IL2067)ServiceDescriptorfor collection-shaped types — no constructor is ever invoked,CreateArrayFrameemitsnew T[]{…}source literal.ServiceContainer.findFamily(Type)(IL2026 + IL3050)Close()call at the open-generic close path. Open-generic registration is itself a dynamic-code feature; AOT-publishing apps avoid it.Effect on the punch list
Remaining slices: #252 Core/Reflection (64), #253 Core/IoC (30), #254 ServiceContainer.cs (16, PR #256 in flight), plus smaller tails.
Overlap with #254
This PR adds an
[UnconditionalSuppressMessage]for IL2026 + IL3050 toServiceContainer.findFamily. PR #256 (closes #254) adds a separate[UnconditionalSuppressMessage]for IL2067 to the same method. Whichever PR merges second will need a one-attribute textual rebase — the attributes themselves are non-conflicting.Verification
CoreTests— 407/407 pass on net9.0 + net10.0SmokeTestAot— build clean, exits 0Closes #255.
🤖 Generated with Claude Code