File: ExpressionFingerprint.cs

package info (click to toggle)
mono 2.6.7-5.1
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 327,344 kB
  • ctags: 413,649
  • sloc: cs: 2,471,883; xml: 1,768,594; ansic: 350,665; sh: 13,644; makefile: 8,640; perl: 1,784; asm: 717; cpp: 209; python: 146; sql: 81; sed: 16
file content (174 lines) | stat: -rw-r--r-- 7,276 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/* ****************************************************************************
 *
 * 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.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Linq.Expressions;

    // Expression fingerprint class
    // Contains information used for generalizing, comparing, and recreating Expression instances
    //
    // Since Expression objects are immutable and are recreated for every invocation of an expression
    // helper method, they can't be compared directly. Fingerprinting Expression objects allows
    // information about them to be abstracted away, and the fingerprints can be directly compared.
    // Consider the process of fingerprinting that all values (parameters, constants, etc.) are hoisted
    // and replaced with dummies. What remains can be decomposed into a sequence of operations on specific
    // types and specific inputs.
    //
    // Some sample fingerprints:
    //
    // 2 + 4 -> OP_ADD(CONST:int, CONST:int):int
    // 2 + 8 -> OP_ADD(CONST:int, CONST:int):int
    // 2.0 + 4.0 -> OP_ADD(CONST:double, CONST:double):double
    //
    // 2 + 4 and 2 + 8 have the same fingerprint, but 2.0 + 4.0 has a different fingerprint since its
    // underlying types differ.
    //
    // "Hello " + "world" -> OP_ADD(CONST:string, CONST:string):string
    // "Hello " + {model} -> OP_ADD(CONST:string, PARAM:string):string
    //
    // These string concatenations have different fingerprints since the inputs are provided differently:
    // one is a hoisted local, the other is a parameter.
    //
    // ({model} ?? "sample").Length -> MEMBER_ACCESS(String.Length, OP_COALESCE(PARAM:string, CONST:string):string):int
    // ({model} ?? "other sample").Length -> MEMBER_ACCESS(String.Length, OP_COALESCE(PARAM:string, CONST:string):string):int
    //
    // These expressions have the same fingerprint.
    internal abstract class ExpressionFingerprint {

        protected ExpressionFingerprint(Expression expression) {
            // since the fingerprints are cached potentially forever, don't keep a reference
            // to the original expression

            NodeType = expression.NodeType;
            Type = expression.Type;
        }

        // the type of expression node, e.g. OP_ADD, MEMBER_ACCESS, etc.
        public ExpressionType NodeType {
            get;
            private set;
        }

        // the CLR type resulting from this expression, e.g. int, string, etc.
        public Type Type {
            get;
            private set;
        }

        internal virtual void AddToHashCodeCombiner(HashCodeCombiner combiner) {
            combiner.AddObject(NodeType);
            combiner.AddObject(Type);
        }

        public static ExpressionFingerprint Create(Expression expression, ParserContext parserContext) {
            {
                BinaryExpression binaryExpression = expression as BinaryExpression;
                if (binaryExpression != null) {
                    return BinaryExpressionFingerprint.Create(binaryExpression, parserContext);
                }
            }

            {
                ConditionalExpression conditionalExpression = expression as ConditionalExpression;
                if (conditionalExpression != null) {
                    return ConditionalExpressionFingerprint.Create(conditionalExpression, parserContext);
                }
            }

            {
                ConstantExpression constantExpression = expression as ConstantExpression;
                if (constantExpression != null) {
                    return ConstantExpressionFingerprint.Create(constantExpression, parserContext);
                }
            }

            {
                MemberExpression memberExpression = expression as MemberExpression;
                if (memberExpression != null) {
                    return MemberExpressionFingerprint.Create(memberExpression, parserContext);
                }
            }

            {
                MethodCallExpression methodCallExpression = expression as MethodCallExpression;
                if (methodCallExpression != null) {
                    return MethodCallExpressionFingerprint.Create(methodCallExpression, parserContext);
                }
            }

            {
                ParameterExpression parameterExpression = expression as ParameterExpression;
                if (parameterExpression != null) {
                    return ParameterExpressionFingerprint.Create(parameterExpression, parserContext);
                }
            }

            {
                UnaryExpression unaryExpression = expression as UnaryExpression;
                if (unaryExpression != null) {
                    return UnaryExpressionFingerprint.Create(unaryExpression, parserContext);
                }
            }

            // unknown expression
            return null;
        }

        public static ReadOnlyCollection<ExpressionFingerprint> Create(IEnumerable<Expression> expressions, ParserContext parserContext) {
            List<ExpressionFingerprint> fingerprints = new List<ExpressionFingerprint>();
            foreach (Expression expression in expressions) {
                ExpressionFingerprint fingerprint = Create(expression, parserContext);
                if (fingerprint == null && expression != null) {
                    // something couldn't be parsed properly
                    return null;
                }
                else {
                    fingerprints.Add(fingerprint);
                }
            }
            return new ReadOnlyCollection<ExpressionFingerprint>(fingerprints);
        }

        public override int GetHashCode() {
            HashCodeCombiner combiner = new HashCodeCombiner();
            combiner.AddObject(GetType());
            AddToHashCodeCombiner(combiner);
            return combiner.CombinedHash;
        }

        public override bool Equals(object obj) {
            ExpressionFingerprint other = obj as ExpressionFingerprint;
            if (other == null) {
                return false;
            }

            return (this.NodeType == other.NodeType
                && this.Type == other.Type
                && this.GetType() == other.GetType());
        }

        protected static Expression ToExpression(ExpressionFingerprint fingerprint, ParserContext parserContext) {
            return (fingerprint != null) ? fingerprint.ToExpression(parserContext) : null;
        }

        protected static IEnumerable<Expression> ToExpression(IEnumerable<ExpressionFingerprint> fingerprints, ParserContext parserContext) {
            return from fingerprint in fingerprints select ToExpression(fingerprint, parserContext);
        }

        public abstract Expression ToExpression(ParserContext parserContext);

    }
}