예제 #1
0
        /// <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);
        }
예제 #2
0
 /// <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));
            }
        }
예제 #5
0
        /// <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))
                ));
 }
예제 #10
0
        /// <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))
                       ));
        }
예제 #13
0
        /// <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)));
        }
예제 #14
0
        /// <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));
        }
예제 #16
0
        /// <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));
        }
예제 #18
0
        /// <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));
        }
예제 #20
0
        /// <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;
            }
        }
예제 #22
0
        /// <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));
            }
        }
예제 #23
0
        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);
        }
예제 #24
0
        /// <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));
 }
예제 #26
0
 /// <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));
 }
예제 #28
0
        /// <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;
 }
예제 #30
0
 protected override Expression Build(IDirectTarget target, IExpressionCompileContext context, IExpressionCompiler compiler)
 {
     return(Expression.Call(
                Expression.Constant(target), Methods.IDirectTarget_GetValue_Method
                ));
 }