/* **************************************************************************** * * Copyright (c) Microsoft Corporation. All rights reserved. * * This software is subject to the Microsoft Public License (Ms-PL). * A copy of the license can be found in the license.htm file included * in this distribution. * * You must not remove this notice, or any other, from this software. * * ***************************************************************************/ namespace System.Web.Mvc.ExpressionUtil { using System; using System.Linq.Expressions; internal static class CachedExpressionCompiler { // This is the entry point to the cached expression tree compiler. The processor will perform a series of checks // and optimizations in order to return a fully-compiled func as quickly as possible to the caller. If the // input expression is particularly obscure, the system will fall back to a slow but correct compilation step. public static Func Process(Expression> lambdaExpression) { return Processor.GetFunc(lambdaExpression); } private static class Processor { private static readonly Cache _cache = new Cache(); public static Func GetFunc(Expression> lambdaExpression) { // look for common patterns that don't need to be fingerprinted Func func = GetFuncFastTrack(lambdaExpression); if (func != null) { return func; } // not a common pattern, so try fingerprinting (slower, but cached) func = GetFuncFingerprinted(lambdaExpression); if (func != null) { return func; } // pattern not recognized by fingerprinting routine, so compile directly (slowest) return GetFuncSlow(lambdaExpression); } private static Func GetFuncFastTrack(Expression> lambdaExpression) { ParameterExpression modelParameter = lambdaExpression.Parameters[0]; Expression body = lambdaExpression.Body; return FastTrack.GetFunc(modelParameter, body); } private static Func GetFuncFingerprinted(Expression> lambdaExpression) { ParserContext context = ExpressionParser.Parse(lambdaExpression); if (context.Fingerprint == null) { // fingerprinting failed return null; } object[] hoistedValues = context.HoistedValues.ToArray(); var del = _cache.GetDelegate(context); return model => del(model, hoistedValues); } private static Func GetFuncSlow(Expression> lambdaExpression) { Func del = lambdaExpression.Compile(); return del; } private sealed class Cache : ReaderWriterCache> { private static CompiledExpressionDelegate CreateDelegate(ParserContext context) { var bodyExpr = context.Fingerprint.ToExpression(context); var lambdaExpr = Expression.Lambda>(bodyExpr, context.ModelParameter, ParserContext.HoistedValuesParameter); var del = lambdaExpr.Compile(); return del; } public CompiledExpressionDelegate GetDelegate(ParserContext context) { return FetchOrCreateItem(context.Fingerprint, () => CreateDelegate(context)); } } } } }