Stashbox by Peter Csajtai

<PackageReference Include="Stashbox" Version="5.0.1-preview-725" />

 ExpressionBuilder

static class 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 static class ExpressionBuilder { internal static Expression BuildExpressionForRegistration(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type requestedType) { Expression expression = BuildExpressionByRegistrationType(serviceRegistration, resolutionContext, requestedType); if (expression == null) return null; if (serviceRegistration.RegistrationContext.ExistingInstance == null && serviceRegistration.RegistrationContext.AsyncInitializer != null) expression = resolutionContext.CurrentScopeParameter.CallMethod(Constants.AddWithAsyncInitializerMethod, expression, serviceRegistration.RegistrationContext.AsyncInitializer.AsConstant()).ConvertTo(requestedType); if (serviceRegistration.RegistrationContext.ExistingInstance == null && serviceRegistration.RegistrationContext.Finalizer != null) expression = resolutionContext.CurrentScopeParameter.CallMethod(Constants.AddWithFinalizerMethod, expression, serviceRegistration.RegistrationContext.Finalizer.AsConstant()).ConvertTo(requestedType); if (!ShouldHandleDisposal(resolutionContext.CurrentContainerContext, serviceRegistration) || !expression.Type.IsDisposable()) return CheckRuntimeCircularDependencyExpression(expression, serviceRegistration, resolutionContext, requestedType); return CheckRuntimeCircularDependencyExpression(resolutionContext.CurrentScopeParameter.CallMethod(Constants.AddDisposalMethod, expression).ConvertTo(requestedType), serviceRegistration, resolutionContext, requestedType); } private static 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 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 static 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 static 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 ReadOnlyKeyValue<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 static Expression GetExpressionForFactory(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType) { if (resolutionContext.WeAreInCircle(serviceRegistration.RegistrationId)) throw new CircularDependencyException(serviceRegistration.ImplementationType, null); resolutionContext.PullOutCircularDependencyBarrier(serviceRegistration.RegistrationId); IEnumerable<Expression> factoryParameters = GetFactoryParameters(serviceRegistration, resolutionContext); Expression instance = ConstructFactoryExpression(serviceRegistration, factoryParameters); Expression result = ExpressionFactory.ConstructBuildUpExpression(serviceRegistration, resolutionContext, instance, resolveType); resolutionContext.LetDownCircularDependencyBarrier(); return result; } private static Expression ConstructFactoryExpression(ServiceRegistration serviceRegistration, IEnumerable<Expression> parameters) { if (serviceRegistration.RegistrationContext.IsFactoryDelegateACompiledLambda || serviceRegistration.RegistrationContext.Factory.IsCompiledLambda()) return serviceRegistration.RegistrationContext.Factory.InvokeDelegate(parameters); MethodInfo method = serviceRegistration.RegistrationContext.Factory.GetMethod(); if (!method.IsStatic) return method.CallMethod(serviceRegistration.RegistrationContext.Factory.Target.AsConstant(), parameters); return method.CallStaticMethod(parameters); } private static IEnumerable<Expression> GetFactoryParameters(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext) { int length = serviceRegistration.RegistrationContext.FactoryParameters.Length; for (int i = 0; i < length - 1; i++) { TypeInformation typeInformation = new TypeInformation(serviceRegistration.RegistrationContext.FactoryParameters[i], null); yield return resolutionContext.CurrentContainerContext.ResolutionStrategy.BuildExpressionForType(resolutionContext, typeInformation).ServiceExpression; } } private static Expression GetExpressionForFunc(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext) { MethodInfo method = serviceRegistration.RegistrationContext.FuncDelegate.GetMethod(); Expression[] funcParametersWithScope = GetFuncParametersWithScope(serviceRegistration.ImplementationType.GetMethod("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; } } }