ILEmit backend for DependencyInjeciton#630
Conversation
|
I see Emit and I upvote ❤️ 🔥 🍺 |
src/DI/DI.csproj
Outdated
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.Extensions.TypeNameHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" /> | ||
| <PackageReference Include="System.Reflection.Emit" PrivateAssets="All" Version="$(SystemReflectionEmitPackageVersion)" /> |
|
|
||
| public Type ServiceType => DefaultValue.GetType(); | ||
| public Type ImplementationType => DefaultValue.GetType(); | ||
| public CallSiteKind Kind { get; } = CallSiteKind.Constant; |
There was a problem hiding this comment.
public CallSiteKind Kind => CallSiteKind.Constant; Why do you want a backing field?
There was a problem hiding this comment.
So JIT can do it's JITty stuff
src/DI/DI.csproj
Outdated
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.Extensions.TypeNameHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" /> | ||
| <PackageReference Include="System.Reflection.Emit" PrivateAssets="All" Version="$(SystemReflectionEmitPackageVersion)" /> |
There was a problem hiding this comment.
I'd recommend making these package references conditional on TargetFramework. System.Ref.Emit is not supported in all netstandard2.0-compatible platforms.
There was a problem hiding this comment.
Yah, what we actually really need is this https://github.com/dotnet/corefx/issues/25944
There was a problem hiding this comment.
Yep, this is temporary.
| Type scopedService; | ||
| if (ReferenceEquals(scope, rootScope) | ||
| && _scopedServices.TryGetValue(serviceType, out scopedService)) | ||
| && _scopedServices.TryGetValue(serviceType, out var scopedService)) |
| { | ||
| VisitCallSite(parameterCallSite, argument); | ||
| } | ||
| argument.Generator.Emit(OpCodes.Newobj, constructorCallSite.ConstructorInfo); |
There was a problem hiding this comment.
Has anyone ever put a struct in the container? Now I'm curious...
There was a problem hiding this comment.
I'm sure someone did and we have to support it.
There was a problem hiding this comment.
Make sure we add tests for it.
There was a problem hiding this comment.
It didn't work in expression either, I tried adding tests and they fail.
There was a problem hiding this comment.
Yeah but the IL for newing up a struct is different soooo
There was a problem hiding this comment.
Nono, I'm sure they would fail in my code. They are failing in old Expression generating code too.
|
Typo alert in PR title: |
| Type ServiceType { get; } | ||
| Type ImplementationType { get; } | ||
| CallSiteKind Kind { get; } | ||
|
|
| @@ -0,0 +1,15 @@ | |||
| using System.Collections.Generic; | |||
| CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite()); | ||
| CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite()); | ||
|
|
||
| RealizedServices = new ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>>(new ReferenceEqualsEqualityComparer<Type>()); |
There was a problem hiding this comment.
Add a comment about ReferenceEqualsEqualityComparer
| public ServiceProviderEngineScope(ServiceProviderEngine engine) | ||
| { | ||
| Engine = engine; | ||
| ResolvedServices = new Dictionary<object, object>(); |
|
|
||
| protected override ILEmitCallSiteAnalysisResult VisitIEnumerable(IEnumerableCallSite enumerableCallSite, object argument) | ||
| { | ||
| var result = new ILEmitCallSiteAnalysisResult(6); |
|
|
||
| protected override ILEmitCallSiteAnalysisResult VisitSingleton(SingletonCallSite singletonCallSite, object argument) => VisitCallSite(singletonCallSite.ServiceCallSite, argument); | ||
|
|
||
| protected override ILEmitCallSiteAnalysisResult VisitScoped(ScopedCallSite scopedCallSite, object argument) => new ILEmitCallSiteAnalysisResult(64, true).Add(VisitCallSite(scopedCallSite.ServiceCallSite, argument)); |
|
|
||
| private static bool BeginCaptureDisposable(Type implType, ILEmitResolverBuilderContext argument) | ||
| { | ||
| var shouldCapture = !(implType != null && !typeof(IDisposable).GetTypeInfo().IsAssignableFrom(implType.GetTypeInfo())); |
|
@jawn thanks 😄 |
|
|
||
| public Type ServiceType { get; } | ||
| public Type ImplementationType => null; | ||
|
|
|
|
||
| namespace Microsoft.Extensions.DependencyInjection.ServiceLookup | ||
| { | ||
| internal struct ILEmitCallSiteAnalysisResult |
There was a problem hiding this comment.
Make this read only since Add returns a new struct?
|
|
||
| public bool HasScope; | ||
|
|
||
| public ILEmitCallSiteAnalysisResult Add(ILEmitCallSiteAnalysisResult other) |
|
|
||
| namespace Microsoft.Extensions.DependencyInjection.ServiceLookup | ||
| { | ||
| internal sealed class ILEmitCallSiteAnalyzer : CallSiteVisitor<object, ILEmitCallSiteAnalysisResult> |
There was a problem hiding this comment.
Write a general comment about why this exists.
| { | ||
| protected override IServiceProvider CreateServiceProvider(IServiceCollection collection) => | ||
| collection.BuildServiceProvider(new ServiceProviderOptions { Mode = ServiceProviderMode.Compiled }); | ||
| collection.BuildServiceProvider(new ServiceProviderOptions() { Mode = ServiceProviderMode.ILEmit}); |
| namespace Microsoft.Extensions.DependencyInjection.Tests | ||
| { | ||
| public class ServiceProviderCompiledContainerTests : ServiceProviderContainerTests | ||
| public class ServiceProviderILEmitContainerTests: ServiceProviderContainerTests |
|
|
||
| private Func<ServiceProviderEngineScope, object> BuildType(IServiceCallSite callSite) | ||
| { | ||
| var dynamicMethod = new DynamicMethod("ResolveService", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(object), new [] {typeof(ILEmitResolverBuilderRuntimeContext), typeof(ServiceProviderEngineScope) }, GetType(), true); |
There was a problem hiding this comment.
This line is too long. Comment on why you had to pass true as the last argument and use the named parameter syntax.
| return null; | ||
| } | ||
|
|
||
|
|
10f7b69 to
c0ccdd2
Compare
|
|
||
| namespace Microsoft.Extensions.DependencyInjection.ServiceLookup | ||
| { | ||
| // This class walkes the service scope tree and tries to calculate approximate |
| { | ||
| VisitCallSite(parameterCallSite, argument); | ||
| } | ||
| argument.Generator.Emit(OpCodes.Newobj, constructorCallSite.ConstructorInfo); |
There was a problem hiding this comment.
Add the comment // new T(...)
fdb06da to
c1d0447
Compare
|
|
||
| private Func<ServiceProviderEngineScope, object> BuildType(IServiceCallSite callSite) | ||
| { | ||
| // We need to skit visibility checks because services/constructors might be private |
|
|
||
| context.Generator.BeginExceptionBlock(); | ||
|
|
||
| // scope |
|
|
||
| private static void EndCaptureDisposable(ILEmitResolverBuilderContext argument) | ||
| { | ||
| argument.Generator.Emit(OpCodes.Callvirt, ExpressionResolverBuilder.CaptureDisposableMethodInfo); |
|
@pakrym why is the built red? |
|
I see the new test does 350 dependencies deep. Is this the new "limit"? (I see there are test files still included for 999) I'll grab this branch and try it out tomorrow with my breaking project from dotnet/aspnetcore#2737 |
|
@pakrym Works great. Also as @davidfowl mentioned about the build failing i had to add readonly to the struct properties to get it to compile locally as well.
|
8198538 to
7a34593
Compare
7a34593 to
bec355c
Compare


No description provided.