/// <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 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 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(SingletonTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { var holder = context.ResolveContext.Resolve <SingletonTarget.SingletonContainer>(); int? targetIdOverride = context.GetOption <TargetIdentityOverride>(context.TargetType ?? target.DeclaredType); TypeAndTargetId id = new TypeAndTargetId(context.TargetType ?? target.DeclaredType, targetIdOverride ?? target.Id); var lazy = holder.GetLazy(id); if (lazy == null) { lazy = holder.GetLazy( target, id, compiler.CompileTargetStrong( target.InnerTarget, context.NewContext( context.TargetType ?? target.DeclaredType, // this override is important - when forcing into the root-scope, as we do // for singletons, 'explicit' means absolutely nothing. So, instead of allowing // our child target to choose, we explicitly ensure that all instances are implicitly // tracked within the root scope, if it is one which can track instances. scopeBehaviourOverride: ScopeBehaviour.Implicit, scopePreferenceOverride: ScopePreference.Root)), context); } return(Expression.Call( Expression.Constant(lazy), lazy.GetType().GetMethod("Resolve"), context.ResolveContextParameterExpression)); }
/// <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 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> 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> /// 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)); } }
/// <summary> /// Builds the expression for the passed <paramref name="target"/> /// </summary> /// <param name="target">The target for which an expression is to be built</param> /// <param name="context">The compilation context</param> /// <param name="compiler">The compiler</param> /// <returns>An expression.</returns> protected override Expression Build(AutoFactoryTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { var(returnType, parameterTypes) = TypeHelpers.DecomposeDelegateType(context.TargetType); var compileReturnType = target.ReturnType.ContainsGenericParameters ? returnType : target.ReturnType; var newContext = context.NewContext(compileReturnType); ParameterExpression[] parameters = new ParameterExpression[0]; // if there are parameters, we have to replace any Resolve calls for the parameter types in // the inner expression with parameter expressions fed from the outer lambda if (target.ParameterTypes.Length != 0) { parameters = target.ParameterTypes.Select((pt, i) => Expression.Parameter(pt, $"p{i}")).ToArray(); foreach (var parameter in parameters) { context.RegisterExpression(parameter, parameter.Type, ScopeBehaviour.None); } } var baseExpression = compiler.BuildResolveLambda(target.Bind(newContext), newContext); var lambda = Expression.Lambda(context.TargetType, Expression.Convert(Expression.Invoke(baseExpression, context.ResolveContextParameterExpression), compileReturnType), parameters); return(lambda); }
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> /// 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))); }