Stashbox by Peter Csajtai

<PackageReference Include="Stashbox" Version="5.10.2-preview-815" />

 ExpressionFactory

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