ExpressionBuilder
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 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;
}
}
}