Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make DependencyInjection more linker trimmable #38678

Closed
eerhardt opened this issue Jul 1, 2020 · 0 comments · Fixed by #38729
Closed

Make DependencyInjection more linker trimmable #38678

eerhardt opened this issue Jul 1, 2020 · 0 comments · Fixed by #38729
Assignees
Labels
area-Extensions-DependencyInjection linkable-framework Issues associated with delivering a linker friendly framework
Milestone

Comments

@eerhardt
Copy link
Member

eerhardt commented Jul 1, 2020

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:

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.

internal enum ServiceProviderMode
{
Default,
Dynamic,
Runtime,
Expressions,
ILEmit

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:

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:

<type fullname="System.Runtime.CompilerServices.RuntimeFeature">
<method signature="System.Boolean get_IsDynamicCodeCompiled()" body="stub" value="false" />
</type>

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-Extensions-DependencyInjection untriaged New issue has not been triaged by the area owner labels Jul 1, 2020
@jkotas jkotas added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 1, 2020
@eerhardt eerhardt self-assigned this Jul 1, 2020
@marek-safar marek-safar added this to the 5.0.0 milestone Jul 2, 2020
@marek-safar marek-safar removed the untriaged New issue has not been triaged by the area owner label Jul 2, 2020
eerhardt added a commit to eerhardt/runtime that referenced this issue Jul 2, 2020
Allow the unused ServiceProviderEngine strategy types to be trimmed by the ILLinker.

Fix dotnet#38678
eerhardt added a commit that referenced this issue Jul 3, 2020
* Make DependencyInjection more linker trimmable

Allow the unused ServiceProviderEngine strategy types to be trimmed by the ILLinker.

This allows for System.Linq.Expressions to be completely removed in a default Blazor application. It also removes one of two usages of System.Reflection.Emit.

Fix #38678
@ghost ghost locked as resolved and limited conversation to collaborators Dec 8, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Extensions-DependencyInjection linkable-framework Issues associated with delivering a linker friendly framework
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants