Stashbox by Peter Csajtai

<PackageReference Include="Stashbox" Version="3.1.1-preview-549" />

 ExpressionBuilder

using Stashbox.Exceptions; using Stashbox.Lifetime; using Stashbox.Registration; using Stashbox.Resolution; using Stashbox.Utils; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace Stashbox.Expressions { internal class ExpressionBuilder { private readonly ExpressionFactory expressionFactory; private readonly ServiceRegistrator serviceRegistrator; internal ExpressionBuilder(ExpressionFactory expressionFactory, ServiceRegistrator serviceRegistrator) { this.expressionFactory = expressionFactory; this.serviceRegistrator = serviceRegistrator; } internal Expression BuildExpressionAndApplyLifetime(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType) { if (!IsOutputLifetimeManageable(serviceRegistration)) return BuildExpressionForRegistration(serviceRegistration, resolutionContext, resolveType); return serviceRegistration.RegistrationContext.Lifetime.ApplyLifetime(this, serviceRegistration, resolutionContext, resolveType); } internal Expression BuildExpressionForRegistration(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType) { if (serviceRegistration.IsDecorator || resolutionContext.DecoratingType == resolveType) return BuildDisposalTrackingAndFinalizerExpression(serviceRegistration, resolutionContext, resolveType); IEnumerable<ServiceRegistration> decoratorsOrDefault = resolutionContext.CurrentContainerContext.DecoratorRepository.GetDecoratorsOrDefault(resolveType); if (decoratorsOrDefault == null) { if (!resolveType.IsClosedGenericType()) return BuildDisposalTrackingAndFinalizerExpression(serviceRegistration, resolutionContext, resolveType); decoratorsOrDefault = resolutionContext.CurrentContainerContext.DecoratorRepository.GetDecoratorsOrDefault(resolveType.GetGenericTypeDefinition()); if (decoratorsOrDefault == null) return BuildDisposalTrackingAndFinalizerExpression(serviceRegistration, resolutionContext, resolveType); } ResolutionContext resolutionContext2 = resolutionContext.BeginDecoratingContext(resolveType); Expression expression = BuildDisposalTrackingAndFinalizerExpression(serviceRegistration, resolutionContext2, resolveType); if (expression == null) return null; foreach (ServiceRegistration item in decoratorsOrDefault) { resolutionContext2.SetExpressionOverride(resolveType, expression); expression = BuildExpressionForRegistration(item, resolutionContext2, resolveType); if (expression == null) return null; } return expression; } private Expression BuildDisposalTrackingAndFinalizerExpression(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType) { Expression expression = BuildExpressionByRegistrationType(serviceRegistration, resolutionContext, resolveType); if (expression == null) return null; if (serviceRegistration.RegistrationContext.ExistingInstance == null && (object)serviceRegistration.RegistrationContext.Finalizer != null) expression = BuildFinalizerExpression(expression, serviceRegistration, resolutionContext.CurrentScopeParameter); if (!RegistrationHoldsDisposable(resolutionContext.CurrentContainerContext, serviceRegistration) || !expression.Type.IsDisposable()) return CheckRuntimeCircularDependencyExpression(expression, serviceRegistration, resolutionContext, resolveType); MethodInfo methodInfo = Constants.AddDisposalMethod.MakeGenericMethod(expression.Type); return CheckRuntimeCircularDependencyExpression(resolutionContext.CurrentScopeParameter.CallMethod(methodInfo, expression), serviceRegistration, resolutionContext, resolveType); } private Expression BuildExpressionByRegistrationType(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType) { resolutionContext = (resolutionContext.ShouldFallBackToRequestInitiatorContext ? resolutionContext.BeginCrossContainerContext(resolutionContext.RequestInitiatorContainerContext) : resolutionContext); switch (serviceRegistration.RegistrationType) { case RegistrationType.Factory: return GetExpressionForFactory(serviceRegistration, resolutionContext, resolveType); case RegistrationType.OpenGeneric: { Type implementationType = serviceRegistration.ImplementationType.MakeGenericType(resolveType.GetGenericArguments()); ServiceRegistration serviceRegistration2 = serviceRegistration.Clone(implementationType, RegistrationType.Default); serviceRegistration2.RegistrationContext.Name = null; serviceRegistrator.Register(resolutionContext.CurrentContainerContext, serviceRegistration2, resolveType, false); return BuildExpressionAndApplyLifetime(serviceRegistration2, resolutionContext, resolveType); } case RegistrationType.Instance: return serviceRegistration.RegistrationContext.ExistingInstance.AsConstant(); case RegistrationType.WireUp: return expressionFactory.ConstructBuildUpExpression(serviceRegistration, resolutionContext, serviceRegistration.RegistrationContext.ExistingInstance.AsConstant(), serviceRegistration.ImplementationType); case RegistrationType.Func: return GetExpressionForFunc(serviceRegistration, resolutionContext); default: return GetExpressionForDefault(serviceRegistration, resolutionContext, resolveType); } } private Expression CheckRuntimeCircularDependencyExpression(Expression expression, ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType) { if (!resolutionContext.CurrentContainerContext.ContainerConfiguration.RuntimeCircularDependencyTrackingEnabled) return expression; ParameterExpression parameterExpression = resolveType.AsVariable(null); return new Expression[4] { resolutionContext.CurrentScopeParameter.CallMethod(Constants.CheckRuntimeCircularDependencyBarrierMethod, serviceRegistration.RegistrationId.AsConstant(), resolveType.AsConstant()), parameterExpression.AssignTo(expression), resolutionContext.CurrentScopeParameter.CallMethod(Constants.ResetRuntimeCircularDependencyBarrierMethod, serviceRegistration.RegistrationId.AsConstant()), parameterExpression }.AsBlock(parameterExpression); } private static Expression BuildFinalizerExpression(Expression instanceExpression, ServiceRegistration serviceRegistration, Expression scopeExpression) { MethodInfo methodInfo = Constants.AddWithFinalizerMethod.MakeGenericMethod(instanceExpression.Type); return scopeExpression.CallMethod(methodInfo, instanceExpression, serviceRegistration.RegistrationContext.Finalizer.AsConstant()); } private static bool IsOutputLifetimeManageable(ServiceRegistration serviceRegistration) { if (serviceRegistration.RegistrationType != RegistrationType.OpenGeneric && serviceRegistration.RegistrationType != RegistrationType.Instance) return serviceRegistration.RegistrationContext.Lifetime != null; return false; } private static bool RegistrationHoldsDisposable(IContainerContext containerContext, ServiceRegistration serviceRegistration) { if (serviceRegistration.RegistrationContext.IsLifetimeExternallyOwned || serviceRegistration.RegistrationContext.ExistingInstance != null) return false; if (!containerContext.ContainerConfiguration.TrackTransientsForDisposalEnabled) return !(serviceRegistration.RegistrationContext.Lifetime is TransientLifetime); return true; } private Expression GetExpressionForDefault(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType) { if (resolutionContext.WeAreInCircle(serviceRegistration.RegistrationId)) throw new CircularDependencyException(resolveType, null); resolutionContext.PullOutCircularDependencyBarrier(serviceRegistration.RegistrationId); Expression result = PrepareDefaultExpression(serviceRegistration, resolutionContext, resolveType); resolutionContext.LetDownCircularDependencyBarrier(); return result; } private Expression PrepareDefaultExpression(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType) { if (serviceRegistration.RegistrationContext.DefinedScopeName == null) return expressionFactory.ConstructExpression(serviceRegistration, resolutionContext, resolveType); ParameterExpression parameterExpression = Constants.ResolutionScopeType.AsVariable(null); MethodCallExpression expression = resolutionContext.CurrentScopeParameter.ConvertTo(Constants.ResolverType).CallMethod(Constants.BeginScopeMethod, serviceRegistration.RegistrationContext.DefinedScopeName.AsConstant(), true.AsConstant()); ResolutionContext resolutionContext2 = resolutionContext.BeginNewScopeContext(new KeyValuePair<object, ParameterExpression>(serviceRegistration.RegistrationContext.DefinedScopeName, parameterExpression)); resolutionContext.AddDefinedVariable(parameterExpression); resolutionContext.AddInstruction(parameterExpression.AssignTo(expression.ConvertTo(Constants.ResolutionScopeType))); Expression result = expressionFactory.ConstructExpression(serviceRegistration, resolutionContext2, resolveType); foreach (KeyValuePair<int, ParameterExpression> item in resolutionContext2.DefinedVariables.Walk()) { resolutionContext.AddDefinedVariable(item.Value); } foreach (Expression singleInstruction in resolutionContext2.SingleInstructions) { resolutionContext.AddInstruction(singleInstruction); } return result; } private Expression GetExpressionForFactory(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType) { Expression instance = ((object)serviceRegistration.RegistrationContext.ContainerFactory != null) ? ConstructFactoryExpression(serviceRegistration.RegistrationContext.ContainerFactory, serviceRegistration, resolutionContext.CurrentScopeParameter.ConvertTo(Constants.ResolverType)) : ConstructFactoryExpression(serviceRegistration.RegistrationContext.SingleFactory, serviceRegistration); return expressionFactory.ConstructBuildUpExpression(serviceRegistration, resolutionContext, instance, resolveType); } private static Expression ConstructFactoryExpression(Delegate delegate, ServiceRegistration serviceRegistration, params Expression[] parameters) { if (serviceRegistration.RegistrationContext.IsFactoryDelegateACompiledLambda || delegate.IsCompiledLambda()) return delegate.InvokeDelegate(parameters); MethodInfo method = delegate.GetMethod(); if (!method.IsStatic) return method.CallMethod(delegate.Target.AsConstant(), parameters); return method.CallStaticMethod(parameters); } private Expression GetExpressionForFunc(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext) { MethodInfo method = serviceRegistration.RegistrationContext.FuncDelegate.GetMethod(); Expression[] funcParametersWithScope = GetFuncParametersWithScope(serviceRegistration.ImplementationType.GetSingleMethod("Invoke").GetParameters(), resolutionContext); if (serviceRegistration.RegistrationContext.FuncDelegate.IsCompiledLambda()) return serviceRegistration.RegistrationContext.FuncDelegate.InvokeDelegate(funcParametersWithScope).AsLambda(funcParametersWithScope.Take(funcParametersWithScope.Length - 1).Cast<ParameterExpression>()); return (method.IsStatic ? method.CallStaticMethod(funcParametersWithScope) : serviceRegistration.RegistrationContext.FuncDelegate.Target.AsConstant().CallMethod(method, funcParametersWithScope)).AsLambda(funcParametersWithScope.Take(funcParametersWithScope.Length - 1).Cast<ParameterExpression>()); } private static Expression[] GetFuncParametersWithScope(IList<ParameterInfo> parameterInfos, ResolutionContext resolutionContext) { int count = parameterInfos.Count; Expression[] array = new Expression[count + 1]; for (int i = 0; i < count; i++) { array[i] = parameterInfos[i].ParameterType.AsParameter(null); } array[array.Length - 1] = resolutionContext.CurrentScopeParameter.ConvertTo(Constants.ResolverType); return array; } } }