PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/System.Web.Mvc3/Mvc/ExpressionUtil/CachedExpressionCompiler.cs

https://bitbucket.org/danipen/mono
C# | 126 lines | 81 code | 25 blank | 20 comment | 16 complexity | 34eff53a2e08530b7007916de86d6694 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. namespace System.Web.Mvc.ExpressionUtil {
  2. using System;
  3. using System.Collections.Concurrent;
  4. using System.Collections.Generic;
  5. using System.Linq.Expressions;
  6. using System.Reflection;
  7. internal static class CachedExpressionCompiler {
  8. // This is the entry point to the cached expression compilation system. The system
  9. // will try to turn the expression into an actual delegate as quickly as possible,
  10. // relying on cache lookups and other techniques to save time if appropriate.
  11. // If the provided expression is particularly obscure and the system doesn't know
  12. // how to handle it, we'll just compile the expression as normal.
  13. public static Func<TModel, TValue> Process<TModel, TValue>(Expression<Func<TModel, TValue>> lambdaExpression) {
  14. return Compiler<TModel, TValue>.Compile(lambdaExpression);
  15. }
  16. private static class Compiler<TIn, TOut> {
  17. private static Func<TIn, TOut> _identityFunc;
  18. private static readonly ConcurrentDictionary<MemberInfo, Func<TIn, TOut>> _simpleMemberAccessDict =
  19. new ConcurrentDictionary<MemberInfo, Func<TIn, TOut>>();
  20. private static readonly ConcurrentDictionary<MemberInfo, Func<object, TOut>> _constMemberAccessDict =
  21. new ConcurrentDictionary<MemberInfo, Func<object, TOut>>();
  22. private static readonly ConcurrentDictionary<ExpressionFingerprintChain, Hoisted<TIn, TOut>> _fingerprintedCache =
  23. new ConcurrentDictionary<ExpressionFingerprintChain, Hoisted<TIn, TOut>>();
  24. public static Func<TIn, TOut> Compile(Expression<Func<TIn, TOut>> expr) {
  25. return CompileFromIdentityFunc(expr)
  26. ?? CompileFromConstLookup(expr)
  27. ?? CompileFromMemberAccess(expr)
  28. ?? CompileFromFingerprint(expr)
  29. ?? CompileSlow(expr);
  30. }
  31. private static Func<TIn, TOut> CompileFromConstLookup(Expression<Func<TIn, TOut>> expr) {
  32. ConstantExpression constExpr = expr.Body as ConstantExpression;
  33. if (constExpr != null) {
  34. // model => {const}
  35. TOut constantValue = (TOut)constExpr.Value;
  36. return _ => constantValue;
  37. }
  38. return null;
  39. }
  40. private static Func<TIn, TOut> CompileFromIdentityFunc(Expression<Func<TIn, TOut>> expr) {
  41. if (expr.Body == expr.Parameters[0]) {
  42. // model => model
  43. // don't need to lock, as all identity funcs are identical
  44. if (_identityFunc == null) {
  45. _identityFunc = expr.Compile();
  46. }
  47. return _identityFunc;
  48. }
  49. return null;
  50. }
  51. private static Func<TIn, TOut> CompileFromFingerprint(Expression<Func<TIn, TOut>> expr) {
  52. List<object> capturedConstants;
  53. ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);
  54. if (fingerprint != null) {
  55. var del = _fingerprintedCache.GetOrAdd(fingerprint, _ => {
  56. // Fingerprinting succeeded, but there was a cache miss. Rewrite the expression
  57. // and add the rewritten expression to the cache.
  58. var hoistedExpr = HoistingExpressionVisitor<TIn, TOut>.Hoist(expr);
  59. return hoistedExpr.Compile();
  60. });
  61. return model => del(model, capturedConstants);
  62. }
  63. // couldn't be fingerprinted
  64. return null;
  65. }
  66. private static Func<TIn, TOut> CompileFromMemberAccess(Expression<Func<TIn, TOut>> expr) {
  67. // Performance tests show that on the x64 platform, special-casing static member and
  68. // captured local variable accesses is faster than letting the fingerprinting system
  69. // handle them. On the x86 platform, the fingerprinting system is faster, but only
  70. // by around one microsecond, so it's not worth it to complicate the logic here with
  71. // an architecture check.
  72. MemberExpression memberExpr = expr.Body as MemberExpression;
  73. if (memberExpr != null) {
  74. if (memberExpr.Expression == expr.Parameters[0] || memberExpr.Expression == null) {
  75. // model => model.Member or model => StaticMember
  76. return _simpleMemberAccessDict.GetOrAdd(memberExpr.Member, _ => expr.Compile());
  77. }
  78. ConstantExpression constExpr = memberExpr.Expression as ConstantExpression;
  79. if (constExpr != null) {
  80. // model => {const}.Member (captured local variable)
  81. var del = _constMemberAccessDict.GetOrAdd(memberExpr.Member, _ => {
  82. // rewrite as capturedLocal => ((TDeclaringType)capturedLocal).Member
  83. var constParamExpr = Expression.Parameter(typeof(object), "capturedLocal");
  84. var constCastExpr = Expression.Convert(constParamExpr, memberExpr.Member.DeclaringType);
  85. var newMemberAccessExpr = memberExpr.Update(constCastExpr);
  86. var newLambdaExpr = Expression.Lambda<Func<object, TOut>>(newMemberAccessExpr, constParamExpr);
  87. return newLambdaExpr.Compile();
  88. });
  89. object capturedLocal = constExpr.Value;
  90. return _ => del(capturedLocal);
  91. }
  92. }
  93. return null;
  94. }
  95. private static Func<TIn, TOut> CompileSlow(Expression<Func<TIn, TOut>> expr) {
  96. // fallback compilation system - just compile the expression directly
  97. return expr.Compile();
  98. }
  99. }
  100. }
  101. }