New test.
[mono.git] / mcs / class / System.Web.Mvc2 / System.Web.Mvc / ExpressionUtil / FastTrack`2.cs
1 /* ****************************************************************************\r
2  *\r
3  * Copyright (c) Microsoft Corporation. All rights reserved.\r
4  *\r
5  * This software is subject to the Microsoft Public License (Ms-PL). \r
6  * A copy of the license can be found in the license.htm file included \r
7  * in this distribution.\r
8  *\r
9  * You must not remove this notice, or any other, from this software.\r
10  *\r
11  * ***************************************************************************/\r
12 \r
13 namespace System.Web.Mvc.ExpressionUtil {\r
14     using System;\r
15     using System.Linq.Expressions;\r
16     using System.Reflection;\r
17 \r
18     internal static class FastTrack<TModel, TValue> {\r
19 \r
20         private static Func<TModel, TValue> _identityFunc;\r
21 \r
22         private static readonly ConstMemberLookupCache _constMemberLookupCache = new ConstMemberLookupCache();\r
23         private static readonly ModelMemberLookupCache _modelMemberLookupCache = new ModelMemberLookupCache();\r
24 \r
25         public static Func<TModel, TValue> GetFunc(ParameterExpression modelParameter, Expression body) {\r
26             { // model => model\r
27                 if (modelParameter == body) {\r
28                     return GetIdentityFunc();\r
29                 }\r
30             }\r
31 \r
32             { // model => {const}\r
33                 ConstantExpression constantExpression = body as ConstantExpression;\r
34                 if (constantExpression != null) {\r
35                     TValue value = (TValue)constantExpression.Value;\r
36                     return _ => value;\r
37                 }\r
38             }\r
39 \r
40             {\r
41                 MemberExpression memberExpression = body as MemberExpression;\r
42                 if (memberExpression != null) {\r
43                     if (memberExpression.Expression == null) {\r
44                         // model => {static member}\r
45                         return GetModelMemberLookupFunc(memberExpression.Member, true /* isStatic */);\r
46                     }\r
47                     else if (memberExpression.Expression == modelParameter) {\r
48                         // model => model.Member\r
49                         return GetModelMemberLookupFunc(memberExpression.Member, false /* isStatic */);\r
50                     }\r
51                     else {\r
52                         ConstantExpression constantExpression = memberExpression.Expression as ConstantExpression;\r
53                         if (constantExpression != null) {\r
54                             // model => {const}.Member, e.g. captured local variable in a foreach\r
55                             return GetConstMemberLookupFunc(memberExpression.Member, constantExpression.Value);\r
56                         }\r
57                     }\r
58                 }\r
59             }\r
60 \r
61             // don't know how to fast-track\r
62             return null;\r
63         }\r
64 \r
65         private static Func<TModel, TValue> GetIdentityFunc() {\r
66             // don't need to worry about locking since all identity funcs are the same\r
67             if (_identityFunc == null) {\r
68                 ParameterExpression modelParameter = Expression.Parameter(typeof(TModel), "model");\r
69                 Expression<Func<TModel, TValue>> identityLambda = Expression.Lambda<Func<TModel, TValue>>(modelParameter, modelParameter);\r
70                 _identityFunc = identityLambda.Compile();\r
71             }\r
72 \r
73             return _identityFunc;\r
74         }\r
75 \r
76         private static Func<TModel, TValue> GetModelMemberLookupFunc(MemberInfo member, bool isStatic) {\r
77             return _modelMemberLookupCache.GetFunc(member, isStatic);\r
78         }\r
79 \r
80         private static Func<TModel, TValue> GetConstMemberLookupFunc(MemberInfo member, object constValue) {\r
81             Func<object, TValue> innerFunc = _constMemberLookupCache.GetFunc(member);\r
82             return _ => innerFunc(constValue);\r
83         }\r
84 \r
85         private sealed class ConstMemberLookupCache : ReaderWriterCache<MemberInfo, Func<object, TValue>> {\r
86             private static Func<object, TValue> CreateFunc(MemberInfo member) {\r
87                 ParameterExpression constParam = Expression.Parameter(typeof(object), "constValue");\r
88                 // cast is necessary since the constant value comes in as a standard 'object'\r
89                 UnaryExpression castExpr = Expression.Convert(constParam, member.DeclaringType);\r
90                 MemberExpression memberExpr = Expression.MakeMemberAccess(castExpr, member);\r
91                 Expression<Func<object, TValue>> lambda = Expression.Lambda<Func<object, TValue>>(memberExpr, constParam);\r
92                 return lambda.Compile();\r
93             }\r
94             public Func<object, TValue> GetFunc(MemberInfo member) {\r
95                 return FetchOrCreateItem(member, () => CreateFunc(member));\r
96             }\r
97         }\r
98 \r
99         private sealed class ModelMemberLookupCache : ReaderWriterCache<MemberInfo, Func<TModel, TValue>> {\r
100             private static Func<TModel, TValue> CreateFunc(MemberInfo member, bool isStatic) {\r
101                 ParameterExpression modelParam = Expression.Parameter(typeof(TModel), "model");\r
102                 MemberExpression memberExpr = Expression.MakeMemberAccess((!isStatic) ? modelParam : null, member);\r
103                 Expression<Func<TModel, TValue>> lambda = Expression.Lambda<Func<TModel, TValue>>(memberExpr, modelParam);\r
104                 return lambda.Compile();\r
105             }\r
106             public Func<TModel, TValue> GetFunc(MemberInfo member, bool isStatic) {\r
107                 return FetchOrCreateItem(member, () => CreateFunc(member, isStatic));\r
108             }\r
109         }\r
110 \r
111     }\r
112 }\r