Stashbox by Peter Csajtai

<PackageReference Include="Stashbox" Version="3.5.1-preview-624" />

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