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;
using System.Runtime.CompilerServices;
namespace Stashbox.Expressions
{
[System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(0)]
internal static class ExpressionBuilder
{
[return: System.Runtime.CompilerServices.Nullable(2)]
internal static Expression BuildExpressionForRegistration(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, TypeInformation typeInformation)
{
Expression expression = BuildExpressionByRegistrationType(serviceRegistration, resolutionContext, typeInformation);
if (expression == null)
return null;
if (serviceRegistration.Options != null && !serviceRegistration.IsInstance()) {
if (serviceRegistration.Options.TryGetValue(RegistrationOption.AsyncInitializer, out object value))
expression = resolutionContext.CurrentScopeParameter.CallMethod(Constants.AddWithAsyncInitializerMethod, expression, value.AsConstant());
if (serviceRegistration.Options.TryGetValue(RegistrationOption.Finalizer, out object value2))
expression = resolutionContext.CurrentScopeParameter.CallMethod(Constants.AddWithFinalizerMethod, expression, value2.AsConstant());
}
if (!ShouldHandleDisposal(resolutionContext.CurrentContainerContext, serviceRegistration) || !expression.Type.IsDisposable())
return expression;
if (!resolutionContext.RequestConfiguration.RequiresRequestContext)
return resolutionContext.CurrentScopeParameter.CallMethod(Constants.AddDisposalMethod, expression);
return resolutionContext.CurrentScopeParameter.CallMethod(Constants.AddRequestContextAwareDisposalMethod, expression, resolutionContext.RequestContextParameter);
}
[return: System.Runtime.CompilerServices.Nullable(2)]
private static Expression BuildExpressionByRegistrationType(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, TypeInformation typeInformation)
{
resolutionContext = resolutionContext.FallBackToRequestInitiatorIfNeeded();
Dictionary<RegistrationOption, object> options = serviceRegistration.Options;
object obj = (options != null) ? options.GetOrDefault(RegistrationOption.RegistrationTypeOptions) : null;
FactoryOptions factoryOptions = obj as FactoryOptions;
if (factoryOptions != null)
return GetExpressionForFactory(serviceRegistration, factoryOptions, resolutionContext, typeInformation);
InstanceOptions instanceOptions = obj as InstanceOptions;
if (instanceOptions != null)
return instanceOptions.IsWireUp ? ExpressionFactory.ConstructBuildUpExpression(serviceRegistration, resolutionContext, instanceOptions.ExistingInstance.AsConstant(), typeInformation) : instanceOptions.ExistingInstance.AsConstant();
Delegate delegate = obj as Delegate;
if ((object)delegate != null)
return GetExpressionForFunc(serviceRegistration, delegate, resolutionContext);
return GetExpressionForDefault(serviceRegistration, resolutionContext, typeInformation);
}
private static bool ShouldHandleDisposal(IContainerContext containerContext, ServiceRegistration serviceRegistration)
{
if (serviceRegistration.Options.IsOn(RegistrationOption.IsLifetimeExternallyOwned) || serviceRegistration.IsInstance())
return false;
if (!containerContext.ContainerConfiguration.TrackTransientsForDisposalEnabled)
return !(serviceRegistration.Lifetime is TransientLifetime);
return true;
}
[return: System.Runtime.CompilerServices.Nullable(2)]
private static Expression GetExpressionForDefault(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, TypeInformation typeInformation)
{
if (resolutionContext.CircularDependencyBarrier.Contains(serviceRegistration.RegistrationId))
throw new CircularDependencyException(serviceRegistration.ImplementationType, null);
resolutionContext.CircularDependencyBarrier.Add(serviceRegistration.RegistrationId);
Expression result = PrepareDefaultExpression(serviceRegistration, resolutionContext, typeInformation);
resolutionContext.CircularDependencyBarrier.Pop();
return result;
}
[return: System.Runtime.CompilerServices.Nullable(2)]
private static Expression PrepareDefaultExpression(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, TypeInformation typeInformation)
{
object orDefault = serviceRegistration.Options.GetOrDefault(RegistrationOption.DefinedScopeName);
if (orDefault != null) {
ParameterExpression parameterExpression = TypeCache<IResolutionScope>.Type.AsVariable(null);
MethodCallExpression expression = resolutionContext.CurrentScopeParameter.CallMethod(Constants.BeginScopeMethod, orDefault.AsConstant(), true.AsConstant());
ResolutionContext resolutionContext2 = resolutionContext.BeginNewScopeContext(new ReadOnlyKeyValue<object, ParameterExpression>(orDefault, parameterExpression));
resolutionContext.AddDefinedVariable(parameterExpression);
resolutionContext.AddInstruction(parameterExpression.AssignTo(expression.ConvertTo(TypeCache<IResolutionScope>.Type)));
Expression result = ExpressionFactory.ConstructExpression(serviceRegistration, resolutionContext2, typeInformation);
foreach (ParameterExpression item in resolutionContext2.DefinedVariables.Walk()) {
resolutionContext.AddDefinedVariable(item);
}
{
foreach (Expression singleInstruction in resolutionContext2.SingleInstructions) {
resolutionContext.AddInstruction(singleInstruction);
}
return result;
}
}
return ExpressionFactory.ConstructExpression(serviceRegistration, resolutionContext, typeInformation);
}
private static Expression GetExpressionForFactory(ServiceRegistration serviceRegistration, FactoryOptions factoryOptions, ResolutionContext resolutionContext, TypeInformation typeInformation)
{
if (resolutionContext.CircularDependencyBarrier.Contains(serviceRegistration.RegistrationId))
throw new CircularDependencyException(serviceRegistration.ImplementationType, null);
resolutionContext.CircularDependencyBarrier.Add(serviceRegistration.RegistrationId);
IEnumerable<Expression> factoryParameters = GetFactoryParameters(factoryOptions, resolutionContext);
Expression instance = ConstructFactoryExpression(factoryOptions, factoryParameters);
Expression result = ExpressionFactory.ConstructBuildUpExpression(serviceRegistration, resolutionContext, instance, typeInformation);
resolutionContext.CircularDependencyBarrier.Pop();
return result;
}
private static Expression ConstructFactoryExpression(FactoryOptions factoryOptions, IEnumerable<Expression> parameters)
{
if (factoryOptions.IsFactoryDelegateACompiledLambda || factoryOptions.Factory.IsCompiledLambda())
return factoryOptions.Factory.InvokeDelegate(parameters);
MethodInfo method = factoryOptions.Factory.GetMethod();
if (!method.IsStatic)
return method.CallMethod(factoryOptions.Factory.Target.AsConstant(), parameters);
return method.CallStaticMethod(parameters);
}
private static IEnumerable<Expression> GetFactoryParameters(FactoryOptions factoryOptions, ResolutionContext resolutionContext)
{
int length = factoryOptions.FactoryParameters.Length;
for (int i = 0; i < length - 1; i++) {
TypeInformation typeInformation = new TypeInformation(factoryOptions.FactoryParameters[i], null);
yield return resolutionContext.CurrentContainerContext.ResolutionStrategy.BuildExpressionForType(resolutionContext, typeInformation).ServiceExpression;
}
}
private static Expression GetExpressionForFunc(ServiceRegistration serviceRegistration, Delegate func, ResolutionContext resolutionContext)
{
MethodInfo method = func.GetMethod();
Expression[] funcParametersWithScope = GetFuncParametersWithScope(serviceRegistration.ImplementationType.GetMethod("Invoke").GetParameters(), resolutionContext);
if (func.IsCompiledLambda())
return func.InvokeDelegate(funcParametersWithScope).AsLambda(funcParametersWithScope.Take(funcParametersWithScope.Length - 1).Cast<ParameterExpression>());
return (method.IsStatic ? method.CallStaticMethod(funcParametersWithScope) : func.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;
}
}
}