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