Merge pull request #249 from pcc/xgetinputfocus
[mono.git] / mcs / class / System.Web.Mvc3 / Mvc / ExpressionUtil / ExpressionFingerprintChain.cs
1 namespace System.Web.Mvc.ExpressionUtil {
2     using System;
3     using System.Collections.Generic;
4
5     // Expression fingerprint chain class
6     // Contains information used for generalizing, comparing, and recreating Expression instances
7     //
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.
14     //
15     // Some sample fingerprints chains:
16     //
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
20     //
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.)
26     //
27     // "Hello " + "world" -> OP_ADD, CONST:string, NULL, CONST:string
28     // "Hello " + {model} -> OP_ADD, CONST:string, NULL, PARAM_0:string
29     //
30     // These string concatenations have different fingerprints since the inputs are provided differently:
31     // one is a constant, the other is a parameter.
32     //
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
35     //
36     // These expressions have the same fingerprint since all constants of the same underlying type are
37     // treated equally.
38     //
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).
42
43     internal sealed class ExpressionFingerprintChain : IEquatable<ExpressionFingerprintChain> {
44
45         public readonly List<ExpressionFingerprint> Elements = new List<ExpressionFingerprint>();
46
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).
50
51             if (other == null) {
52                 return false;
53             }
54
55             if (this.Elements.Count != other.Elements.Count) {
56                 return false;
57             }
58
59             for (int i = 0; i < this.Elements.Count; i++) {
60                 if (!Object.Equals(this.Elements[i], other.Elements[i])) {
61                     return false;
62                 }
63             }
64
65             return true;
66         }
67
68         public override bool Equals(object obj) {
69             return Equals(obj as ExpressionFingerprintChain);
70         }
71
72         public override int GetHashCode() {
73             HashCodeCombiner combiner = new HashCodeCombiner();
74             Elements.ForEach(combiner.AddFingerprint);
75             return combiner.CombinedHash;
76         }
77
78     }
79 }