/// <summary> /// Builds an expression for the specified <see cref="ConstructorBinding" />. /// Called by <see cref="Build(ConstructorTarget, IExpressionCompileContext, IExpressionCompiler)" /> /// </summary> /// <param name="binding">The binding.</param> /// <param name="context">The context.</param> /// <param name="compiler">The compiler to be used to build the target.</param> /// <remarks>The returned expression will either be a NewExpression or a MemberInitExpression</remarks> protected virtual Expression Build(ConstructorBinding binding, IExpressionCompileContext context, IExpressionCompiler compiler) { var newExpr = Expression.New(binding.Constructor, binding.BoundArguments.Select( a => compiler.Build(a.Target, context.NewContext(a.Parameter.ParameterType)))); if (binding.MemberBindings.Length == 0) { return(newExpr); } else { ParameterExpression localVar = null; List <MemberAssignment> memberBindings = new List <MemberAssignment>(); List <Expression> adHocBindings = new List <Expression>(); foreach (var mb in binding.MemberBindings) { // as soon as we have one list binding (which we can actually implement // using the list binding expression) we need to capture the locally newed // object into a local variable and pass it to the function below if (mb is ListMemberBinding listBinding) { if (localVar == null) { localVar = Expression.Parameter(newExpr.Type, "toReturn"); } adHocBindings.Add(GenerateListBindingExpression(localVar, listBinding, context, compiler)); } else { memberBindings.Add(Expression.Bind(mb.Member, compiler.Build(mb.Target, context.NewContext(mb.MemberType)))); } } Expression toReturn = newExpr; if (memberBindings.Count != 0) { toReturn = Expression.MemberInit(newExpr, memberBindings); } if (adHocBindings.Count != 0) { List <Expression> blockCode = new List <Expression> { Expression.Assign(localVar, toReturn) }; blockCode.AddRange(adHocBindings); blockCode.Add(localVar); toReturn = Expression.Block(new[] { localVar }, blockCode); } return(toReturn); } }
/// <summary> /// Builds an expression for the given <paramref name="target"/>. /// </summary> /// <param name="target">The target whose expression is to be built.</param> /// <param name="context">The compilation context.</param> /// <param name="compiler">The expression compiler to be used to build any other expressions for targets /// which might be required by the <paramref name="target" />. Note that unlike on the interface, where this /// parameter is optional, this will always be provided</param> /// <exception cref="System.InvalidOperationException"></exception> protected override Expression Build(ResolvedTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { var staticTarget = target.Bind(context); Expression staticExpr; if (staticTarget != null) { staticExpr = compiler.Build(staticTarget, context.NewContext(target.DeclaredType)); if (staticExpr == null) { throw new InvalidOperationException(string.Format(ExceptionResources.TargetReturnedNullExpressionFormat, staticTarget.GetType(), context.TargetType)); } if (staticExpr.Type != target.DeclaredType) { staticExpr = Expression.Convert(staticExpr, target.DeclaredType); } } else { // this should generate a missing dependency exception if executed // or, might actually yield a result if registrations have been added // after the expression is compiled. staticExpr = Methods.CallResolveContext_Resolve_Strong_Method( context.ResolveContextParameterExpression, target.DeclaredType); } return(staticExpr); }
/// <summary> /// Builds an expression which represents an instance of <see cref="IEnumerable{T}"/> whose elements are created by the /// <see cref="EnumerableTarget.Targets"/> of the passed <paramref name="target"/>. /// </summary> /// <param name="target">The target for which an expression is to be built.</param> /// <param name="context">The current compilation context.</param> /// <param name="compiler">The compiler to use when building expressions for child targets.</param> /// <returns>An expression which can be compiled into a delegate that, when executed, will create an instance of the enumerable /// represented by <paramref name="target"/> /// </returns> /// <remarks> /// The compiler is capable of producing both lazy-loaded and eager-loaded enumerables, which can be controlled via /// target container options. /// /// ## Lazy vs Eager loading /// /// The option <see cref="Options.LazyEnumerables"/> is read from the <paramref name="context"/> for the /// <see cref="EnumerableTarget.ElementType"/> of the <paramref name="target"/>. If it is equivalent to <c>true</c> /// (the <see cref="Options.LazyEnumerables.Default"/>), then a lazily-loaded enumerable is constructed which will /// create new instances of each object in the enumerable each time it is enumerated. /// /// If the option is instead equivalent to <c>false</c>, then all instances will be created in advance, and an already-materialised /// enumerable is constructed.</remarks> protected override Expression Build(EnumerableTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { if (context.GetOption(target.ElementType, Options.LazyEnumerables.Default)) { var funcs = target.Targets.Select(t => compiler.BuildResolveLambdaStrong(t, context.NewContext(target.ElementType)).Compile()) .ToArray(); var lazyType = typeof(LazyEnumerable <>).MakeGenericType(target.ElementType); var ctor = lazyType.GetConstructor(new[] { typeof(Delegate[]) }); var lazy = ctor.Invoke(new object[] { funcs }); return(Expression.Call( Expression.Constant(lazy), "GetInstances", null, context.ResolveContextParameterExpression)); } else { List <Expression> all = new List <Expression>(); for (var f = 0; f < target.Targets.Length; f++) { all.Add(compiler.Build(target.Targets[f], context.NewContext(target.ElementType))); } return(Expression.New( typeof(EagerEnumerable <>).MakeGenericType(target.ElementType).GetConstructors()[0], Expression.NewArrayInit(target.ElementType, all))); } }
/// <summary> /// Builds the conversion expression represented by the <paramref name="target"/> /// </summary> /// <param name="target">The target whose expression is to be built.</param> /// <param name="context">The compilation context.</param> /// <param name="compiler">The expression compiler to be used to build any other expressions for targets /// which might be required by the <paramref name="target" />.</param> protected override Expression Build(VariantMatchTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { // Here, the target type is *key*, especially for singletons return(Expression.Convert(compiler.Build(target.Target, context.NewContext(target.RegisteredType, scopeBehaviourOverride: context.ScopeBehaviourOverride)), target.RequestedType)); }
/// <summary> /// Builds the conversion expression represented by the <paramref name="target"/> /// </summary> /// <param name="target">The target whose expression is to be built.</param> /// <param name="context">The compilation context.</param> /// <param name="compiler">The expression compiler to be used to build any other expressions for targets /// which might be required by the <paramref name="target" />.</param> protected override Expression Build(ChangeTypeTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { // build the inner target's expression; and wrap it in a conversion expression for the // target type of the ChangeTypeTarget. // note that if the compilation context was overriding the scoping behaviour before - then we pass that through, // because this target defaults to 'None' return(Expression.Convert(compiler.Build(target.InnerTarget, context.NewContext(target.InnerTarget.DeclaredType, scopeBehaviourOverride: context.ScopeBehaviourOverride)), target.DeclaredType)); }
/// <summary> /// Builds an expression from the specified target for the given <see cref="IExpressionCompileContext" /> /// </summary> /// <param name="target">The target whose expression is to be built.</param> /// <param name="context">The compilation context.</param> /// <param name="compiler">The expression compiler to be used to build any other expressions for targets /// which might be required by the <paramref name="target" />. Note that unlike on the interface, where this /// parameter is optional, this will always be provided</param> /// <exception cref="NotImplementedException"></exception> protected override Expression Build(DelegateTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { var bindings = ParameterBinding.BindWithRezolvedArguments(target.Factory.GetMethodInfo()); return(Expression.Invoke(Expression.Constant(target.Factory), bindings.Select(b => b.Parameter.ParameterType == typeof(ResolveContext) ? context.ResolveContextParameterExpression : compiler.Build(b.Target, context.NewContext(b.Parameter.ParameterType))))); }
/// <summary> /// Builds an expression for the given <paramref name="target"/>. /// </summary> /// <param name="target">The target whose expression is to be built.</param> /// <param name="context">The compilation context.</param> /// <param name="compiler">The expression compiler to be used to build any other expressions for targets /// which might be required by the <paramref name="target" />. Note that unlike on the interface, where this /// parameter is optional, this will always be provided</param> protected override Expression Build(ScopedTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { // all we need to do is force the inner target's scope behaviour to None - and this builder's // base code will ensure that the whole resulting expression is converted into an explicitly scoped one // note that this scope deactivation is only in place for this one target - if it has any child targets then // scoping behaviour for those returns to normal if compiled with a new context (which they always should be) return(compiler.Build(target.InnerTarget, context.NewContext(scopeBehaviourOverride: ScopeBehaviour.None))); }
/// <summary> /// Builds an expression for the passed <paramref name="target"/> /// </summary> /// <param name="target"></param> /// <param name="context"></param> /// <param name="compiler"></param> /// <returns></returns> protected override Expression Build(ProjectionTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { // functionally the same as the DecoratorTargetBuilder var newContext = context.NewContext(target.ImplementationType); newContext.Register(target.InputTarget, target.InputType ?? target.InputTarget.DeclaredType); // projection target acts as an anchor for the target it wraps - this allows a single registered // target which is either a singleton or scoped to be reused for multiple input targets. newContext.SetOption(new TargetIdentityOverride(target.Id), target.DeclaredType); return(compiler.Build(target.OutputTarget, newContext)); }
/// <summary> /// Similar to <see cref="BuildResolveLambda(IExpressionCompiler, ITarget, IExpressionCompileContext)"/>, except this builds a /// lambda whose type is a strongly-typed delegate instead of object - i.e. Func{ResolveContext, T} /// </summary> /// <param name="compiler"></param> /// <param name="target"></param> /// <param name="context"></param> /// <returns></returns> public static LambdaExpression BuildResolveLambdaStrong(this IExpressionCompiler compiler, ITarget target, IExpressionCompileContext context) { if (compiler == null) { throw new ArgumentNullException(nameof(compiler)); } if (target == null) { throw new ArgumentNullException(nameof(target)); } var expression = compiler.Build(target, context); return(compiler.BuildStrongFactoryLambda(expression, context)); }
/// <summary> /// This method is a shortcut for building a lambda expression directly from an <see cref="ITarget" />. /// It calls <see cref="IExpressionCompiler.Build(ITarget, IExpressionCompileContext)" /> and passes the result to /// <see cref="IExpressionCompiler.BuildObjectFactoryLambda(Expression, IExpressionCompileContext)" />, which should yield an /// optimised lambda expression for the expression produced from the target which can then be /// compiled and used as the factory for that target. /// </summary> /// <param name="compiler">The compiler.</param> /// <param name="target">The target.</param> /// <param name="context">The current compilation context.</param> public static Expression <Func <ResolveContext, object> > BuildResolveLambda(this IExpressionCompiler compiler, ITarget target, IExpressionCompileContext context) { if (compiler == null) { throw new ArgumentNullException(nameof(compiler)); } if (target == null) { throw new ArgumentNullException(nameof(target)); } var expression = compiler.Build(target, context); return(compiler.BuildObjectFactoryLambda(expression, context)); }
/// <summary> /// Creates a new compilation context, registers the target's <see cref="DecoratorTarget.DecoratedTarget"/> /// into it as the correct target for the <see cref="DecoratorTarget.DecoratedType"/>, and then builds the /// expression for the <see cref="DecoratorTarget.InnerTarget"/> (which is typically a constructor target). /// </summary> /// <param name="target">The target whose expression is to be built.</param> /// <param name="context">The compilation context.</param> /// <param name="compiler">The expression compiler to be used to build any other expressions for targets /// which might be required by the <paramref name="target" />. Note that unlike on the interface, where this /// parameter is optional, this will always be provided</param> protected override Expression Build(DecoratorTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { // need a new context for this, into which we can override the registration of the decorated type // to be the decorated target so that the decorator target will resolve that. // there's a potential hole here in that if another container resolves this same decorator after it // has been compiled, it might end up decorating itself - might need a test scenario for that. var newContext = context.NewContext(); // add the decorated target into the compile context under the type which the enclosing decorator // was registered against. If the inner target is bound to a type which correctly implements the decorator // pattern over the common decorated type, then the decorated instance should be resolved when constructor // arguments are resolved. newContext.Register(target.DecoratedTarget, target.DecoratedType); // TODO: Do the same target anchoring that SingletonTargetBuilder is doing. return(compiler.Build(target.InnerTarget, newContext)); }
/// <summary> /// Builds an expression which either represents creating an array or a list of objects using an /// enumerable of targets from the <paramref name="target"/>'s <see cref="ListTarget.Items"/>. /// /// The target's <see cref="ListTarget.AsArray"/> flag is used to determine which expression to build. /// </summary> /// <param name="target">The target whose expression is to be built.</param> /// <param name="context">The compilation context.</param> /// <param name="compiler">The expression compiler to be used to build any other expressions for targets /// which might be required by the <paramref name="target" />. Note that unlike on the interface, where this /// parameter is optional, this will always be provided</param> protected override Expression Build(ListTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { var items = new List <Expression>(); foreach (var itemTarget in target.Items) { items.Add(compiler.Build(itemTarget, context.NewContext(target.ElementType))); } var arrayExpr = Expression.NewArrayInit(target.ElementType, items); if (target.AsArray) { return(arrayExpr); } else { return(Expression.New(target.ListConstructor, arrayExpr)); } }
private Expression GenerateListBindingExpression(Expression targetObj, ListMemberBinding listBinding, IExpressionCompileContext context, IExpressionCompiler compiler) { var method = AddToCollection_Method.MakeGenericMethod(listBinding.ElementType); var enumType = typeof(IEnumerable <>).MakeGenericType(listBinding.ElementType); var enumerable = compiler.Build(listBinding.Target, context.NewContext(enumType)); var enumLocal = Expression.Parameter(enumType, "enumerable"); var enumAssign = Expression.Assign(enumLocal, enumerable); var addDelegateParam = Expression.Parameter(listBinding.ElementType, "item"); var callAddToCollection = Expression.Call(null, method, Expression.Lambda( Expression.Call( listBinding.Member is PropertyInfo prop ? Expression.Property(targetObj, prop) : Expression.Field(targetObj, (FieldInfo)listBinding.Member), listBinding.AddMethod, addDelegateParam), addDelegateParam), enumLocal); return(Expression.Block(new[] { enumLocal, addDelegateParam }, enumAssign, callAddToCollection)); }
/// <summary> /// Obtains the bound target for the <paramref name="target"/> passed (by calling /// <see cref="GenericConstructorTarget.Bind(ICompileContext)"/>, and passes it to the /// <paramref name="compiler"/> to have an expression built for it. /// </summary> /// <param name="target">The target whose expression is to be built.</param> /// <param name="context">The compilation context.</param> /// <param name="compiler">The expression compiler to be used to build any other expressions for targets /// which might be required by the <paramref name="target" />. Note that unlike on the interface, where this /// parameter is optional, this will always be provided</param> protected override Expression Build(GenericConstructorTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { // simply bind the generic target to the context, obtain the target that is produced // and then build it. return(compiler.Build(target.Bind(context), context)); }
/// <summary> /// Builds an expression for the given <paramref name="target"/>. /// </summary> /// <param name="target">The target whose expression is to be built.</param> /// <param name="context">The compilation context.</param> /// <param name="compiler">The expression compiler to be used to build any other expressions for targets /// which might be required by the <paramref name="target" />. Note that unlike on the interface, where this /// parameter is optional, this will always be provided</param> protected override Expression Build(UnscopedTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { // force scoping off for the inner target return(compiler.Build(target.Inner, context.NewContext(scopeBehaviourOverride: ScopeBehaviour.None))); }