Skip to content

Trim-safe alternative for ConfigurationBinder.BindInstance()? #3001

@jonpryor

Description

@jonpryor

What would you like to be added:

A trimming-safe alternative to ConfigurationBinder.BindInstance().

Why is this needed:

If you build uno.chefs for macOS + NativeAOT:

dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop \
  -bl Chefs/Chefs.csproj -p:UseSkiaRendering=true -p:SelfContained=true \
  -p:PublishAot=true -p:IsAotCompatible=true -p:TrimmerSingleWarn=false -p:_ExtraTrimmerArgs=--verbose \
  -p:IlcGenerateMapFile=true -p:IlcGenerateMstatFile=true -p:IlcGenerateDgmlFile=true -p:IlcGenerateMetadataLog=true \
  -p:EmitCompilerGeneratedFiles=true -p:CompilerGeneratedFilesOutputPath=`pwd`/_gen

several of the warnings are around configuration:

…/Chefs/App.xaml.cs(45,16): warning IL2026: Using member 'Uno.Extensions.ApplicationBuilderExtensions.NavigateAsync<TShell>(IApplicationBuilder, Func<IServiceProvider, INavigator, Task>)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Cannot statically analyze the type of instance so its members may be trimmed. [From TypeDescriptor.GetConverter() and others.].
…/Chefs/App.xaml.host.cs(20,23): warning IL2026: Using member 'Uno.Extensions.Localization.HostBuilderExtensions.UseLocalization(IHostBuilder, Action<HostBuilderContext, IServiceCollection>)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Cannot statically analyze the type of instance so its members may be trimmed. [From TypeDescriptor.GetConverter() and others.].
…/Chefs/App.xaml.host.cs(20,23): warning IL3050: Using member 'Uno.Extensions.Localization.HostBuilderExtensions.UseLocalization(IHostBuilder, Action<HostBuilderContext, IServiceCollection>)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. Binding strongly typed objects to configuration values may require generating dynamic code at runtime. [From Array.CreateInstance() and others.].
…/Chefs/App.xaml.host.cs(20,23): warning IL2026: Using member 'Uno.Extensions.HostBuilderExtensions.UseSerialization(IHostBuilder, Action<HostBuilderContext, IServiceCollection>)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Default behavior requires Reflection. Use UseJsonSerializationResolvers instead.
…/Chefs/App.xaml.host.cs(20,23): warning IL3050: Using member 'Uno.Extensions.HostBuilderExtensions.UseSerialization(IHostBuilder, Action<HostBuilderContext, IServiceCollection>)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. Default behavior requires Reflection. Use UseJsonSerializationResolvers instead.
…/Chefs/App.xaml.host.cs(20,23): warning IL2026: Using member 'Uno.Extensions.HostBuilderExtensions.UseNavigation(IHostBuilder, IDictionary<Type, Type>, Action<IViewRegistry, IRouteRegistry>, Func<IServiceCollection, MappedViewRegistry>, Func<IServiceCollection, IRouteRegistry>, Func<NavigationConfiguration, NavigationConfiguration>, Action<HostBuilderContext, IServiceCollection>)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Cannot statically analyze the type of instance so its members may be trimmed. [From TypeDescriptor.GetConverter() and others.].
…/Chefs/App.xaml.host.cs(51,6): warning IL2026: Using member 'Uno.Extensions.Configuration.ConfigBuilderExtensions.Section<TSettingsOptions>(IConfigBuilder, String, Func<HostBuilderContext, IConfigurationSection>)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Cannot statically analyze the type of instance so its members may be trimmed. [From TypeDescriptor.GetConverter() and others.].
…/Chefs/App.xaml.host.cs(51,6): warning IL3050: Using member 'Uno.Extensions.Configuration.ConfigBuilderExtensions.Section<TSettingsOptions>(IConfigBuilder, String, Func<HostBuilderContext, IConfigurationSection>)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. Binding strongly typed objects to configuration values may require generating dynamic code at runtime. [From Array.CreateInstance() and others.].
…/Chefs/App.xaml.host.cs(51,6): warning IL2026: Using member 'Uno.Extensions.Configuration.ConfigBuilderExtensions.Section<TSettingsOptions>(IConfigBuilder, String, Func<HostBuilderContext, IConfigurationSection>)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Cannot statically analyze the type of instance so its members may be trimmed. [From TypeDescriptor.GetConverter() and others.].
…/Chefs/App.xaml.host.cs(51,6): warning IL3050: Using member 'Uno.Extensions.Configuration.ConfigBuilderExtensions.Section<TSettingsOptions>(IConfigBuilder, String, Func<HostBuilderContext, IConfigurationSection>)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. Binding strongly typed objects to configuration values may require generating dynamic code at runtime. [From Array.CreateInstance() and others.].

We see that HostBuilderExtensions.UseNavigation() hits ConfigBuilderExtensions.Section<TSettingsOptions>(), which eventually hits ConfigurationBinder.BindInstance(), which uses TryConvertValue():

All of these are annotated with [RequiresDynamicCode] and [RequiresUnreferencedCode] -- added in commit cd3e1a6 -- because:

In trying to be helpful and useful, this codepath is fundamentally incompatible with trimming.

"Fortunately," Uno isn't alone in this: IConfiguration.Bind() has the same annotations!:

[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Binding strongly typed objects to configuration values requires generating dynamic code at runtime, for example instantiating generic types.")]
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Cannot statically analyze the type of instance so its members may be trimmed")]
public static void Bind(this Microsoft.Extensions.Configuration.IConfiguration configuration, string key, object? instance);

If .NET is in the same proverbial boat, what does .NET do to support trimming around IConfiguration?

Source generators, of course: Configuration-binding source generator.

Alas, the above docs are rather sparse on what it does and how it works:

        // !! Configure call - to be replaced with source-gen'd implementation
        builder.Services.Configure<MyOptions>(section);

        // !! Get call - to be replaced with source-gen'd implementation
        MyOptions? options0 = section.Get<MyOptions>();

        // !! Bind call - to be replaced with source-gen'd implementation
        MyOptions options1 = new();
        section.Bind(options1);

How are the calls "replaced with source-gem'd implementation"s?

It's something to explore.

Questions

  • Do we need to care about these "originating" IL2026 and IL3050 warnings? uno.chefs appears to work even with these warnings, so they might not be a "real" problem (yet).
  • Assuming we do care, how do we fix them? Using the Configuration-binding source generator looks like an interesting solution, but should other ideas be considered?
  • Is it possible to "retcon" a solution atop .Section<TSettingsOptions>()/etc.? If possible, this means we don't need to update existing assemblies to propagate a fix.

For which Platform:

  • iOS
  • Android
  • WebAssembly
  • WebAssembly renders for Xamarin.Forms
  • Windows
  • Build tasks

Anything else we need to know?

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/enhancementNew feature or request.triage/untriagedIndicates an issue requires triaging or verification.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions