1 namespace System.Web.Mvc.ExpressionUtil {
3 using System.Collections.Generic;
5 // Expression fingerprint chain class
6 // Contains information used for generalizing, comparing, and recreating Expression instances
8 // Since Expression objects are immutable and are recreated for every invocation of an expression
9 // helper method, they can't be compared directly. Fingerprinting Expression objects allows
10 // information about them to be abstracted away, and the fingerprints can be directly compared.
11 // Consider the process of fingerprinting that all values (parameters, constants, etc.) are hoisted
12 // and replaced with dummies. What remains can be decomposed into a sequence of operations on specific
13 // types and specific inputs.
15 // Some sample fingerprints chains:
17 // 2 + 4 -> OP_ADD, CONST:int, NULL, CONST:int
18 // 2 + 8 -> OP_ADD, CONST:int, NULL, CONST:int
19 // 2.0 + 4.0 -> OP_ADD, CONST:double, NULL, CONST:double
21 // 2 + 4 and 2 + 8 have the same fingerprint, but 2.0 + 4.0 has a different fingerprint since its
22 // underlying types differ. Note that this looks a bit like prefix notation and is a side effect
23 // of how the ExpressionVisitor class recurses into expressions. (Occasionally there will be a NULL
24 // in the fingerprint chain, which depending on context can denote a static member, a null Conversion
25 // in a BinaryExpression, and so forth.)
27 // "Hello " + "world" -> OP_ADD, CONST:string, NULL, CONST:string
28 // "Hello " + {model} -> OP_ADD, CONST:string, NULL, PARAM_0:string
30 // These string concatenations have different fingerprints since the inputs are provided differently:
31 // one is a constant, the other is a parameter.
33 // ({model} ?? "sample").Length -> MEMBER_ACCESS(String.Length), OP_COALESCE, PARAM_0:string, NULL, CONST:string
34 // ({model} ?? "other sample").Length -> MEMBER_ACCESS(String.Length), OP_COALESCE, PARAM_0:string, NULL, CONST:string
36 // These expressions have the same fingerprint since all constants of the same underlying type are
39 // It's also important that the fingerprints don't reference the actual Expression objects that were
40 // used to generate them, as the fingerprints will be cached, and caching a fingerprint that references
41 // an Expression will root the Expression (and any objects it references).
43 internal sealed class ExpressionFingerprintChain : IEquatable<ExpressionFingerprintChain> {
45 public readonly List<ExpressionFingerprint> Elements = new List<ExpressionFingerprint>();
47 public bool Equals(ExpressionFingerprintChain other) {
48 // Two chains are considered equal if two elements appearing in the same index in
49 // each chain are equal (value equality, not referential equality).
55 if (this.Elements.Count != other.Elements.Count) {
59 for (int i = 0; i < this.Elements.Count; i++) {
60 if (!Object.Equals(this.Elements[i], other.Elements[i])) {
68 public override bool Equals(object obj) {
69 return Equals(obj as ExpressionFingerprintChain);
72 public override int GetHashCode() {
73 HashCodeCombiner combiner = new HashCodeCombiner();
74 Elements.ForEach(combiner.AddFingerprint);
75 return combiner.CombinedHash;