-
Notifications
You must be signed in to change notification settings - Fork 316
ILEmit backend for DependencyInjeciton #630
Conversation
I see Emit and I upvote ❤️ 🔥 🍺 |
src/DI/DI.csproj
Outdated
@@ -17,6 +17,8 @@ | |||
|
|||
<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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Private assets?
@@ -16,5 +16,6 @@ public ConstantCallSite(Type serviceType, object defaultValue) | |||
|
|||
public Type ServiceType => DefaultValue.GetType(); | |||
public Type ImplementationType => DefaultValue.GetType(); | |||
public CallSiteKind Kind { get; } = CallSiteKind.Constant; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public CallSiteKind Kind => CallSiteKind.Constant; Why do you want a backing field?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So JIT can do it's JITty stuff
src/DI/DI.csproj
Outdated
@@ -17,6 +17,8 @@ | |||
|
|||
<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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yah, what we actually really need is this https://github.com/dotnet/corefx/issues/25944
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, this is temporary.
if (ReferenceEquals(scope, rootScope) | ||
&& _scopedServices.TryGetValue(serviceType, out scopedService)) | ||
&& _scopedServices.TryGetValue(serviceType, out var scopedService)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PranavPoints++
{ | ||
VisitCallSite(parameterCallSite, argument); | ||
} | ||
argument.Generator.Emit(OpCodes.Newobj, constructorCallSite.ConstructorInfo); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we support value types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has anyone ever put a struct in the container? Now I'm curious...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sure someone did and we have to support it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure we add tests for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It didn't work in expression either, I tried adding tests and they fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah but the IL for newing up a struct is different soooo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nono, I'm sure they would fail in my code. They are failing in old Expression generating code too.
Typo alert in PR title: |
@@ -12,5 +12,7 @@ internal interface IServiceCallSite | |||
{ | |||
Type ServiceType { get; } | |||
Type ImplementationType { get; } | |||
CallSiteKind Kind { get; } | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: space
@@ -0,0 +1,15 @@ | |||
using System.Collections.Generic; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add copyright everywhere
CallSiteFactory = new CallSiteFactory(serviceDescriptors); | ||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment about ReferenceEqualsEqualityComparer
@@ -18,9 +18,10 @@ internal class ServiceProviderEngineScope : IServiceScope, IServiceProvider | |||
public ServiceProviderEngineScope(ServiceProviderEngine engine) | |||
{ | |||
Engine = engine; | |||
ResolvedServices = new Dictionary<object, object>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Undo
|
||
protected override ILEmitCallSiteAnalysisResult VisitIEnumerable(IEnumerableCallSite enumerableCallSite, object argument) | ||
{ | ||
var result = new ILEmitCallSiteAnalysisResult(6); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is 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)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
64, true?
|
||
private static bool BeginCaptureDisposable(Type implType, ILEmitResolverBuilderContext argument) | ||
{ | ||
var shouldCapture = !(implType != null && !typeof(IDisposable).GetTypeInfo().IsAssignableFrom(implType.GetTypeInfo())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😒
@jawn thanks 😄 |
@@ -17,5 +17,7 @@ public FactoryCallSite(Type serviceType, Func<IServiceProvider, object> factory) | |||
|
|||
public Type ServiceType { get; } | |||
public Type ImplementationType => null; | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Remove space
|
||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup | ||
{ | ||
internal struct ILEmitCallSiteAnalysisResult |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make this read only since Add returns a new struct?
|
||
public bool HasScope; | ||
|
||
public ILEmitCallSiteAnalysisResult Add(ILEmitCallSiteAnalysisResult other) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pass via in?
|
||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup | ||
{ | ||
internal sealed class ILEmitCallSiteAnalyzer : CallSiteVisitor<object, ILEmitCallSiteAnalysisResult> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Remove the ()
@@ -5,9 +5,9 @@ | |||
|
|||
namespace Microsoft.Extensions.DependencyInjection.Tests | |||
{ | |||
public class ServiceProviderCompiledContainerTests : ServiceProviderContainerTests | |||
public class ServiceProviderILEmitContainerTests: ServiceProviderContainerTests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: fix formatting
|
||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Remove extra space
10f7b69
to
c0ccdd2
Compare
|
||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup | ||
{ | ||
// This class walkes the service scope tree and tries to calculate approximate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
walks.....
{ | ||
VisitCallSite(parameterCallSite, argument); | ||
} | ||
argument.Generator.Emit(OpCodes.Newobj, constructorCallSite.ConstructorInfo); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: skit
|
||
context.Generator.BeginExceptionBlock(); | ||
|
||
// scope |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the codegen comment here
|
||
private static void EndCaptureDisposable(ILEmitResolverBuilderContext argument) | ||
{ | ||
argument.Generator.Emit(OpCodes.Callvirt, ExpressionResolverBuilder.CaptureDisposableMethodInfo); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM just the nits
@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.