private static Delegate CompileImpl(LambdaExpression expression, ICompiledDelegateCache cache, bool outliningEnabled, IConstantHoister hoister) { // // E.g. xs => xs.Bar(x => x > 0).Foo(x => x * 2) // var expr = expression; if (outliningEnabled) { // // E.g. xs => xs.Bar(delegate1).Foo(delegate2) where delegate1 and delegate2 are constants // expr = Outline(expression, cache, hoister); } var template = ExpressionTemplatizer.Templatize(expr, hoister ?? SimpleHoister.Instance); // // E.g. outline | template expressions // ---------+------------------------------------------------------------------------------------------ // Y | (a, b) => xs => xs.Bar(a).Foo(b) and recursively c => x => x > c and d => x => x * d // N | (c, d) => xs => xs.Bar(x => x > c).Foo(x => x * d) // var lambda = template.Template; var argument = template.Argument; Delegate res; if (lambda.Parameters.Count == 0) { // // No template parameters, hence no tuple packing required. // res = cache.GetOrAdd(expr); } else { // // E.g. without outlining: t => xs => xs.Bar(x => x > t.Item1).Foo(x => x * t.Item2) // var cachedDelegate = cache.GetOrAdd(lambda); // // E.g. new Tuple<int, int>(0, 2) // var tupleArgument = TupleEvaluator.Instance.Visit(argument); res = (Delegate)cachedDelegate.DynamicInvoke(new object[] { tupleArgument }); } return(res); }
/// <summary> /// Evaluates the specified expression tree. /// If the specified expression has unbound parameters, an exception will be thrown. /// </summary> /// <typeparam name="TResult">Type of the evaluation result.</typeparam> /// <param name="expression">Expression to evaluate.</param> /// <param name="cache">Compiled delegate cache.</param> /// <returns>Result of evaluating the expression tree.</returns> public static TResult Evaluate <TResult>(this Expression expression, ICompiledDelegateCache cache) { if (expression == null) { throw new ArgumentNullException(nameof(expression)); } if (cache == null) { throw new ArgumentNullException(nameof(cache)); } return(EvalImpl <TResult>(expression, cache)); }
/// <summary> /// Compiles the specified lambda expression by hoisting constants from the expression and consulting /// the cache for the resulting templatized lambda expression. This technique increases the likelihood /// for a cache hit. /// </summary> /// <param name="expression">Lambda expression to compile.</param> /// <param name="cache">Cache to hold the compiled delegate.</param> /// <param name="outliningEnabled">Specifies whether nested lambda expressions should be outlined into delegate constants by recursive compilation using the cache.</param> /// <returns>Compiled delegate for the specified lambda expression.</returns> public static Delegate Compile(this LambdaExpression expression, ICompiledDelegateCache cache, bool outliningEnabled) { if (expression == null) { throw new ArgumentNullException(nameof(expression)); } if (cache == null) { throw new ArgumentNullException(nameof(cache)); } return(CompileImpl(expression, cache, outliningEnabled, hoister: null)); }
/// <summary> /// Compiles the specified lambda expression by hoisting constants from the expression and consulting /// the cache for the resulting templatized lambda expression. This technique increases the likelihood /// for a cache hit. /// </summary> /// <typeparam name="T">Delegate type of the lambda expression.</typeparam> /// <param name="expression">Lambda expression to compile.</param> /// <param name="cache">Cache to hold the compiled delegate.</param> /// <param name="outliningEnabled">Specifies whether nested lambda expressions should be outlined into delegate constants by recursive compilation using the cache.</param> /// <returns>Compiled delegate for the specified lambda expression.</returns> public static T Compile <T>(this Expression <T> expression, ICompiledDelegateCache cache, bool outliningEnabled) { if (expression == null) { throw new ArgumentNullException(nameof(expression)); } if (cache == null) { throw new ArgumentNullException(nameof(cache)); } return((T)(object)CompileImpl(expression, cache, outliningEnabled, hoister: null)); }
/// <summary> /// Outlines nested lambda expressions for consideration in the constant hoisting step. Only non-quoted /// lambda expressions without a closure will be considered for hoisting and subsequent caching. /// </summary> /// <param name="lambda">Lambda expression to perform outlining of inner lambda expressions on.</param> /// <param name="cache">Cache to hold any recursively compiled delegates.</param> /// <param name="hoister">Constant hoister used to selectively hoist constants in the specified expression.</param> /// <returns>Original lambda expression with outlined inner lambda expressions substituted for constant delegate expressions.</returns> /// <remarks>Future extensions can provide for a means to control the outlining policy, e.g. to insert a thunk to defer recursive compilation.</remarks> private static LambdaExpression Outline(LambdaExpression lambda, ICompiledDelegateCache cache, IConstantHoister hoister) { var oldBody = lambda.Body; var newBody = ExpressionOutliner.Outline(oldBody, cache, hoister); if (ReferenceEquals(oldBody, newBody)) { return(lambda); } var result = Expression.Lambda(lambda.Type, newBody, lambda.Name, lambda.TailCall, lambda.Parameters); return(result); }
public void CachedLambdaCompiler_AllSortsOfCaches() { var cs = new ICompiledDelegateCache[] { VoidCompiledDelegateCache.Instance, new SimpleCompiledDelegateCache(), new LeastRecentlyUsedCompiledDelegateCache(4) }; var es = new LambdaExpression[] { #pragma warning disable IDE0004 // Remove Unnecessary Cast. (Only unnecessary on C# 10 or later.) (Expression <Func <int> >)(() => 42), (Expression <Func <int, int> >)(x => x), (Expression <Func <int, int> >)(x => x * 2), (Expression <Func <int, int> >)(y => y), (Expression <Func <int, int, int> >)((a, b) => a * 2 + b * 3), (Expression <Func <int> >)(() => 43), (Expression <Func <int, int> >)(x => x * 17), (Expression <Func <int, int, int> >)((a, b) => a * 7 + b * 4), (Expression <Func <int, int, int> >)((a, b) => a * 8 + b * 5), (Expression <Func <int, int> >)(x => 3 * x), (Expression <Func <int> >)(() => 44), (Expression <Func <int, int> >)(x => 1 + x), (Expression <Func <int, int> >)(x => 2 + x), #pragma warning restore IDE0004 }; var nums = new[] { 7, 12, 64, 49, 18 }; foreach (var e in es) { var d0 = e.Compile(); var args = nums.Take(e.Parameters.Count).Cast <object>().ToArray(); var r = d0.DynamicInvoke(args); foreach (var c in cs) { var d = e.Compile(c); var s = d.DynamicInvoke(args); Assert.AreEqual(s, r); } } }
private static TResult EvalImpl <TResult>(Expression expression, ICompiledDelegateCache cache) { // Adding the case for ExpressionType.Default turns out to be a bit tricky // when dealing with value types that don't have a default constructor (e.g. // the decimal type). See EmitDefault in ILGen. switch (expression.NodeType) { case ExpressionType.Constant: if (typeof(TResult).IsAssignableFrom(expression.Type)) { return((TResult)((ConstantExpression)expression).Value); } break; } var funcletized = FuncletizeImpl <TResult>(expression); return(cache != null?funcletized.Compile(cache)() : funcletized.Compile()()); }
public CheckpointingQueryEngine(Uri uri, IReactiveServiceResolver serviceResolver, IScheduler scheduler, IReactiveMetadata metadataRegistry, IKeyValueStore keyValueStore, IQuotedTypeConversionTargets quotedTypeConversionTargets, TraceSource traceSource, ICompiledDelegateCache delegateCache) : base(uri, serviceResolver, scheduler, metadataRegistry, keyValueStore, SerializationPolicy.Default, quotedTypeConversionTargets, traceSource, delegateCache) { _objectSpace = new PersistedObjectSpace(new SerializationFactory()); }
private static TResult EvalImpl <TResult>(Expression <Func <TResult> > expression, ICompiledDelegateCache cache) { var funcletized = FuncletizeImpl <TResult>(expression); return(cache != null?funcletized.Compile(cache)() : funcletized.Compile()()); }
public Visitor(ICompiledDelegateCache cache, IConstantHoister hoister) { _cache = cache; _hoister = hoister; }
/// <summary> /// Outlines nested lambda expressions in the specified expression. If the expression is a /// lambda expression itself, it will be considered for rewriting as well. Care should be /// taken when calling this method as not to trigger a stack overflow. /// </summary> /// <param name="expression">Expression to apply nested lambda expression outlining on.</param> /// <param name="cache">Cache to hold any recursively compiled delegates.</param> /// <param name="hoister">Constant hoister used to selectively hoist constants in the specified expression.</param> /// <returns>Original expression with outlining steps applied.</returns> public static Expression Outline(Expression expression, ICompiledDelegateCache cache, IConstantHoister hoister) => new Visitor(cache, hoister).Visit(expression);