ExpressionFactory
class ExpressionFactory
using Stashbox.Exceptions;
using Stashbox.Registration;
using Stashbox.Resolution;
using Stashbox.Resolution.Extensions;
using Stashbox.Utils.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
namespace Stashbox.Expressions
{
internal class ExpressionFactory
{
public Expression ConstructBuildUpExpression(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Expression instance, Type serviceType)
{
if (instance.Type != serviceRegistration.ImplementationType)
instance = instance.ConvertTo(serviceRegistration.ImplementationType);
MethodInfo[] usableMethods = serviceRegistration.ImplementationTypeInfo.GetUsableMethods();
MemberInfo[] usableMembers = serviceRegistration.ImplementationTypeInfo.GetUsableMembers(serviceRegistration.RegistrationContext, resolutionContext.CurrentContainerContext.ContainerConfiguration);
if (usableMembers.Length == 0 && usableMethods.Length == 0 && (object)serviceRegistration.RegistrationContext.Initializer == null)
return instance;
ParameterExpression parameterExpression = instance.Type.AsVariable(null);
BinaryExpression item = parameterExpression.AssignTo(instance);
ExpandableArray<Expression> expandableArray = new ExpandableArray<Expression> {
item
};
expandableArray.AddRange(GetMemberExpressions(usableMembers, serviceRegistration.RegistrationContext, resolutionContext, parameterExpression));
expandableArray.AddRange(CreateMethodExpressions(usableMethods, serviceRegistration.RegistrationContext, resolutionContext, parameterExpression));
if ((object)serviceRegistration.RegistrationContext.Initializer != null)
expandableArray.Add(serviceRegistration.RegistrationContext.Initializer.AsConstant().CallMethod(serviceRegistration.RegistrationContext.Initializer.GetType().GetSingleMethod("Invoke"), parameterExpression, resolutionContext.CurrentScopeParameter));
expandableArray.Add((parameterExpression.Type != serviceType) ? parameterExpression.ConvertTo(serviceType) : parameterExpression);
return expandableArray.AsBlock(parameterExpression);
}
public Expression ConstructBuildUpExpression(ResolutionContext resolutionContext, Expression instance, Type serviceType)
{
RegistrationContext empty = RegistrationContext.Empty;
TypeInfo typeInfo = instance.Type.GetTypeInfo();
MethodInfo[] usableMethods = typeInfo.GetUsableMethods();
MemberInfo[] usableMembers = typeInfo.GetUsableMembers(empty, resolutionContext.CurrentContainerContext.ContainerConfiguration);
if (usableMembers.Length == 0 && usableMethods.Length == 0)
return instance;
ParameterExpression parameterExpression = instance.Type.AsVariable(null);
BinaryExpression item = parameterExpression.AssignTo(instance);
ExpandableArray<Expression> expandableArray = new ExpandableArray<Expression>();
expandableArray.Add(item);
expandableArray.AddRange(GetMemberExpressions(usableMembers, empty, resolutionContext, parameterExpression));
expandableArray.AddRange(CreateMethodExpressions(usableMethods, empty, resolutionContext, instance));
expandableArray.Add((parameterExpression.Type != serviceType) ? parameterExpression.ConvertTo(serviceType) : parameterExpression);
return expandableArray.AsBlock(parameterExpression);
}
public Expression ConstructExpression(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext)
{
IEnumerable<ConstructorInfo> usableConstructors = serviceRegistration.ImplementationTypeInfo.GetUsableConstructors();
MethodInfo[] usableMethods = serviceRegistration.ImplementationTypeInfo.GetUsableMethods();
MemberInfo[] usableMembers = serviceRegistration.ImplementationTypeInfo.GetUsableMembers(serviceRegistration.RegistrationContext, resolutionContext.CurrentContainerContext.ContainerConfiguration);
Expression expression = CreateInitExpression(serviceRegistration.ImplementationType, serviceRegistration.RegistrationContext, resolutionContext, usableConstructors);
if (expression == null)
return null;
if (usableMembers.Length != 0)
expression = expression.InitMembers(GetMemberBindings(usableMembers, serviceRegistration.RegistrationContext, resolutionContext));
if (usableMethods.Length == 0 && (object)serviceRegistration.RegistrationContext.Initializer == null)
return expression;
ParameterExpression parameterExpression = expression.Type.AsVariable(null);
BinaryExpression item = parameterExpression.AssignTo(expression);
ExpandableArray<Expression> expandableArray = new ExpandableArray<Expression> {
item
};
expandableArray.AddRange(CreateMethodExpressions(usableMethods, serviceRegistration.RegistrationContext, resolutionContext, parameterExpression));
if ((object)serviceRegistration.RegistrationContext.Initializer != null)
expandableArray.Add(serviceRegistration.RegistrationContext.Initializer.AsConstant().CallMethod(serviceRegistration.RegistrationContext.Initializer.GetType().GetSingleMethod("Invoke"), parameterExpression, resolutionContext.CurrentScopeParameter));
expandableArray.Add(parameterExpression);
return expandableArray.AsBlock(parameterExpression);
}
public Expression ConstructExpression(ResolutionContext resolutionContext, Type serviceType)
{
RegistrationContext empty = RegistrationContext.Empty;
TypeInfo typeInfo = serviceType.GetTypeInfo();
MethodInfo[] usableMethods = typeInfo.GetUsableMethods();
MemberInfo[] usableMembers = typeInfo.GetUsableMembers(empty, resolutionContext.CurrentContainerContext.ContainerConfiguration);
Expression[] parameterExpressions;
Expression expression = SelectConstructor(serviceType, empty, resolutionContext, typeInfo.DeclaredConstructors.CastToArray(), out parameterExpressions).MakeNew(parameterExpressions);
if (usableMembers.Length != 0)
expression = expression.InitMembers(GetMemberBindings(usableMembers, empty, resolutionContext));
if (usableMethods.Length == 0)
return expression;
ParameterExpression parameterExpression = expression.Type.AsVariable(null);
BinaryExpression item = parameterExpression.AssignTo(expression);
ExpandableArray<Expression> expandableArray = new ExpandableArray<Expression>();
expandableArray.Add(item);
expandableArray.AddRange(CreateMethodExpressions(usableMethods, empty, resolutionContext, parameterExpression));
expandableArray.Add(parameterExpression);
return expandableArray.AsBlock(parameterExpression);
}
private Expression CreateInitExpression(Type typeToConstruct, RegistrationContext registrationContext, ResolutionContext resolutionContext, IEnumerable<ConstructorInfo> constructors)
{
if (registrationContext.SelectedConstructor != (ConstructorInfo)null) {
if (registrationContext.ConstructorArguments != null)
return registrationContext.SelectedConstructor.MakeNew(registrationContext.ConstructorArguments.Select(Expression.Constant));
return registrationContext.SelectedConstructor.MakeNew(CreateParameterExpressionsForMethod(registrationContext, resolutionContext, registrationContext.SelectedConstructor));
}
constructors = (registrationContext.ConstructorSelectionRule ?? resolutionContext.CurrentContainerContext.ContainerConfiguration.ConstructorSelectionRule)(constructors);
Expression[] parameterExpressions;
ConstructorInfo constructorInfo = SelectConstructor(typeToConstruct, registrationContext, resolutionContext, constructors.CastToArray(), out parameterExpressions);
if ((object)constructorInfo == null)
return null;
return constructorInfo.MakeNew(parameterExpressions);
}
private IEnumerable<Expression> GetMemberExpressions(IEnumerable<MemberInfo> members, RegistrationContext registrationContext, ResolutionContext resolutionContext, Expression instance)
{
return from member in members
let expression = GetMemberExpression(member, registrationContext, resolutionContext)
where expression != null
select instance.Member(member).AssignTo(expression);
}
private IEnumerable<MemberBinding> GetMemberBindings(IEnumerable<MemberInfo> members, RegistrationContext registrationContext, ResolutionContext resolutionContext)
{
return from member in members
let expression = GetMemberExpression(member, registrationContext, resolutionContext)
where expression != null
select member.AssignTo(expression);
}
private static Expression GetMemberExpression(MemberInfo member, RegistrationContext registrationContext, ResolutionContext resolutionContext)
{
TypeInformation typeInformation = member.AsTypeInformation(registrationContext, resolutionContext.CurrentContainerContext.ContainerConfiguration);
Expression expression = registrationContext.InjectionParameters.SelectInjectionParameterOrDefault(typeInformation);
if (expression != null)
return expression;
Expression expression2 = resolutionContext.ResolutionStrategy.BuildExpressionForType(resolutionContext, typeInformation);
if (expression2 != null || resolutionContext.NullResultAllowed)
return expression2;
string text = (member is PropertyInfo) ? "property" : "field";
throw new ResolutionFailedException(typeInformation.ParentType, typeInformation.DependencyName, "Unresolvable " + text + ": (" + typeInformation.Type.FullName + ")" + typeInformation.ParameterOrMemberName + ".", null);
}
private IEnumerable<Expression> CreateParameterExpressionsForMethod(RegistrationContext registrationContext, ResolutionContext resolutionContext, MethodBase method)
{
ParameterInfo[] parameters = method.GetParameters();
int paramLength = parameters.Length;
int i = 0;
TypeInformation parameter;
while (true) {
if (i >= paramLength)
yield break;
parameter = parameters[i].AsTypeInformation(method.DeclaringType, registrationContext, resolutionContext.CurrentContainerContext.ContainerConfiguration);
Expression expression = registrationContext.InjectionParameters.SelectInjectionParameterOrDefault(parameter);
if (expression != null)
yield return expression;
Expression expression2 = resolutionContext.ResolutionStrategy.BuildExpressionForType(resolutionContext, parameter);
if (expression2 == null)
break;
yield return expression2;
i++;
}
throw new ResolutionFailedException(method.DeclaringType, registrationContext.Name, $"""{method}""{parameter.Type}""{parameter.ParameterOrMemberName}", null);
}
private ConstructorInfo SelectConstructor(Type typeToConstruct, RegistrationContext registrationContext, ResolutionContext resolutionContext, ConstructorInfo[] constructors, out Expression[] parameterExpressions)
{
if (constructors.Length == 0)
throw new ResolutionFailedException(typeToConstruct, registrationContext.Name, "No public constructor found. Make sure there is at least one public constructor on the type.", null);
Dictionary<MethodBase, TypeInformation> dictionary = new Dictionary<MethodBase, TypeInformation>();
ResolutionContext resolutionContext2 = resolutionContext.CurrentContainerContext.ContainerConfiguration.UnknownTypeResolutionEnabled ? resolutionContext.BeginUnknownTypeCheckDisabledContext() : resolutionContext;
int num = constructors.Length;
for (int i = 0; i < num; i++) {
ConstructorInfo constructorInfo = constructors[i];
if (TryBuildMethod(constructorInfo, registrationContext, resolutionContext2, out TypeInformation failedParameter, out parameterExpressions))
return constructorInfo;
dictionary.Add(constructorInfo, failedParameter);
}
if (resolutionContext.CurrentContainerContext.ContainerConfiguration.UnknownTypeResolutionEnabled) {
for (int j = 0; j < num; j++) {
ConstructorInfo constructorInfo2 = constructors[j];
if (TryBuildMethod(constructorInfo2, registrationContext, resolutionContext, out TypeInformation _, out parameterExpressions))
return constructorInfo2;
}
}
if (resolutionContext.NullResultAllowed) {
parameterExpressions = null;
return null;
}
StringBuilder stringBuilder = new StringBuilder();
foreach (KeyValuePair<MethodBase, TypeInformation> item in dictionary) {
stringBuilder.AppendLine($"""{item.Key}""{item.Value.Type.FullName}""{item.Value.ParameterOrMemberName}""");
}
throw new ResolutionFailedException(typeToConstruct, registrationContext.Name, stringBuilder.ToString(), null);
}
private IEnumerable<Expression> CreateMethodExpressions(IEnumerable<MethodInfo> methods, RegistrationContext registrationContext, ResolutionContext resolutionContext, Expression instance)
{
foreach (MethodInfo method in methods) {
if (method.GetParameters().Length == 0)
yield return (Expression)instance.CallMethod(method, Array.Empty<Expression>());
else
yield return (Expression)instance.CallMethod(method, CreateParameterExpressionsForMethod(registrationContext, resolutionContext, method));
}
}
private static bool TryBuildMethod(MethodBase method, RegistrationContext registrationContext, ResolutionContext resolutionContext, out TypeInformation failedParameter, out Expression[] parameterExpressions)
{
ParameterInfo[] parameters = method.GetParameters();
int num = parameters.Length;
parameterExpressions = new Expression[num];
failedParameter = null;
for (int i = 0; i < num; i++) {
TypeInformation typeInformation = parameters[i].AsTypeInformation(method.DeclaringType, registrationContext, resolutionContext.CurrentContainerContext.ContainerConfiguration);
Expression expression = registrationContext.InjectionParameters.SelectInjectionParameterOrDefault(typeInformation);
parameterExpressions[i] = (expression ?? resolutionContext.ResolutionStrategy.BuildExpressionForType(resolutionContext, typeInformation));
if (parameterExpressions[i] == null) {
failedParameter = typeInformation;
return false;
}
}
return true;
}
}
}