namespace System.Web.Mvc.ExpressionUtil { using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; internal static class CachedExpressionCompiler { // This is the entry point to the cached expression compilation system. The system // will try to turn the expression into an actual delegate as quickly as possible, // relying on cache lookups and other techniques to save time if appropriate. // If the provided expression is particularly obscure and the system doesn't know // how to handle it, we'll just compile the expression as normal. public static Func Process(Expression> lambdaExpression) { return Compiler.Compile(lambdaExpression); } private static class Compiler { private static Func _identityFunc; private static readonly ConcurrentDictionary> _simpleMemberAccessDict = new ConcurrentDictionary>(); private static readonly ConcurrentDictionary> _constMemberAccessDict = new ConcurrentDictionary>(); private static readonly ConcurrentDictionary> _fingerprintedCache = new ConcurrentDictionary>(); public static Func Compile(Expression> expr) { return CompileFromIdentityFunc(expr) ?? CompileFromConstLookup(expr) ?? CompileFromMemberAccess(expr) ?? CompileFromFingerprint(expr) ?? CompileSlow(expr); } private static Func CompileFromConstLookup(Expression> expr) { ConstantExpression constExpr = expr.Body as ConstantExpression; if (constExpr != null) { // model => {const} TOut constantValue = (TOut)constExpr.Value; return _ => constantValue; } return null; } private static Func CompileFromIdentityFunc(Expression> expr) { if (expr.Body == expr.Parameters[0]) { // model => model // don't need to lock, as all identity funcs are identical if (_identityFunc == null) { _identityFunc = expr.Compile(); } return _identityFunc; } return null; } private static Func CompileFromFingerprint(Expression> expr) { List capturedConstants; ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants); if (fingerprint != null) { var del = _fingerprintedCache.GetOrAdd(fingerprint, _ => { // Fingerprinting succeeded, but there was a cache miss. Rewrite the expression // and add the rewritten expression to the cache. var hoistedExpr = HoistingExpressionVisitor.Hoist(expr); return hoistedExpr.Compile(); }); return model => del(model, capturedConstants); } // couldn't be fingerprinted return null; } private static Func CompileFromMemberAccess(Expression> expr) { // Performance tests show that on the x64 platform, special-casing static member and // captured local variable accesses is faster than letting the fingerprinting system // handle them. On the x86 platform, the fingerprinting system is faster, but only // by around one microsecond, so it's not worth it to complicate the logic here with // an architecture check. MemberExpression memberExpr = expr.Body as MemberExpression; if (memberExpr != null) { if (memberExpr.Expression == expr.Parameters[0] || memberExpr.Expression == null) { // model => model.Member or model => StaticMember return _simpleMemberAccessDict.GetOrAdd(memberExpr.Member, _ => expr.Compile()); } ConstantExpression constExpr = memberExpr.Expression as ConstantExpression; if (constExpr != null) { // model => {const}.Member (captured local variable) var del = _constMemberAccessDict.GetOrAdd(memberExpr.Member, _ => { // rewrite as capturedLocal => ((TDeclaringType)capturedLocal).Member var constParamExpr = Expression.Parameter(typeof(object), "capturedLocal"); var constCastExpr = Expression.Convert(constParamExpr, memberExpr.Member.DeclaringType); var newMemberAccessExpr = memberExpr.Update(constCastExpr); var newLambdaExpr = Expression.Lambda>(newMemberAccessExpr, constParamExpr); return newLambdaExpr.Compile(); }); object capturedLocal = constExpr.Value; return _ => del(capturedLocal); } } return null; } private static Func CompileSlow(Expression> expr) { // fallback compilation system - just compile the expression directly return expr.Compile(); } } } }