ExpressionFactory
using Stashbox.Exceptions;
using Stashbox.Registration;
using Stashbox.Resolution;
using Stashbox.Resolution.Extensions;
using Stashbox.Utils;
using Stashbox.Utils.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
namespace Stashbox.Expressions
{
[System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(0)]
internal static class ExpressionFactory
{
public static Expression ConstructBuildUpExpression(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Expression instance, TypeInformation typeInformation)
{
if (instance.Type != serviceRegistration.ImplementationType)
instance = instance.ConvertTo(serviceRegistration.ImplementationType);
MethodInfo[] usableMethods = serviceRegistration.ImplementationType.GetUsableMethods();
MemberInfo[] usableMembers = serviceRegistration.ImplementationType.GetUsableMembers(serviceRegistration, resolutionContext.CurrentContainerContext.ContainerConfiguration);
object orDefault = serviceRegistration.Options.GetOrDefault(RegistrationOption.Initializer);
if (usableMembers.Length == 0 && usableMethods.Length == 0 && orDefault == 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, resolutionContext, parameterExpression, typeInformation));
expandableArray.AddRange(CreateMethodExpressions(usableMethods, serviceRegistration, resolutionContext, parameterExpression, typeInformation));
if (orDefault != null)
expandableArray.Add(orDefault.AsConstant().CallMethod(orDefault.GetType().GetMethod("Invoke"), parameterExpression, resolutionContext.CurrentScopeParameter));
expandableArray.Add(parameterExpression);
return expandableArray.AsBlock(parameterExpression);
}
public static Expression ConstructBuildUpExpression(ResolutionContext resolutionContext, Expression instance, TypeInformation typeInformation)
{
Type type = instance.Type;
MethodInfo[] usableMethods = type.GetUsableMethods();
MemberInfo[] usableMembers = type.GetUsableMembers(null, resolutionContext.CurrentContainerContext.ContainerConfiguration);
if (usableMembers.Length == 0 && usableMethods.Length == 0)
return instance;
ParameterExpression parameterExpression = type.AsVariable(null);
BinaryExpression item = parameterExpression.AssignTo(instance);
ExpandableArray<Expression> expandableArray = new ExpandableArray<Expression>();
expandableArray.Add(item);
expandableArray.AddRange(GetMemberExpressions(usableMembers, null, resolutionContext, parameterExpression, typeInformation));
expandableArray.AddRange(CreateMethodExpressions(usableMethods, null, resolutionContext, instance, typeInformation));
expandableArray.Add(parameterExpression);
return expandableArray.AsBlock(parameterExpression);
}
[return: System.Runtime.CompilerServices.Nullable(2)]
public static Expression ConstructExpression(ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, TypeInformation typeInformation)
{
ConstructorInfo[] constructors = serviceRegistration.ImplementationType.GetConstructors();
Expression expression = CreateInitExpression(serviceRegistration.ImplementationType, serviceRegistration, typeInformation, resolutionContext, constructors);
if (expression == null)
return null;
MethodInfo[] usableMethods = serviceRegistration.ImplementationType.GetUsableMethods();
MemberInfo[] usableMembers = serviceRegistration.ImplementationType.GetUsableMembers(serviceRegistration, resolutionContext.CurrentContainerContext.ContainerConfiguration);
if (usableMembers.Length != 0)
expression = expression.InitMembers(GetMemberBindings(usableMembers, serviceRegistration, resolutionContext, typeInformation));
object orDefault = serviceRegistration.Options.GetOrDefault(RegistrationOption.Initializer);
if (usableMethods.Length == 0 && orDefault == 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, resolutionContext, parameterExpression, typeInformation));
if (orDefault != null)
expandableArray.Add(orDefault.AsConstant().CallMethod(orDefault.GetType().GetMethod("Invoke"), parameterExpression, resolutionContext.CurrentScopeParameter));
expandableArray.Add(parameterExpression);
return expandableArray.AsBlock(parameterExpression);
}
[return: System.Runtime.CompilerServices.Nullable(2)]
public static Expression ConstructExpression(ResolutionContext resolutionContext, TypeInformation typeInformation)
{
MethodInfo[] usableMethods = typeInformation.Type.GetUsableMethods();
MemberInfo[] usableMembers = typeInformation.Type.GetUsableMembers(null, resolutionContext.CurrentContainerContext.ContainerConfiguration);
Expression[] parameterExpressions;
ConstructorInfo constructorInfo = SelectConstructor(typeInformation.Type, null, typeInformation, resolutionContext, typeInformation.Type.GetConstructors(), out parameterExpressions);
Expression expression = ((object)constructorInfo != null) ? constructorInfo.MakeNew(parameterExpressions) : null;
if (expression == null)
return null;
if (usableMembers.Length != 0)
expression = expression.InitMembers(GetMemberBindings(usableMembers, null, resolutionContext, typeInformation));
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, null, resolutionContext, parameterExpression, typeInformation));
expandableArray.Add(parameterExpression);
return expandableArray.AsBlock(parameterExpression);
}
[return: System.Runtime.CompilerServices.Nullable(2)]
private static Expression CreateInitExpression(Type typeToConstruct, ServiceRegistration serviceRegistration, TypeInformation typeInformation, ResolutionContext resolutionContext, IEnumerable<ConstructorInfo> constructors)
{
Func<IEnumerable<ConstructorInfo>, IEnumerable<ConstructorInfo>> func = resolutionContext.CurrentContainerContext.ContainerConfiguration.ConstructorSelectionRule;
ConstructorOptions orDefault = serviceRegistration.Options.GetOrDefault<ConstructorOptions>(RegistrationOption.ConstructorOptions);
if (orDefault != null) {
if (orDefault.ConstructorArguments != null)
return orDefault.SelectedConstructor.MakeNew(orDefault.ConstructorArguments.Select(Expression.Constant));
return orDefault.SelectedConstructor.MakeNew(CreateParameterExpressionsForMethod(serviceRegistration, resolutionContext, orDefault.SelectedConstructor, typeInformation));
}
Func<IEnumerable<ConstructorInfo>, IEnumerable<ConstructorInfo>> orDefault2 = serviceRegistration.Options.GetOrDefault<Func<IEnumerable<ConstructorInfo>, IEnumerable<ConstructorInfo>>>(RegistrationOption.ConstructorSelectionRule);
if (orDefault2 != null)
func = orDefault2;
constructors = func(constructors);
Expression[] parameterExpressions;
ConstructorInfo constructorInfo = SelectConstructor(typeToConstruct, serviceRegistration, typeInformation, resolutionContext, constructors, out parameterExpressions);
if ((object)constructorInfo == null)
return null;
return constructorInfo.MakeNew(parameterExpressions);
}
private static IEnumerable<Expression> GetMemberExpressions(IEnumerable<MemberInfo> members, [System.Runtime.CompilerServices.Nullable(2)] ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Expression instance, TypeInformation typeInformation)
{
return from member in members
let expression = GetMemberExpression(member, serviceRegistration, resolutionContext, typeInformation)
where expression != null
select instance.Member(member).AssignTo(expression);
}
private static IEnumerable<MemberBinding> GetMemberBindings(IEnumerable<MemberInfo> members, [System.Runtime.CompilerServices.Nullable(2)] ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, TypeInformation typeInformation)
{
return from member in members
let expression = GetMemberExpression(member, serviceRegistration, resolutionContext, typeInformation)
where expression != null
select member.AssignTo(expression);
}
private static Expression GetMemberExpression(MemberInfo member, [System.Runtime.CompilerServices.Nullable(2)] ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, TypeInformation typeInformation)
{
TypeInformation typeInformation2 = member.AsTypeInformation(serviceRegistration, typeInformation, resolutionContext.CurrentContainerContext.ContainerConfiguration);
object obj;
if (serviceRegistration == null)
obj = null;
else {
ExpandableArray<KeyValuePair<string, object>> orDefault = serviceRegistration.Options.GetOrDefault<ExpandableArray<KeyValuePair<string, object>>>(RegistrationOption.InjectionParameters);
obj = ((orDefault != null) ? orDefault.SelectInjectionParameterOrDefault(typeInformation2) : null);
}
Expression expression = (Expression)obj;
if (expression != null)
return expression;
ServiceContext serviceContext = resolutionContext.CurrentContainerContext.ResolutionStrategy.BuildExpressionForType(resolutionContext, typeInformation2);
if (!serviceContext.IsEmpty() || resolutionContext.NullResultAllowed)
return serviceContext.ServiceExpression;
string text = (member is PropertyInfo) ? "property" : "field";
throw new ResolutionFailedException(typeInformation2.ParentType, typeInformation2.DependencyName, "Unresolvable " + text + ": (" + typeInformation2.Type.FullName + ")" + typeInformation2.ParameterOrMemberName + ".", null);
}
private static IEnumerable<Expression> CreateParameterExpressionsForMethod([System.Runtime.CompilerServices.Nullable(2)] ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, MethodBase method, TypeInformation typeInformation)
{
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, typeInformation, serviceRegistration, resolutionContext.CurrentContainerContext.ContainerConfiguration);
object obj;
if (serviceRegistration == null)
obj = null;
else {
ExpandableArray<KeyValuePair<string, object>> orDefault = serviceRegistration.Options.GetOrDefault<ExpandableArray<KeyValuePair<string, object>>>(RegistrationOption.InjectionParameters);
obj = ((orDefault != null) ? orDefault.SelectInjectionParameterOrDefault(parameter) : null);
}
Expression expression = (Expression)obj;
if (expression != null)
yield return expression;
Expression serviceExpression = resolutionContext.CurrentContainerContext.ResolutionStrategy.BuildExpressionForType(resolutionContext, parameter).ServiceExpression;
if (serviceExpression == null)
break;
yield return serviceExpression;
i++;
}
throw new ResolutionFailedException(method.DeclaringType, serviceRegistration?.Name, $"""{method}""{parameter.Type}""{parameter.ParameterOrMemberName}", null);
}
[return: System.Runtime.CompilerServices.Nullable(2)]
private static ConstructorInfo SelectConstructor(Type typeToConstruct, [System.Runtime.CompilerServices.Nullable(2)] ServiceRegistration serviceRegistration, TypeInformation typeInformation, ResolutionContext resolutionContext, IEnumerable<ConstructorInfo> constructorsEnumerable, out Expression[] parameterExpressions)
{
parameterExpressions = TypeCache.EmptyArray<Expression>();
ConstructorInfo[] array3;
if (resolutionContext.ParameterExpressions.Length > 0) {
ConstructorInfo[] array = constructorsEnumerable.CastToArray();
ConstructorInfo[] array2 = (from c in array
where Array.Exists(c.GetParameters(), (ParameterInfo p) => resolutionContext.ParameterExpressions.Any((Pair<bool, ParameterExpression>[] pe) => Array.Exists(pe, delegate(Pair<bool, ParameterExpression> item) {
if (!(item.I2.Type == p.ParameterType))
return item.I2.Type.Implements(p.ParameterType);
return true;
})))
select c).CastToArray();
IEnumerable<ConstructorInfo> second = array.Except(array2);
array3 = array2.Concat(second).CastToArray();
} else
array3 = constructorsEnumerable.CastToArray();
if (array3.Length == 0)
throw new ResolutionFailedException(typeToConstruct, serviceRegistration?.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 = array3.Length;
for (int i = 0; i < num; i++) {
ConstructorInfo constructorInfo = array3[i];
if (TryBuildMethod(constructorInfo, serviceRegistration, resolutionContext2, typeInformation, 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 = array3[j];
if (TryBuildMethod(constructorInfo2, serviceRegistration, resolutionContext, typeInformation, out TypeInformation _, out parameterExpressions))
return constructorInfo2;
}
}
if (resolutionContext.NullResultAllowed)
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, serviceRegistration?.Name, stringBuilder.ToString(), null);
}
private static IEnumerable<Expression> CreateMethodExpressions(IEnumerable<MethodInfo> methods, [System.Runtime.CompilerServices.Nullable(2)] ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Expression instance, TypeInformation typeInformation)
{
foreach (MethodInfo method in methods) {
if (method.GetParameters().Length == 0)
yield return (Expression)instance.CallMethod(method);
else
yield return (Expression)instance.CallMethod(method, CreateParameterExpressionsForMethod(serviceRegistration, resolutionContext, method, typeInformation));
}
}
private static bool TryBuildMethod(MethodBase method, [System.Runtime.CompilerServices.Nullable(2)] ServiceRegistration serviceRegistration, ResolutionContext resolutionContext, TypeInformation typeInformation, out TypeInformation failedParameter, out Expression[] parameterExpressions)
{
ParameterInfo[] parameters = method.GetParameters();
int num = parameters.Length;
parameterExpressions = new Expression[num];
failedParameter = TypeInformation.Empty;
for (int i = 0; i < num; i++) {
TypeInformation typeInformation2 = parameters[i].AsTypeInformation(method.DeclaringType, typeInformation, serviceRegistration, resolutionContext.CurrentContainerContext.ContainerConfiguration);
object obj;
if (serviceRegistration == null)
obj = null;
else {
ExpandableArray<KeyValuePair<string, object>> orDefault = serviceRegistration.Options.GetOrDefault<ExpandableArray<KeyValuePair<string, object>>>(RegistrationOption.InjectionParameters);
obj = ((orDefault != null) ? orDefault.SelectInjectionParameterOrDefault(typeInformation2) : null);
}
Expression expression = (Expression)obj;
if (expression != null)
parameterExpressions[i] = expression;
else {
ServiceContext serviceContext = resolutionContext.CurrentContainerContext.ResolutionStrategy.BuildExpressionForType(resolutionContext, typeInformation2);
if (serviceContext.IsEmpty()) {
failedParameter = typeInformation2;
return false;
}
parameterExpressions[i] = serviceContext.ServiceExpression;
}
}
return true;
}
}
}