diff --git a/src/Builder/Abstracts/CompiledStrategy.cs b/src/Builder/Abstracts/CompiledStrategy.cs deleted file mode 100644 index 020a59b4..00000000 --- a/src/Builder/Abstracts/CompiledStrategy.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using Unity.Policy; -using Unity.Storage; -using Unity.Strategies; - -namespace Unity.Builder -{ - public class CompiledStrategy : BuilderStrategy, - IStagedStrategyChain, SelectionStage> - where TMemberInfo : MemberInfo - { - #region Fields - - private readonly List>[] _stages; - private Converter[] _cache; - - #endregion - - - #region Constructors - - public CompiledStrategy(params (Converter, SelectionStage)[] arguments) - { - var length = Enum.GetValues(typeof(SelectionStage)).Length; - _stages = new List>[length]; - for (var i = 0; i < length; i++) - { - _stages[i] = new List>(); - } - - foreach (var tuple in arguments) - { - _stages[Convert.ToInt32(tuple.Item2)].Add(tuple.Item1); - } - - _cache = _stages.SelectMany(s => s).ToArray(); - } - - #endregion - - - #region IStagedStrategyChain - - public void Add(Converter strategy, SelectionStage stage) - { - lock (_stages) - { - _stages[Convert.ToInt32(stage)].Add(strategy); - _cache = _stages.SelectMany(s => s).ToArray(); - Invalidated?.Invoke(this, new EventArgs()); - } - } - - #endregion - - - #region Event - - public event EventHandler Invalidated; - - #endregion - } -} diff --git a/src/Builder/Context/BuilderContext.cs b/src/Builder/Context/BuilderContext.cs index f5793292..a4498d42 100644 --- a/src/Builder/Context/BuilderContext.cs +++ b/src/Builder/Context/BuilderContext.cs @@ -1,12 +1,12 @@ using System; using System.Diagnostics; -using System.Linq.Expressions; using System.Reflection; using System.Security; using Unity.Policy; using Unity.Registration; using Unity.Resolution; using Unity.Storage; +using Unity.Strategies; namespace Unity.Builder { @@ -73,7 +73,7 @@ namespace Unity.Builder #endregion - #region Public Members + #region Public Properties public object Existing { get; set; } @@ -85,8 +85,6 @@ namespace Unity.Builder public Type DeclaringType { get; private set; } - public ParameterExpression Variable; - #endregion @@ -107,7 +105,7 @@ namespace Unity.Builder DeclaringType = RegistrationType }; - return registration.BuildChain.ExecuteReThrowingPlan(ref context); + return ExecuteReThrowingPlan(registration.BuildChain, ref context); } public object Resolve(ParameterInfo parameter, string name, object value) @@ -278,5 +276,36 @@ namespace Unity.Builder } #endregion + + + #region Implementation + + + private static object ExecuteReThrowingPlan(BuilderStrategy[] chain, ref BuilderContext context) + { + var i = -1; + + try + { + while (!context.BuildComplete && ++i < chain.Length) + { + chain[i].PreBuildUp(ref context); + } + + while (--i >= 0) + { + chain[i].PostBuildUp(ref context); + } + } + catch + { + context.RequiresRecovery?.Recover(); + throw; + } + + return context.Existing; + } + + #endregion } } diff --git a/src/Builder/Context/BuilderContextExpression.cs b/src/Builder/Context/BuilderContextExpression.cs new file mode 100644 index 00000000..65274891 --- /dev/null +++ b/src/Builder/Context/BuilderContextExpression.cs @@ -0,0 +1,29 @@ +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Unity.Resolution; + +namespace Unity.Builder +{ + public class BuilderContextExpression : IResolveContextExpression + { + #region Constructor + + static BuilderContextExpression() + { + var typeInfo = typeof(BuilderContext).GetTypeInfo(); + + + Existing = Expression.MakeMemberAccess(Context, typeInfo.GetDeclaredProperty(nameof(BuilderContext.Existing))); + } + + #endregion + + + #region Public Properties + + public static readonly MemberExpression Existing; + + #endregion + } +} diff --git a/src/Builder/Expressions/ContextExpression.cs b/src/Builder/Expressions/ContextExpression.cs deleted file mode 100644 index a24f2d79..00000000 --- a/src/Builder/Expressions/ContextExpression.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using Unity.Container.Lifetime; -using Unity.Resolution; - -namespace Unity.Builder.Expressions -{ - public class BuilderContextExpression : IResolveContextExpression - { - #region Fields - - private static ConstructorInfo _perResolveInfo = typeof(InternalPerResolveLifetimeManager) - .GetTypeInfo().DeclaredConstructors.First(); - - - private static readonly MethodInfo _resolveField = - typeof(BuilderContext).GetTypeInfo() - .GetDeclaredMethods(nameof(BuilderContext.Resolve)) - .First(m => - { - var parameters = m.GetParameters(); - return 2 <= parameters.Length && - typeof(FieldInfo) == parameters[0].ParameterType; - }); - - private static readonly MethodInfo _resolveProperty = - typeof(BuilderContext).GetTypeInfo() - .GetDeclaredMethods(nameof(BuilderContext.Resolve)) - .First(m => - { - var parameters = m.GetParameters(); - return 2 <= parameters.Length && - typeof(PropertyInfo) == parameters[0].ParameterType; - }); - - private static readonly MethodInfo _resolveParameter = - typeof(BuilderContext).GetTypeInfo() - .GetDeclaredMethods(nameof(BuilderContext.Resolve)) - .First(m => - { - var parameters = m.GetParameters(); - return 2 <= parameters.Length && - typeof(ParameterInfo) == parameters[0].ParameterType; - }); - - #endregion - - - #region Constructor - - static BuilderContextExpression() - { - var typeInfo = typeof(BuilderContext).GetTypeInfo(); - - - Existing = Expression.MakeMemberAccess(Context, typeInfo.GetDeclaredProperty(nameof(BuilderContext.Existing))); - } - - #endregion - - - #region Public Properties - - public static readonly MemberExpression Existing; - - #endregion - - - #region Methods - - public static Expression Resolve(FieldInfo field, string name, object resolver = null) - { - return Expression.Convert( - Expression.Call(Context, _resolveField, - Expression.Constant(field, typeof(FieldInfo)), - Expression.Constant(name, typeof(string)), - Expression.Constant(resolver, typeof(object))), - field.FieldType); - } - - public static Expression Resolve(PropertyInfo property, string name, object resolver = null) - { - return Expression.Convert( - Expression.Call(Context, _resolveProperty, - Expression.Constant(property, typeof(PropertyInfo)), - Expression.Constant(name, typeof(string)), - Expression.Constant(resolver, typeof(object))), - property.PropertyType); - } - - public static Expression Resolve(ParameterInfo parameter, string name, object resolver = null) - { - return Expression.Convert( - Expression.Call(Context, _resolveParameter, - Expression.Constant(parameter, typeof(ParameterInfo)), - Expression.Constant(name, typeof(string)), - Expression.Constant(resolver, typeof(object))), - parameter.ParameterType); - } - - public static Expression SetPerBuildSingleton(ref BuilderContext context) - { - return Set(context.RegistrationType, context.RegistrationName, typeof(LifetimeManager), - Expression.New(_perResolveInfo, context.Variable)); - } - - #endregion - } -} diff --git a/src/Builder/Expressions/ExceptionExpression.cs b/src/Builder/Expressions/ExceptionExpression.cs deleted file mode 100644 index 0c0b608f..00000000 --- a/src/Builder/Expressions/ExceptionExpression.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; - -namespace Unity.Builder.Expressions -{ - public class ExceptionExpression - { - public static readonly ConstructorInfo InvalidOperationExceptionCtor = - typeof(InvalidOperationException) - .GetTypeInfo() - .DeclaredConstructors - .First(c => - { - var parameters = c.GetParameters(); - return 2 == parameters.Length && - typeof(string) == parameters[0].ParameterType && - typeof(Exception) == parameters[1].ParameterType; - }); - } -} diff --git a/src/Builder/Plan/PlanExtension.cs b/src/Builder/Plan/PlanExtension.cs deleted file mode 100644 index 5c718540..00000000 --- a/src/Builder/Plan/PlanExtension.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using Unity.Strategies; - -namespace Unity.Builder -{ - public static class PlanExtension - { - public static object ExecutePlan(this BuilderStrategy[] chain, ref BuilderContext context) - { - var i = -1; - - while (!context.BuildComplete && ++i < chain.Length) - { - chain[i].PreBuildUp(ref context); - } - - while (--i >= 0) - { - chain[i].PostBuildUp(ref context); - } - - return context.Existing; - } - - public static object ExecuteThrowingPlan(this BuilderStrategy[] chain, ref BuilderContext context) - { - var i = -1; - - try - { - while (!context.BuildComplete && ++i < chain.Length) - { - chain[i].PreBuildUp(ref context); - } - - while (--i >= 0) - { - chain[i].PostBuildUp(ref context); - } - } - catch (Exception ex) - { - context.RequiresRecovery?.Recover(); - // TODO: 5.9.0 Add proper error message - throw new ResolutionFailedException(context.RegistrationType, - context.RegistrationName, - "", ex); - } - - return context.Existing; - } - - public static object ExecuteReThrowingPlan(this BuilderStrategy[] chain, ref BuilderContext context) - { - var i = -1; - - try - { - while (!context.BuildComplete && ++i < chain.Length) - { - chain[i].PreBuildUp(ref context); - } - - while (--i >= 0) - { - chain[i].PostBuildUp(ref context); - } - } - catch - { - context.RequiresRecovery?.Recover(); - throw; - } - - return context.Existing; - } - } -} diff --git a/src/Legacy/DynamicMethod/DynamicBuildPlanGenerationContext.cs b/src/Legacy/DynamicMethod/DynamicBuildPlanGenerationContext.cs index b6cf8a41..854229f0 100644 --- a/src/Legacy/DynamicMethod/DynamicBuildPlanGenerationContext.cs +++ b/src/Legacy/DynamicMethod/DynamicBuildPlanGenerationContext.cs @@ -4,9 +4,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; using Unity.Builder; -using Unity.Builder.Expressions; using Unity.Policy; -using Unity.Resolution; namespace Unity.ObjectBuilder.BuildPlan.DynamicMethod { diff --git a/src/Builder/Abstracts/NamedTypeBuildKey.cs b/src/Legacy/NamedTypeBuildKey.cs similarity index 100% rename from src/Builder/Abstracts/NamedTypeBuildKey.cs rename to src/Legacy/NamedTypeBuildKey.cs diff --git a/src/Processors/Abstracts/MemberBuildProcessor.cs b/src/Processors/Abstracts/MemberBuildProcessor.cs index 6c6bb362..6bd4f738 100644 --- a/src/Processors/Abstracts/MemberBuildProcessor.cs +++ b/src/Processors/Abstracts/MemberBuildProcessor.cs @@ -14,7 +14,28 @@ namespace Unity.Processors public abstract class MemberBuildProcessor { + #region Fields + + protected static readonly MethodInfo StringFormat = + typeof(string).GetTypeInfo() + .DeclaredMethods + .First(m => + { + var parameters = m.GetParameters(); + return m.Name == nameof(string.Format) && + m.GetParameters().Length == 2 && + typeof(object) == parameters[1].ParameterType; + }); + protected static readonly Expression InvalidRegistrationExpression = Expression.New(typeof(InvalidRegistrationException)); + + #endregion + + + #region Public Methods + public abstract IEnumerable GetEnumerator(ref BuilderContext context); + + #endregion } @@ -23,18 +44,6 @@ namespace Unity.Processors { #region Fields - protected static readonly MethodInfo StringFormat = - typeof(string).GetTypeInfo() - .DeclaredMethods - .First(m => - { - var parameters = m.GetParameters(); - return m.Name == nameof(string.Format) && - m.GetParameters().Length == 2 && - typeof(object) == parameters[1].ParameterType; - }); - protected static readonly Expression InvalidRegistrationExpression = Expression.New(typeof(InvalidRegistrationException)); - protected (Type type, MemberExpressionFactory factory)[] ResolverFactories; #endregion @@ -79,7 +88,7 @@ namespace Unity.Processors #region Public Methods - public virtual void Add(Type type, MemberExpressionFactory factory) + public void Add(Type type, MemberExpressionFactory factory) { for (var i = 0; i < ResolverFactories.Length; i++) { @@ -104,7 +113,7 @@ namespace Unity.Processors { var selector = GetPolicy>(ref context, context.RegistrationType, context.RegistrationName); var members = selector.Select(ref context); - return GetEnumerator(context.Type, context.Name, context.Variable, members); + return GetEnumerator(context.Type, context.Name, members); } #endregion @@ -112,7 +121,7 @@ namespace Unity.Processors #region Expression Building - protected virtual IEnumerable GetEnumerator(Type type, string name, ParameterExpression variable, IEnumerable members) + protected virtual IEnumerable GetEnumerator(Type type, string name, IEnumerable members) { foreach (var member in members) { @@ -121,13 +130,13 @@ namespace Unity.Processors switch (member) { case TMemberInfo memberInfo: - memberExpr = CreateMemberExpression(variable, memberInfo); + memberExpr = CreateMemberExpression(memberInfo); yield return BuildMemberExpression(memberExpr, memberInfo, name, null); break; case InjectionMember injectionMember: var (info, value) = injectionMember.FromType(type); - memberExpr = CreateMemberExpression(variable, info); + memberExpr = CreateMemberExpression(info); yield return BuildMemberExpression(memberExpr, info, name, value); break; @@ -169,7 +178,7 @@ namespace Unity.Processors protected virtual Expression ResolveExpression(TMemberInfo info, string name, object resolver) => throw new NotImplementedException(); - protected virtual MemberExpression CreateMemberExpression(ParameterExpression variable, TMemberInfo info) + protected virtual MemberExpression CreateMemberExpression(TMemberInfo info) => throw new NotImplementedException(); #endregion diff --git a/src/Processors/Abstracts/MethodBaseProcessor.cs b/src/Processors/Abstracts/MethodBaseProcessor.cs index e44c3eb7..12533cdb 100644 --- a/src/Processors/Abstracts/MethodBaseProcessor.cs +++ b/src/Processors/Abstracts/MethodBaseProcessor.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using System.Reflection; -using Unity.Builder.Expressions; +using Unity.Builder; using Unity.Injection; namespace Unity.Processors @@ -10,6 +11,21 @@ namespace Unity.Processors public abstract class MethodBaseInfoProcessor : MemberBuildProcessor where TMemberInfo : MethodBase { + #region Fields + + private static readonly MethodInfo ResolveParameter = + typeof(BuilderContext).GetTypeInfo() + .GetDeclaredMethods(nameof(BuilderContext.Resolve)) + .First(m => + { + var parameters = m.GetParameters(); + return 2 <= parameters.Length && + typeof(ParameterInfo) == parameters[0].ParameterType; + }); + + #endregion + + #region Constructors public MethodBaseInfoProcessor(Type attribute) @@ -27,7 +43,7 @@ namespace Unity.Processors protected override Type MemberType(TMemberInfo info) => info.DeclaringType; - protected override IEnumerable GetEnumerator(Type type, string name, ParameterExpression variable, IEnumerable members) + protected override IEnumerable GetEnumerator(Type type, string name, IEnumerable members) { foreach (var member in members) { @@ -35,13 +51,13 @@ namespace Unity.Processors { case TMemberInfo memberInfo: yield return ValidateMemberInfo(memberInfo) ?? - CreateExpression(memberInfo, null, variable); + CreateExpression(memberInfo, null); break; case MethodBaseMember injectionMember: var (info, resolvers) = injectionMember.FromType(type); yield return ValidateMemberInfo(info) ?? - CreateExpression(info, resolvers, variable); + CreateExpression(info, resolvers); break; default: @@ -76,12 +92,12 @@ namespace Unity.Processors if (null == defaultValueExpr) { // Plain vanilla case - expression = BuilderContextExpression.Resolve(parameter, null, resolver); + expression = ResolveExpression(parameter, null, resolver); } else { var variable = Expression.Variable(parameter.ParameterType); - var resolve = BuilderContextExpression.Resolve(parameter, null, resolver); + var resolve = ResolveExpression(parameter, null, resolver); expression = Expression.Block(new[] { variable }, new Expression[] { @@ -121,7 +137,7 @@ namespace Unity.Processors if (null == member) { // Plain vanilla case - return BuilderContextExpression.Resolve(parameter, ((DependencyResolutionAttribute)attribute).Name, resolver); + return ResolveExpression(parameter, ((DependencyResolutionAttribute)attribute).Name, resolver); } else { @@ -133,7 +149,7 @@ namespace Unity.Processors Expression.TryCatch( Expression.Assign( variable, - BuilderContextExpression.Resolve(parameter, ((DependencyResolutionAttribute)attribute).Name, resolver)), + ResolveExpression(parameter, ((DependencyResolutionAttribute)attribute).Name, resolver)), Expression.Catch(typeof(Exception), Expression.Assign(variable, member))), variable @@ -153,7 +169,7 @@ namespace Unity.Processors Expression.TryCatch( Expression.Assign( variable, - BuilderContextExpression.Resolve(parameter, ((DependencyResolutionAttribute)attribute).Name, resolver)), + ResolveExpression(parameter, ((DependencyResolutionAttribute)attribute).Name, resolver)), Expression.Catch(typeof(Exception), Expression.Assign(variable, member ?? Expression.Constant(null, parameter.ParameterType)))), variable @@ -166,7 +182,17 @@ namespace Unity.Processors #region Implementation - protected abstract Expression CreateExpression(TMemberInfo info, object[] resolvers, ParameterExpression variable); + private Expression ResolveExpression(ParameterInfo parameter, string name, object resolver = null) + { + return Expression.Convert( + Expression.Call(BuilderContextExpression.Context, ResolveParameter, + Expression.Constant(parameter, typeof(ParameterInfo)), + Expression.Constant(name, typeof(string)), + Expression.Constant(resolver, typeof(object))), + parameter.ParameterType); + } + + protected abstract Expression CreateExpression(TMemberInfo info, object[] resolvers); protected virtual Expression ValidateMemberInfo(TMemberInfo info) => null; diff --git a/src/Processors/Constructor/ConstructorProcessor.cs b/src/Processors/Constructor/ConstructorProcessor.cs index 1e114ef6..89f1d8d0 100644 --- a/src/Processors/Constructor/ConstructorProcessor.cs +++ b/src/Processors/Constructor/ConstructorProcessor.cs @@ -5,10 +5,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; using Unity.Builder; -using Unity.Builder.Expressions; using Unity.Container.Lifetime; -using Unity.Injection; -using Unity.Policy; namespace Unity.Processors { @@ -18,6 +15,29 @@ namespace Unity.Processors private static readonly ConstructorLengthComparer ConstructorComparer = new ConstructorLengthComparer(); + private static readonly ConstructorInfo PerResolveInfo = typeof(InternalPerResolveLifetimeManager) + .GetTypeInfo().DeclaredConstructors.First(); + + private static readonly ConstructorInfo InvalidOperationExceptionCtor = + typeof(InvalidOperationException) + .GetTypeInfo() + .DeclaredConstructors + .First(c => + { + var parameters = c.GetParameters(); + return 2 == parameters.Length && + typeof(string) == parameters[0].ParameterType && + typeof(Exception) == parameters[1].ParameterType; + }); + + private static readonly Expression NoConstructorExceptionExpr = + Expression.Throw( + Expression.New(InvalidOperationExceptionCtor, + Expression.Call(StringFormat, + Expression.Constant("No public constructor is available for type {0}."), + BuilderContextExpression.Type), + InvalidRegistrationExpression)); + #endregion @@ -97,31 +117,20 @@ namespace Unity.Processors } } - var expression = base.GetEnumerator(ref context) - .FirstOrDefault(); - switch (expression) - { - case NewExpression newExpression: - expression = Expression.Assign(context.Variable, newExpression); - break; + var newExpr = base.GetEnumerator(ref context) + .FirstOrDefault() ?? + NoConstructorExceptionExpr; - case null: - expression = Expression.Throw( - Expression.New(ExceptionExpression.InvalidOperationExceptionCtor, - Expression.Call(StringFormat, - Expression.Constant("No public constructor is available for type {0}."), - BuilderContextExpression.Type), - InvalidRegistrationExpression)); - break; - } - - var createExpr = Expression.IfThenElse(Expression.NotEqual(Expression.Constant(null), BuilderContextExpression.Existing), - Expression.Assign(context.Variable, Expression.Convert(BuilderContextExpression.Existing, context.Type)), - ValidateConstructedType(ref context) ?? expression); + var IfThenExpr = Expression.IfThen(Expression.Equal(Expression.Constant(null), BuilderContextExpression.Existing), + ValidateConstructedType(ref context) ?? newExpr); return context.Registration.Get(typeof(LifetimeManager)) is PerResolveLifetimeManager - ? new Expression[] { createExpr, BuilderContextExpression.SetPerBuildSingleton(ref context) } - : new Expression[] { createExpr }; + ? new[] { IfThenExpr, BuilderContextExpression.Set(context.RegistrationType, + context.RegistrationName, + typeof(LifetimeManager), + Expression.New(PerResolveInfo, + BuilderContextExpression.Existing)) } + : new Expression[] { IfThenExpr }; } protected override Expression ValidateMemberInfo(ConstructorInfo info) @@ -129,7 +138,7 @@ namespace Unity.Processors var parameters = info.GetParameters(); if (parameters.Any(pi => pi.ParameterType.IsByRef)) { - return Expression.Throw(Expression.New(ExceptionExpression.InvalidOperationExceptionCtor, + return Expression.Throw(Expression.New(InvalidOperationExceptionCtor, Expression.Constant(CreateErrorMessage(Constants.SelectedConstructorHasRefParameters, info.DeclaringType, info)), InvalidRegistrationExpression)); } @@ -148,8 +157,15 @@ namespace Unity.Processors } - protected override Expression CreateExpression(ConstructorInfo info, object[] resolvers, ParameterExpression variable) - => Expression.New(info, CreateParameterExpressions(info.GetParameters(), resolvers)); + protected override Expression CreateExpression(ConstructorInfo info, object[] resolvers) + { + var variable = Expression.Variable(info.DeclaringType); + return Expression.Block(new[] { variable }, new Expression[] + { + Expression.Assign(variable, Expression.New(info, CreateParameterExpressions(info.GetParameters(), resolvers))), + Expression.Assign(BuilderContextExpression.Existing, Expression.Convert(variable, typeof(object))) + }); + } #endregion @@ -166,7 +182,7 @@ namespace Unity.Processors #endif { return Expression.Throw( - Expression.New(ExceptionExpression.InvalidOperationExceptionCtor, + Expression.New(InvalidOperationExceptionCtor, Expression.Call( StringFormat, Expression.Constant(Constants.CannotConstructInterface), @@ -181,7 +197,7 @@ namespace Unity.Processors #endif { return Expression.Throw( - Expression.New(ExceptionExpression.InvalidOperationExceptionCtor, + Expression.New(InvalidOperationExceptionCtor, Expression.Call( StringFormat, Expression.Constant(Constants.CannotConstructAbstractClass), @@ -196,7 +212,7 @@ namespace Unity.Processors #endif { return Expression.Throw( - Expression.New(ExceptionExpression.InvalidOperationExceptionCtor, + Expression.New(InvalidOperationExceptionCtor, Expression.Call( StringFormat, Expression.Constant(Constants.CannotConstructDelegate), @@ -207,7 +223,7 @@ namespace Unity.Processors if (context.Type == typeof(string)) { return Expression.Throw( - Expression.New(ExceptionExpression.InvalidOperationExceptionCtor, + Expression.New(InvalidOperationExceptionCtor, Expression.Call( StringFormat, Expression.Constant(Constants.TypeIsNotConstructable), diff --git a/src/Processors/Fields/FieldsProcessor.cs b/src/Processors/Fields/FieldsProcessor.cs index 8dcdc2d1..3bffdf45 100644 --- a/src/Processors/Fields/FieldsProcessor.cs +++ b/src/Processors/Fields/FieldsProcessor.cs @@ -4,30 +4,51 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; using Unity.Builder; -using Unity.Builder.Expressions; namespace Unity.Processors { public class FieldsProcessor : MemberBuildProcessor { + #region Fields + + private static readonly MethodInfo ResolveField = + typeof(BuilderContext).GetTypeInfo() + .GetDeclaredMethods(nameof(BuilderContext.Resolve)) + .First(m => + { + var parameters = m.GetParameters(); + return 2 <= parameters.Length && + typeof(FieldInfo) == parameters[0].ParameterType; + }); + + #endregion + + #region Overrides public override IEnumerable Select(ref BuilderContext context) => base.Select(ref context).Distinct(); - #endregion - - - #region Implementation + protected override FieldInfo[] DeclaredMembers(Type type) + { + return base.DeclaredMembers(type); + } protected override Type MemberType(FieldInfo info) => info.FieldType; - protected override Expression ResolveExpression(FieldInfo info, string name, object resolver) - => BuilderContextExpression.Resolve(info, name, resolver); + protected override Expression ResolveExpression(FieldInfo field, string name, object resolver) + { + return Expression.Convert( + Expression.Call(BuilderContextExpression.Context, ResolveField, + Expression.Constant(field, typeof(FieldInfo)), + Expression.Constant(name, typeof(string)), + Expression.Constant(resolver, typeof(object))), + field.FieldType); + } - protected override MemberExpression CreateMemberExpression(ParameterExpression variable, FieldInfo info) - => Expression.Field(variable, info); + protected override MemberExpression CreateMemberExpression(FieldInfo info) + => Expression.Field(Expression.Convert(BuilderContextExpression.Existing, info.DeclaringType), info); #endregion diff --git a/src/Processors/Methods/MethodsProcessor.cs b/src/Processors/Methods/MethodsProcessor.cs index bde70f95..ae27a548 100644 --- a/src/Processors/Methods/MethodsProcessor.cs +++ b/src/Processors/Methods/MethodsProcessor.cs @@ -55,8 +55,9 @@ namespace Unity.Processors return null; } - protected override Expression CreateExpression(MethodInfo info, object[] resolvers, ParameterExpression variable) - => Expression.Call(variable, info, CreateParameterExpressions(info.GetParameters(), resolvers)); + protected override Expression CreateExpression(MethodInfo info, object[] resolvers) + => Expression.Call(Expression.Convert(BuilderContextExpression.Existing, info.DeclaringType), + info, CreateParameterExpressions(info.GetParameters(), resolvers)); #endregion } diff --git a/src/Processors/Properties/PropertiesProcessor.cs b/src/Processors/Properties/PropertiesProcessor.cs index dcf2aa9d..f6cf7bc6 100644 --- a/src/Processors/Properties/PropertiesProcessor.cs +++ b/src/Processors/Properties/PropertiesProcessor.cs @@ -4,13 +4,27 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; using Unity.Builder; -using Unity.Builder.Expressions; using Unity.Utility; namespace Unity.Processors { public class PropertiesProcessor : MemberBuildProcessor { + #region Fields + + public static readonly MethodInfo ResolveProperty = + typeof(BuilderContext).GetTypeInfo() + .GetDeclaredMethods(nameof(BuilderContext.Resolve)) + .First(m => + { + var parameters = m.GetParameters(); + return 2 <= parameters.Length && + typeof(PropertyInfo) == parameters[0].ParameterType; + }); + + #endregion + + #region Overrides public override IEnumerable Select(ref BuilderContext context) => @@ -41,21 +55,22 @@ namespace Unity.Processors #endif } - #endregion - - - #region Implementation - protected override Type MemberType(PropertyInfo info) => info.PropertyType; - protected override Expression ResolveExpression(PropertyInfo member, string name, object resolver) - => BuilderContextExpression.Resolve(member, name, resolver); + protected override Expression ResolveExpression(PropertyInfo property, string name, object resolver) + { + return Expression.Convert( + Expression.Call(BuilderContextExpression.Context, ResolveProperty, + Expression.Constant(property, typeof(PropertyInfo)), + Expression.Constant(name, typeof(string)), + Expression.Constant(resolver, typeof(object))), + property.PropertyType); + } - protected override MemberExpression CreateMemberExpression(ParameterExpression variable, PropertyInfo info) - => Expression.Property(variable, info); + protected override MemberExpression CreateMemberExpression(PropertyInfo info) + => Expression.Property(Expression.Convert(BuilderContextExpression.Existing, info.DeclaringType), info); #endregion - } } diff --git a/src/ResolverPolicy/FixedTypeResolvePolicy.cs b/src/ResolverPolicy/FixedTypeResolvePolicy.cs deleted file mode 100644 index 3ddbe909..00000000 --- a/src/ResolverPolicy/FixedTypeResolvePolicy.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using Unity.Builder; -using Unity.Policy; -using Unity.Resolution; - -namespace Unity.ResolverPolicy -{ - /// - /// An implementation of that - /// calls back into the build chain to build up the dependency, passing - /// a type given at compile time as its build key. - /// - public class FixedTypeResolvePolicy : IResolve - { - private readonly NamedTypeBuildKey _keyToBuild; - - /// - /// Create a new instance storing the given type. - /// - /// Type to resolve. - public FixedTypeResolvePolicy(Type typeToBuild) - { - _keyToBuild = new NamedTypeBuildKey(typeToBuild); - } - - #region IResolverPolicy Members - - /// - /// GetOrDefault the value for a dependency. - /// - /// Current build context. - /// The value for the dependency. - public object Resolve(ref TContext context) - where TContext : IResolveContext - { - return context.Resolve(_keyToBuild.Type, _keyToBuild.Name); - } - - #endregion - } -} diff --git a/src/ResolverPolicy/LiteralValueDependencyResolvePolicy.cs b/src/ResolverPolicy/LiteralValueDependencyResolvePolicy.cs deleted file mode 100644 index 7f598131..00000000 --- a/src/ResolverPolicy/LiteralValueDependencyResolvePolicy.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Unity.Policy; -using Unity.Resolution; - -namespace Unity.ResolverPolicy -{ - /// - /// A implementation that returns - /// the value set in the constructor. - /// - public class LiteralValueDependencyResolvePolicy : IResolve - { - private readonly object _dependencyValue; - - /// - /// Create a new instance of - /// which will return the given value when resolved. - /// - /// The value to return. - public LiteralValueDependencyResolvePolicy(object dependencyValue) - { - _dependencyValue = dependencyValue; - } - - /// - /// GetOrDefault the value for a dependency. - /// - /// Current build context. - /// The value for the dependency. - public object Resolve(ref TContext context) - where TContext : IResolveContext - { - return _dependencyValue; - } - } -} diff --git a/src/ResolverPolicy/NamedTypeDependencyResolvePolicy.cs b/src/ResolverPolicy/NamedTypeDependencyResolvePolicy.cs deleted file mode 100644 index 8911601e..00000000 --- a/src/ResolverPolicy/NamedTypeDependencyResolvePolicy.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using Unity.Policy; -using Unity.Resolution; - -namespace Unity.ResolverPolicy -{ - /// - /// An implementation of that stores a - /// type and name, and at resolution time puts them together into a - /// . - /// - public class NamedTypeDependencyResolvePolicy : IResolve - { - /// - /// Create an instance of - /// with the given type and name. - /// - /// The type. - /// The name (may be null). - public NamedTypeDependencyResolvePolicy(Type type, string name) - { - Type = type; - Name = name; - } - - /// - /// Resolve the value for a dependency. - /// - /// Current build context. - /// The value for the dependency. - public object Resolve(ref TContext context) - where TContext : IResolveContext - { - return context.Resolve(Type, Name); - } - - /// - /// The type that this resolver resolves. - /// - public Type Type { get; } - - /// - /// The name that this resolver resolves. - /// - public string Name { get; } - } -} diff --git a/src/ResolverPolicy/OptionalDependencyResolvePolicy.cs b/src/ResolverPolicy/OptionalDependencyResolvePolicy.cs deleted file mode 100644 index 79dfc656..00000000 --- a/src/ResolverPolicy/OptionalDependencyResolvePolicy.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Globalization; -using System.Reflection; -using Unity.Policy; -using Unity.Resolution; - -namespace Unity.ResolverPolicy -{ - /// - /// A that will attempt to - /// resolve a value, and return null if it cannot rather than throwing. - /// - public class OptionalDependencyResolvePolicy : IResolve - { - /// - /// Construct a new object - /// that will attempt to resolve the given name and type from the container. - /// - /// Type to resolve. Must be a reference type. - /// Name to resolve with. - public OptionalDependencyResolvePolicy(Type type, string name) - { - if ((type ?? throw new ArgumentNullException(nameof(type))).GetTypeInfo().IsValueType) - { - throw new ArgumentException( - string.Format(CultureInfo.CurrentCulture, - Constants.OptionalDependenciesMustBeReferenceTypes, - type.GetTypeInfo().Name)); - } - - DependencyType = type; - Name = name; - } - - /// - /// Construct a new object - /// that will attempt to resolve the given type from the container. - /// - /// Type to resolve. Must be a reference type. - public OptionalDependencyResolvePolicy(Type type) - : this(type, null) - { - } - - /// - /// Type this resolver will resolve. - /// - public Type DependencyType { get; } - - /// - /// Name this resolver will resolve. - /// - public string Name { get; } - - #region IResolverPolicy Members - - /// - /// GetOrDefault the value for a dependency. - /// - /// Current build context. - /// The value for the dependency. - public object Resolve(ref TContext context) - where TContext : IResolveContext - { - try - { - return context.Resolve(DependencyType, Name); - } - catch (Exception) - { - return null; - } - } - - #endregion - } -} diff --git a/src/UnityContainer.IUnityContainer.cs b/src/UnityContainer.IUnityContainer.cs index b72c7f9c..1b53ba00 100644 --- a/src/UnityContainer.IUnityContainer.cs +++ b/src/UnityContainer.IUnityContainer.cs @@ -167,7 +167,7 @@ namespace Unity ? containerRegistration.Type : type, }; - return registration.BuildChain.ExecuteThrowingPlan(ref context); + return ExecutePlan(registration.BuildChain, ref context); } #endregion @@ -197,7 +197,7 @@ namespace Unity ? containerRegistration.Type : type }; - return registration.BuildChain.ExecuteThrowingPlan(ref context); + return ExecutePlan(registration.BuildChain, ref context); } #endregion diff --git a/src/UnityContainer.Implementation.cs b/src/UnityContainer.Implementation.cs index 8d4389f7..75d8064f 100644 --- a/src/UnityContainer.Implementation.cs +++ b/src/UnityContainer.Implementation.cs @@ -340,6 +340,34 @@ namespace Unity return argType; } + private static object ExecutePlan(BuilderStrategy[] chain, ref BuilderContext context) + { + var i = -1; + + try + { + while (!context.BuildComplete && ++i < chain.Length) + { + chain[i].PreBuildUp(ref context); + } + + while (--i >= 0) + { + chain[i].PostBuildUp(ref context); + } + } + catch (Exception ex) + { + context.RequiresRecovery?.Recover(); + // TODO: 5.9.0 Add proper error message + throw new ResolutionFailedException(context.RegistrationType, + context.RegistrationName, + "", ex); + } + + return context.Existing; + } + #endregion diff --git a/src/UnityContainer.Resolution.cs b/src/UnityContainer.Resolution.cs index 472bca14..50ff7077 100644 --- a/src/UnityContainer.Resolution.cs +++ b/src/UnityContainer.Resolution.cs @@ -5,7 +5,6 @@ using System.Linq.Expressions; using System.Reflection; using System.Text; using Unity.Builder; -using Unity.Builder.Expressions; using Unity.Exceptions; using Unity.Policy; using Unity.Registration; @@ -164,8 +163,6 @@ namespace Unity private ResolveDelegate GetResolver(ref BuilderContext context) { - context.Variable = Expression.Variable(context.Type, "instance"); - var expressions = new List(); foreach (var processor in _processorsChain) @@ -174,11 +171,10 @@ namespace Unity expressions.Add(step); } - expressions.Add(Expression.Convert(context.Variable, typeof(object))); + expressions.Add(BuilderContextExpression.Existing); var lambda = Expression.Lambda>( - Expression.Block(new ParameterExpression[] { context.Variable }, expressions), - BuilderContextExpression.Context); + Expression.Block(expressions), BuilderContextExpression.Context); return lambda.Compile(); } @@ -194,7 +190,7 @@ namespace Unity var builder = new StringBuilder(); builder.AppendLine($"Resolution of the dependency failed for type = '{typeRequested}', name = '{FormatName(nameRequested)}'."); - builder.AppendLine($"Exception occurred while: {ExceptionReason(ref context)}."); + builder.AppendLine($"Exception occurred while: {Constants.NoOperationExceptionReason}."); builder.AppendLine($"Exception is: {innerException?.GetType().GetTypeInfo().Name ?? "ResolutionFailedException"} - {innerException?.Message}"); builder.AppendLine("-----------------------------------------------"); builder.AppendLine("At the time of the exception, the container was: "); @@ -227,33 +223,6 @@ namespace Unity return string.IsNullOrEmpty(name) ? "(none)" : name; } - private static string ExceptionReason(ref BuilderContext context) - { - //var deepestContext = context; - - //// Find deepest child - //while (deepestContext.ChildContext != null) - //{ - // deepestContext = deepestContext.ChildContext; - //} - - // Backtrack to last known operation - - return Constants.NoOperationExceptionReason; - } - - private static string OperationError(object operation) - { - switch (operation) - { - case ConstructorInfo ctor: - return $"Calling constructor {ctor}"; - - default: - return operation.ToString(); - } - } - #endregion } diff --git a/tests/Performance/Tests/ColdStart.cs b/tests/Performance/Tests/ColdStart.cs index 9399f7bd..6728bd34 100644 --- a/tests/Performance/Tests/ColdStart.cs +++ b/tests/Performance/Tests/ColdStart.cs @@ -20,25 +20,28 @@ namespace Runner.Tests _container.RegisterType(); _container.RegisterType(); _container.RegisterType("1"); - _container.RegisterType("2"); + _container.RegisterType("2", Invoke.Factory(c => new Service())); } - [Benchmark(Description = "Resolve")] + [Benchmark(Description = "Resolve ")] public object IUnityContainer() => _container.Resolve(typeof(IUnityContainer), null); [Benchmark(Description = "Resolve (unregistered)")] public object Unregistered() => _container.Resolve(typeof(object), null); - [Benchmark(Description = "Resolve (registered)")] + [Benchmark(Description = "Resolve (registered)")] public object Transient() => _container.Resolve(typeof(Poco), null); - [Benchmark(Description = "Resolve (registered)")] + [Benchmark(Description = "Resolve (registered)")] public object Mapping() => _container.Resolve(typeof(IService), null); - [Benchmark(Description = "Resolve (registered)")] + [Benchmark(Description = "Resolve (factory)")] + public object Factory() => _container.Resolve(typeof(IService), "2"); + + [Benchmark(Description = "Resolve (registered)")] public object Array() => _container.Resolve(typeof(IService[]), null); - [Benchmark(Description = "Resolve> (registered)")] + [Benchmark(Description = "Resolve> (registered)")] public object Enumerable() => _container.Resolve(typeof(IEnumerable), null); diff --git a/tests/Performance/Tests/PreBuilt.cs b/tests/Performance/Tests/PreBuilt.cs index 7f680454..a7d90d07 100644 --- a/tests/Performance/Tests/PreBuilt.cs +++ b/tests/Performance/Tests/PreBuilt.cs @@ -20,7 +20,7 @@ namespace Runner.Tests _container.RegisterType(); _container.RegisterType(); _container.RegisterType("1"); - _container.RegisterType("2"); + _container.RegisterType("2", Invoke.Factory(c => new Service())); _container.Resolve(); _container.Resolve(); @@ -41,6 +41,9 @@ namespace Runner.Tests [Benchmark(Description = "Resolve (pre-built)")] public object Mapping() => _container.Resolve(typeof(IService), null); + [Benchmark(Description = "Resolve (factory)")] + public object Factory() => _container.Resolve(typeof(IService), "2"); + [Benchmark(Description = "Resolve (pre-built)")] public object Array() => _container.Resolve(typeof(IService[]), null); diff --git a/tests/Unity.Tests/Injection/InjectionParameterValueFixture.cs b/tests/Unity.Tests/Injection/InjectionParameterValueFixture.cs index fbb6e780..842c2245 100644 --- a/tests/Unity.Tests/Injection/InjectionParameterValueFixture.cs +++ b/tests/Unity.Tests/Injection/InjectionParameterValueFixture.cs @@ -3,8 +3,6 @@ using System; using System.Collections.Generic; using System.Linq; using Unity.Injection; -using Unity.Policy; -using Unity.ResolverPolicy; using Unity.Tests.v5.Generics; using Unity.Tests.v5.TestSupport; diff --git a/tests/Unity.Tests/ResolvingArraysFixture.cs b/tests/Unity.Tests/ResolvingArraysFixture.cs index eb2cf104..ffb22cfb 100644 --- a/tests/Unity.Tests/ResolvingArraysFixture.cs +++ b/tests/Unity.Tests/ResolvingArraysFixture.cs @@ -5,7 +5,6 @@ using Unity.Builder; using Unity.Extension; using Unity.Injection; using Unity.Policy; -using Unity.ResolverPolicy; using Unity.Strategies; using Unity.Tests.v5.TestSupport;