- 
                Notifications
    You must be signed in to change notification settings 
- Fork 5.2k
Description
In Microsoft.Extensions.DependencyInjection, there are a few different "engines" that decide how to instantiate services, and how to inject those services into objects using those services.
Options are:
- Dynamic
- Runtime
- ILEmit
- Expressions (System.Linq.Expressions)
The way it decides which to pick is based on an enum:
runtime/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs
Lines 31 to 63 in 8b50c51
| switch (options.Mode) | |
| { | |
| case ServiceProviderMode.Default: | |
| #if !NETCOREAPP | |
| _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback); | |
| #else | |
| if (RuntimeFeature.IsSupported("IsDynamicCodeCompiled")) | |
| { | |
| _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback); | |
| } | |
| else | |
| { | |
| // Don't try to compile Expressions/IL if they are going to get interpreted | |
| _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback); | |
| } | |
| #endif | |
| break; | |
| case ServiceProviderMode.Dynamic: | |
| _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback); | |
| break; | |
| case ServiceProviderMode.Runtime: | |
| _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback); | |
| break; | |
| #if IL_EMIT | |
| case ServiceProviderMode.ILEmit: | |
| _engine = new ILEmitServiceProviderEngine(serviceDescriptors, callback); | |
| break; | |
| #endif | |
| case ServiceProviderMode.Expressions: | |
| _engine = new ExpressionsServiceProviderEngine(serviceDescriptors, callback); | |
| break; | |
| default: | |
| throw new NotSupportedException(nameof(options.Mode)); | 
However, this enum is internal, and is only used in unit testing. An actual consumer will only ever get the ServiceProviderMode.Default option.
runtime/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProviderMode.cs
Lines 7 to 13 in 8b50c51
| internal enum ServiceProviderMode | |
| { | |
| Default, | |
| Dynamic, | |
| Runtime, | |
| Expressions, | |
| ILEmit | 
runtime/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProviderOptions.cs
Line 28 in e3ffd34
| internal ServiceProviderMode Mode { get; set; } = ServiceProviderMode.Default; | 
The issue is, the way the above code is written, none of unused options are being trimmed, even though they are never used. This means that all the code, and its dependencies (ILEmit, Linq.Expressions) are being preserved, even though they are not being used.
We should change DependencyInjection so that these unused implementations can be trimmed by the linker.
Also we should change:
runtime/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs
Line 37 in e3ffd34
| if (RuntimeFeature.IsSupported("IsDynamicCodeCompiled")) | 
to be
if (RuntimeFeature.IsDynamicCodeCompiled)Because on WASM the linker will see that property is hard-coded to false and the unused branch will be trimmed. See:
runtime/src/mono/netcore/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.wasm.xml
Lines 9 to 11 in 2125ac5
| <type fullname="System.Runtime.CompilerServices.RuntimeFeature"> | |
| <method signature="System.Boolean get_IsDynamicCodeCompiled()" body="stub" value="false" /> | |
| </type> |