Stashbox by Peter Csajtai

<PackageReference Include="Stashbox" Version="3.5.1" />

 ExpressionBuilder

using Stashbox.Exceptions; using Stashbox.Lifetime; using Stashbox.Registration; using Stashbox.Resolution; using Stashbox.Utils; using Stashbox.Utils.Data; 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; internal ExpressionBuilder(ExpressionFactory expressionFactory) { this.expressionFactory = expressionFactory; } internal Expression BuildExpressionForRegistration(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type requestedType) { Expression expression = BuildExpressionByRegistrationType(serviceRegistration, resolutionContext, requestedType); if (expression == null) return null; if (serviceRegistration.RegistrationContext.ExistingInstance == null && (object)serviceRegistration.RegistrationContext.Finalizer != null) expression = BuildFinalizerExpression(expression, serviceRegistration, resolutionContext.CurrentScopeParameter); if (!ShouldHandleDisposal(resolutionContext.CurrentContainerContext, serviceRegistration) || !expression.Type.IsDisposable()) return CheckRuntimeCircularDependencyExpression(expression, serviceRegistration, resolutionContext, requestedType); MethodInfo methodInfo = Constants.AddDisposalMethod.MakeGenericMethod(expression.Type); return CheckRuntimeCircularDependencyExpression(resolutionContext.CurrentScopeParameter.CallMethod(methodInfo, expression), serviceRegistration, resolutionContext, requestedType); } private Expression BuildExpressionByRegistrationType(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type requestedType) { resolutionContext = (resolutionContext.ShouldFallBackToRequestInitiatorContext ? resolutionContext.BeginCrossContainerContext(resolutionContext.RequestInitiatorContainerContext) : resolutionContext); switch (serviceRegistration.RegistrationType) { case RegistrationType.Factory: return GetExpressionForFactory(serviceRegistration, resolutionContext, requestedType); 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); } } private static Expression CheckRuntimeCircularDependencyExpression(Expression expression, ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type requestedType) { if (!resolutionContext.CurrentContainerContext.ContainerConfiguration.RuntimeCircularDependencyTrackingEnabled) return expression; ParameterExpression parameterExpression = requestedType.AsVariable(null); return new Expression[4] { resolutionContext.CurrentScopeParameter.CallMethod(Constants.CheckRuntimeCircularDependencyBarrierMethod, serviceRegistration.RegistrationId.AsConstant(), requestedType.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 ShouldHandleDisposal(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) { if (resolutionContext.WeAreInCircle(serviceRegistration.RegistrationId)) throw new CircularDependencyException(serviceRegistration.ImplementationType, null); resolutionContext.PullOutCircularDependencyBarrier(serviceRegistration.RegistrationId); Expression result = PrepareDefaultExpression(serviceRegistration, resolutionContext); resolutionContext.LetDownCircularDependencyBarrier(); return result; } private Expression PrepareDefaultExpression(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext) { if (serviceRegistration.RegistrationContext.DefinedScopeName == null) return expressionFactory.ConstructExpression(serviceRegistration, resolutionContext); ParameterExpression parameterExpression = Constants.ResolutionScopeType.AsVariable(null); MethodCallExpression expression = resolutionContext.CurrentScopeParameter.CallMethod(Constants.BeginScopeMethod, serviceRegistration.RegistrationContext.DefinedScopeName.AsConstant(), true.AsConstant()); ResolutionContext resolutionContext2 = resolutionContext.BeginNewScopeContext(new KeyValue<object, ParameterExpression>(serviceRegistration.RegistrationContext.DefinedScopeName, parameterExpression)); resolutionContext.AddDefinedVariable(parameterExpression); resolutionContext.AddInstruction(parameterExpression.AssignTo(expression.ConvertTo(Constants.ResolutionScopeType))); Expression result = expressionFactory.ConstructExpression(serviceRegistration, resolutionContext2); foreach (ParameterExpression item in resolutionContext2.DefinedVariables.Walk()) { resolutionContext.AddDefinedVariable(item); } 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) : ConstructFactoryExpression(serviceRegistration.RegistrationContext.SingleFactory, serviceRegistration, Array.Empty<Expression>()); 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; return array; } } }