/// <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> /// Equivalent to <see cref="BuildObjectFactoryLambda(Expression, IExpressionCompileContext)"/> except this produces a lambda whose delegate /// type is strongly typed for the type of object that's returned (instead of just being 'object'). /// </summary> /// <param name="expression">The expression.</param> /// <param name="context">The context.</param> /// <returns></returns> public LambdaExpression BuildStrongFactoryLambda(Expression expression, IExpressionCompileContext context) { return(Expression.Lambda( typeof(Func <,>).MakeGenericType(typeof(ResolveContext), expression.Type), expression, context.ResolveContextParameterExpression)); }
/// <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 specified target. /// </summary> /// <param name="target">The target.</param> /// <param name="context">The context.</param> /// <param name="compiler">Optional. The compiler that's requesting the expression; and which can be used /// to compile other targets within the target. If not provided, then the implementation attempts to locate /// the context compiler using the <see cref="ExpressionBuilderBase.GetContextCompiler(IExpressionCompileContext)"/> method, and will throw /// an <see cref="InvalidOperationException"/> if it cannot do so.</param> /// <exception cref="ArgumentNullException"><paramref name="target"/> is null or <paramref name="context"/> is null</exception> /// <exception cref="InvalidOperationException"><paramref name="compiler"/> is null and an IExpressionCompiler /// couldn't be resolved for the current context (via <see cref="ExpressionBuilderBase.GetContextCompiler(IExpressionCompileContext)"/></exception> Expression IExpressionBuilder <TTarget> .Build(TTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { // note that this is a copy of the code found in the non-generic ExpressionBuilderBase's explicit implementation // of IExpressionBuilder.Build because there's no simple way to share the implementation between two explicit // implementations of interface methods. // can't use the MustNotBeNull extn method because no class constraint on TTarget if (target == null) { throw new ArgumentNullException(nameof(target)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } if (compiler == null) { compiler = GetContextCompiler(context); if (compiler == null) { throw new InvalidOperationException("Unable to identify the IExpressionCompiler for the current context"); } } if (target is ITarget tTarget) { return(BuildCore(tTarget, context, compiler)); } else { throw new ArgumentException($"The target must implement ITarget as well as {typeof(TTarget)} - this object only supports {typeof(TTarget)}", nameof(target)); } }
/// <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)); }
private static void TrackCompilation(ITarget target, IExpressionCompileContext context) { Type theType = context.TargetType ?? target.DeclaredType; int? targetIdOverride = context.GetOption <Runtime.TargetIdentityOverride>(theType); _compiledTargets.GetOrAdd(targetIdOverride ?? target.Id, target); _compileCounts.AddOrUpdate(new TypeAndTargetId(theType, targetIdOverride ?? target.Id), 1, (k, i) => i + 1); }
/// <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> /// Implementation of <see cref="ExpressionBuilderBase{TTarget}.Build(TTarget, IExpressionCompileContext, IExpressionCompiler)"/> /// </summary> /// <param name="target">The instance whose <see cref="IInstanceProvider.GetInstance(ResolveContext)"/> method is to be called /// by the returned expression.</param> /// <param name="context">The compilation context</param> /// <param name="compiler">The compiler for which the expression is being built.</param> /// <returns>The created expression</returns> protected override Expression Build(IInstanceProvider target, IExpressionCompileContext context, IExpressionCompiler compiler) { return(Expression.Call( Expression.Constant(target), Methods.IInstanceProvider_GetInstance_Method, Methods.CallResolveContext_New_Type(context.ResolveContextParameterExpression, Expression.Constant(context.TargetType)) )); }
/// <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> /// Implementation of <see cref="ExpressionBuilderBase{TTarget}.Build(TTarget, IExpressionCompileContext, IExpressionCompiler)"/> /// </summary> /// <param name="target">The instance whose <see cref="IFactoryProvider.Factory"/> is to be invoked by the returned expression.</param> /// <param name="context">The compilation context</param> /// <param name="compiler">The compiler for which the expression is being built.</param> /// <returns>The created expression.</returns> protected override Expression Build(IFactoryProvider target, IExpressionCompileContext context, IExpressionCompiler compiler) { var factory = target.Factory; return(Expression.Invoke( Expression.Constant(factory, typeof(Func <ResolveContext, object>)), Methods.CallResolveContext_New_Type(context.ResolveContextParameterExpression, Expression.Constant(context.TargetType)) )); }
/// <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> /// Initializes a new instance of the <see cref="ExpressionCompileContext"/> class as a child of another. /// /// Note that the <see cref="ResolveContextParameterExpression"/> is always inherited from the source context to ensure /// consistency across all expressions being built during a particular compilation chain. /// </summary> /// <param name="sourceContext">The source context.</param> /// <param name="useParentSharedExpressions">If <c>true</c> then the <see cref="SharedExpressions"/> of the <paramref name="sourceContext"/> /// will be reused by this new context. If <c>false</c>, then this context will start with a new empty set of shared expressions.</param> /// <param name="scopeBehaviourOverride">Override the scope behaviour to be used for the target that is compiled with this context.</param> /// <param name="targetType">If not null, the type for which expressions are to be compiled. If null, then the /// <paramref name="sourceContext"/>'s <see cref="ICompileContext.TargetType"/> will be inherited.</param> /// <param name="scopePreferenceOverride">Allows you to override the scope preference for any target being compiled in this context - /// if not provided, then it is automatically inherited from the parent.</param> protected internal ExpressionCompileContext(IExpressionCompileContext sourceContext, Type targetType = null, bool useParentSharedExpressions = true, ScopeBehaviour?scopeBehaviourOverride = null, ScopePreference?scopePreferenceOverride = null) : base(sourceContext, targetType, scopeBehaviourOverride, scopePreferenceOverride) { this._sharedExpressions = useParentSharedExpressions ? null : new Dictionary <SharedExpressionKey, Expression>(); RegisterExpressionTargets(); }
/// <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> /// Resolves an expression builder that can build the given target for the given compile context. /// /// Or /// /// Returns null if no builder can be found. /// </summary> /// <param name="target">The target.</param> /// <param name="context">The context.</param> /// <remarks>The function builds a list of all the types in the hierarchy represented /// by the type of the <paramref name="target"/> and, for each of those types which are /// compatible with <see cref="ITarget"/>, it looks for an <see cref="IExpressionBuilder{TTarget}"/> /// which is specialised for that type. If no compatible builder is found, then it attempts /// to find a general purpose <see cref="IExpressionBuilder"/> which can build the type.</remarks> public virtual IExpressionBuilder ResolveBuilder(ITarget target, IExpressionCompileContext context) { if (target == null) { throw new ArgumentNullException(nameof(target)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } return(context.GetOption <IExpressionBuilder>(target.GetType())); }
/// <summary> /// Overrides the abstract <see cref="ExpressionBuilderBase.Build(ITarget, IExpressionCompileContext, IExpressionCompiler)" /> (and seals it from /// further overrides); checks that <paramref name="target" /> is an instance of <typeparamref name="TTarget" /> /// (throwing an <see cref="ArgumentException" /> if not) and then calls this class' <see cref="Build(TTarget, IExpressionCompileContext, IExpressionCompiler)" /> /// abstract function. /// </summary> /// <param name="target">The target for which an expression is to be built. Must be an instance of <typeparamref name="TTarget" />.</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.ArgumentException">target must be an instance of { typeof(TTarget) }</exception> /// <exception cref="ArgumentException">If the passed target is not an instance of <typeparamref name="TTarget" /></exception> protected sealed override Expression Build(ITarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { TTarget target2; try { target2 = (TTarget)target; } catch (InvalidCastException) { throw new ArgumentException($"target must be an instance of {typeof(TTarget)}", nameof(target)); } return(Build(target2, context, compiler)); }
/// <summary> /// Called to build an expression for the specified target for the given <see cref="IExpressionCompileContext" /> - implementation /// of the <see cref="IExpressionCompiler.Build(ITarget, IExpressionCompileContext)"/> method. /// </summary> /// <param name="target">The target for which an expression is to be built</param> /// <param name="context">The compilation context.</param> /// <exception cref="ArgumentException">If the compiler is unable to resolve an <see cref="IExpressionBuilder" /> from /// the <paramref name="context" /> for the <paramref name="target" /></exception> /// <remarks>This implementation first looks for an <see cref="IExpressionCompilationFilter"/> as an option from the /// <paramref name="context"/>. If one is obtained, its <see cref="IExpressionCompilationFilter.Intercept(ITarget, IExpressionCompileContext, IExpressionCompiler)"/> /// method is called and, if it returns a non-null <see cref="Expression"/>, then that expression is used. /// /// Otherwise, the normal behaviour is to attempt to resolve an <see cref="IExpressionBuilder{TTarget}" /> (with <c>TTarget"</c> /// equal to the runtime type of the <paramref name="target" />) or <see cref="IExpressionBuilder" /> whose /// <see cref="IExpressionBuilder.CanBuild(Type)" /> function returns <c>true</c> for the given target and context. /// If that lookup fails, then an <see cref="ArgumentException" /> is raised. If the lookup succeeds, then the builder's /// <see cref="IExpressionBuilder.Build(ITarget, IExpressionCompileContext, IExpressionCompiler)" /> function is called, and the expression it /// produces is returned.</remarks> public Expression Build(ITarget target, IExpressionCompileContext context) { if (target == null) { throw new ArgumentNullException(nameof(target)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } var builder = ResolveBuilder(target, context) ?? throw new ArgumentException($"Unable to find an IExpressionBuilder for the target {target}", nameof(target)); return(builder.Build(target, 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(ExpressionTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { // reasonably simple - get the underlying expression, push it through the ExpressionTranslator to perform any parameter augmentations // or conversion to other targets (like ResolvedTarget, CconstructorTarget etc) and then push the result through a // TargetExpressionRewriter to compile any newly created targets into their respective expressions and into the resulting // expression. var translator = new ExpressionTranslator(context); var translated = translator.Visit(target.ExpressionFactory != null ? target.ExpressionFactory(context) : target.Expression); // the translator does lots of things - including identifying common code constructs which have rich target equivalents - such as // the NewExpression being the same as the ConstructorTarget. When it creates a target in place of an expression, it wrap it // inside a TargetExpression - so these then have to be compiled again via the TargetExpressionRewriter. var targetRewriter = new TargetExpressionRewriter(compiler, context); return(targetRewriter.Visit(translated)); }
/// <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)); }
public Expression Intercept(ITarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { try { // first callback to return a non-null result wins if (_isIntercepting) { return(null); } _isIntercepting = true; return(_filters.Select(f => f.Intercept(target, context, compiler)).FirstOrDefault()); } finally { _isIntercepting = false; } }
/// <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 static Expression BuildFactoryBody(Expression expression, IExpressionCompileContext context) { if (expression == null) { throw new ArgumentNullException(nameof(expression)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } // strip unnecessary conversions expression = new RedundantConvertRewriter().Visit(expression); // if we have shared conditionals, then we want to try and reorder them; as the intention // of the use of shared expressions is to consolidate them into one. We do this on the boolean // expressions that might be used as tests for conditionals // Note that this is a tricky optimisation to understand - but the remarks section // of the ConditionalRewriter XML documentation contains a code-based example which should explain // what's going on. var sharedConditionalTests = context.SharedExpressions.Where(e => e.Type == typeof(bool)).ToArray(); if (sharedConditionalTests.Length != 0) { expression = new ConditionalRewriter(expression, sharedConditionalTests).Rewrite(); } // shared locals are local variables generated by targets that would normally be duplicated // if multiple targets of the same type are used in one compiled target. By sharing them, // they reduce the size of the stack required for any generated code, but in turn // the compiler is required to lift them out and add them to an all-encompassing BlockExpression // surrounding all the code - otherwise they won't be in scope. var sharedLocals = context.SharedExpressions.OfType <ParameterExpression>().ToArray(); if (sharedLocals.Length != 0) { expression = Expression.Block(expression.Type, sharedLocals, new BlockExpressionLocalsRewriter(sharedLocals).Visit(expression)); } return(expression); }
/// <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); }
/// <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> /// returns a ConstantExpression wrapped around the <see cref="ObjectTarget.Value"/> reference. /// </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(ObjectTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { return(Expression.Constant(target.Value, context.TargetType ?? target.DeclaredType)); }
/// <summary> /// Always returns a <see cref="ConstantExpression"/> which contains the <see cref="OptionalParameterTarget.Value"/>. /// </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(OptionalParameterTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { // note we ignore the context requested type return(Expression.Constant(target.Value, target.DeclaredType)); }
/// <summary> /// Takes the unoptimised expression built for a target and optimises it and turns it into a lambda expression ready to /// be compiled into a factory delegate. /// </summary> /// <param name="expression">The expression.</param> /// <param name="context">The context.</param> public virtual Expression <Func <ResolveContext, object> > BuildObjectFactoryLambda(Expression expression, IExpressionCompileContext context) { expression = BuildFactoryBody(expression, context); // value types must be boxed, and that requires an explicit convert expression if (expression.Type != typeof(object) && expression.Type.IsValueType) { expression = Expression.Convert(expression, typeof(object)); } return(Expression.Lambda <Func <ResolveContext, object> >(expression, context.ResolveContextParameterExpression)); }
public TargetExpressionRewriter(IExpressionCompiler compiler, IExpressionCompileContext context) { this._compiler = compiler; this._sourceCompileContext = context; }
protected override Expression Build(IDirectTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { return(Expression.Call( Expression.Constant(target), Methods.IDirectTarget_GetValue_Method )); }