PageRenderTime 191ms CodeModel.GetById 100ms app.highlight 13ms RepoModel.GetById 68ms app.codeStats 7ms

/Microsoft.Scripting.Core/Actions/BindingRestrictions.cs

https://bitbucket.org/stefanrusek/xronos
C# | 317 lines | 203 code | 40 blank | 74 comment | 33 complexity | 6433af9aad2448356f3a6783ff7f9f81 MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Microsoft Public License. A 
  6 * copy of the license can be found in the License.html file at the root of this distribution. If 
  7 * you cannot locate the  Microsoft Public License, please send an email to 
  8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
  9 * by the terms of the Microsoft Public License.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15using System; using Microsoft;
 16
 17
 18using System.Collections.Generic;
 19#if CODEPLEX_40
 20using System.Dynamic.Utils;
 21using System.Linq.Expressions;
 22#else
 23using Microsoft.Scripting.Utils;
 24using Microsoft.Linq.Expressions;
 25#endif
 26
 27#if CODEPLEX_40
 28namespace System.Dynamic {
 29#else
 30namespace Microsoft.Scripting {
 31#endif
 32    /// <summary>
 33    /// Represents a set of binding restrictions on the <see cref="DynamicMetaObject"/>under which the dynamic binding is valid.
 34    /// </summary>
 35    public sealed class BindingRestrictions {
 36        private class Restriction {
 37            internal enum RestrictionKind {
 38                Type,
 39                Instance,
 40                Custom
 41            };
 42
 43            private readonly RestrictionKind _kind;
 44
 45            // Simplification ... for now just one kind of restriction rather than hierarchy.
 46            private readonly Expression _expression;
 47            private readonly Type _type;
 48
 49            // We hold onto the instance here, but that's okay, because
 50            // we're binding. Once we generate the instance test however, we 
 51            // should store the instance as a WeakReference
 52            private readonly object _instance;
 53
 54            internal RestrictionKind Kind {
 55                get { return _kind; }
 56            }
 57
 58            internal Expression Expression {
 59                get { return _expression; }
 60            }
 61
 62            internal Type Type {
 63                get { return _type; }
 64            }
 65
 66            internal object Instance {
 67                get { return _instance; }
 68            }
 69
 70            internal Restriction(Expression parameter, Type type) {
 71                _kind = RestrictionKind.Type;
 72                _expression = parameter;
 73                _type = type;
 74            }
 75
 76            internal Restriction(Expression parameter, object instance) {
 77                _kind = RestrictionKind.Instance;
 78                _expression = parameter;
 79                _instance = instance;
 80            }
 81
 82            internal Restriction(Expression expression) {
 83                _kind = RestrictionKind.Custom;
 84                _expression = expression;
 85            }
 86
 87            public override bool Equals(object obj) {
 88                Restriction other = obj as Restriction;
 89                if (other == null) {
 90                    return false;
 91                }
 92
 93                if (other.Kind != Kind ||
 94                    other.Expression != Expression) {
 95                    return false;
 96                }
 97
 98                switch (other.Kind) {
 99                    case RestrictionKind.Instance:
100                        return other.Instance == Instance;
101                    case RestrictionKind.Type:
102                        return other.Type == Type;
103                    default:
104                        return false;
105                }
106            }
107
108            public override int GetHashCode() {
109                // lots of collisions but we don't hash Restrictions ever
110                return (int)Kind ^ Expression.GetHashCode();
111            }
112        }
113
114        private readonly Restriction[] _restrictions;
115
116        private BindingRestrictions(params Restriction[] restrictions) {
117            _restrictions = restrictions;
118        }
119
120        private bool IsEmpty {
121            get {
122                return _restrictions.Length == 0;
123            }
124        }
125
126        /// <summary>
127        /// Merges the set of binding restrictions with the current binding restrictions.
128        /// </summary>
129        /// <param name="restrictions">The set of restrictions with which to merge the current binding restrictions.</param>
130        /// <returns>The new set of binding restrictions.</returns>
131        public BindingRestrictions Merge(BindingRestrictions restrictions) {
132            if (restrictions.IsEmpty) {
133                return this;
134            } else if (IsEmpty) {
135                return restrictions;
136            } else {
137                List<Restriction> res = new List<Restriction>(_restrictions.Length + restrictions._restrictions.Length);
138                AddRestrictions(_restrictions, res);
139                AddRestrictions(restrictions._restrictions, res);
140
141                return new BindingRestrictions(res.ToArray());
142            }
143        }
144
145        /// <summary>
146        /// Adds unique restrictions and doesn't add restrictions which are alerady present
147        /// </summary>
148        private static void AddRestrictions(Restriction[] list, List<Restriction> res) {
149            foreach (Restriction r in list) {
150                bool found = false;
151                for (int j = 0; j < res.Count; j++) {
152                    if (res[j].Equals(r)) {
153                        found = true;
154                    }
155                }
156
157                if (!found) {
158                    res.Add(r);
159                }
160            }
161        }
162
163        /// <summary>
164        /// Represents an empty set of binding restrictions. This field is read only.
165        /// </summary>
166        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
167        public static readonly BindingRestrictions Empty = new BindingRestrictions();
168
169        /// <summary>
170        /// Creates the binding restriction that check the expression for runtime type identity.
171        /// </summary>
172        /// <param name="expression">The expression to test.</param>
173        /// <param name="type">The exact type to test.</param>
174        /// <returns>The new binding restrictions.</returns>
175        public static BindingRestrictions GetTypeRestriction(Expression expression, Type type) {
176            ContractUtils.RequiresNotNull(expression, "expression");
177            ContractUtils.RequiresNotNull(type, "type");
178
179            return new BindingRestrictions(new Restriction(expression, type));
180        }
181
182        /// <summary>
183        /// The method takes a DynamicMetaObject, and returns an instance restriction for testing null if the object
184        /// holds a null value, otherwise returns a type restriction.
185        /// </summary>
186        internal static BindingRestrictions GetTypeRestriction(DynamicMetaObject obj) {
187            if (obj.Value == null && obj.HasValue) {
188                return BindingRestrictions.GetInstanceRestriction(obj.Expression, null);
189            } else {
190                return BindingRestrictions.GetTypeRestriction(obj.Expression, obj.LimitType);
191            }
192        }
193
194        /// <summary>
195        /// Creates the binding restriction that checks the expression for object instance identity.
196        /// </summary>
197        /// <param name="expression">The expression to test.</param>
198        /// <param name="instance">The exact object instance to test.</param>
199        /// <returns>The new binding restrictions.</returns>
200        public static BindingRestrictions GetInstanceRestriction(Expression expression, object instance) {
201            ContractUtils.RequiresNotNull(expression, "expression");
202
203            return new BindingRestrictions(new Restriction(expression, instance));
204        }
205
206        /// <summary>
207        /// Creates the binding restriction that checks the expression for arbitrary immutable properties.
208        /// </summary>
209        /// <param name="expression">The expression expression the restrictions.</param>
210        /// <returns>The new binding restrictions.</returns>
211        /// <remarks>
212        /// By convention, the general restrictions created by this method must only test
213        /// immutable object properties.
214        /// </remarks>
215        public static BindingRestrictions GetExpressionRestriction(Expression expression) {
216            ContractUtils.RequiresNotNull(expression, "expression");
217            ContractUtils.Requires(expression.Type == typeof(bool), "expression");
218            return new BindingRestrictions(new Restriction(expression));
219        }
220
221        /// <summary>
222        /// Combines binding restrictions from the list of <see cref="DynamicMetaObject"/> instances into one set of restrictions.
223        /// </summary>
224        /// <param name="contributingObjects">The list of <see cref="DynamicMetaObject"/> instances from which to combine restrictions.</param>
225        /// <returns>The new set of binding restrictions.</returns>
226        public static BindingRestrictions Combine(IList<DynamicMetaObject> contributingObjects) {
227            BindingRestrictions res = BindingRestrictions.Empty;
228            if (contributingObjects != null) {
229                foreach (DynamicMetaObject mo in contributingObjects) {
230                    if (mo != null) {
231                        res = res.Merge(mo.Restrictions);
232                    }
233                }
234            }
235            return res;
236        }
237
238        /// <summary>
239        /// Creates the <see cref="Expression"/> representing the binding restrictions.
240        /// </summary>
241        /// <returns>The expression tree representing the restrictions.</returns>
242        public Expression ToExpression() {
243            // We could optimize this better, e.g. common subexpression elimination
244            // But for now, it's good enough.
245            Expression test = null;
246            foreach (Restriction r in _restrictions) {
247                Expression one;
248                switch (r.Kind) {
249                    case Restriction.RestrictionKind.Type:
250                        one = CreateTypeRestriction(r.Expression, r.Type);
251                        break;
252                    case Restriction.RestrictionKind.Instance:
253                        one = CreateInstanceRestriction(r.Expression, r.Instance);
254                        break;
255                    case Restriction.RestrictionKind.Custom:
256                        one = r.Expression;
257                        break;
258                    default:
259                        throw new InvalidOperationException();
260                }
261
262                if (one != null) {
263                    if (test == null) {
264                        test = one;
265                    } else {
266                        test = Expression.AndAlso(test, one);
267                    }
268                }
269            }
270
271            return test ?? Expression.Constant(true);
272        }
273
274        /// <summary>
275        /// Creates one type identity test 
276        /// </summary>
277        private static Expression CreateTypeRestriction(Expression expression, Type rt) {
278            return Expression.TypeEqual(expression, rt);
279        }
280
281        private static Expression CreateInstanceRestriction(Expression expression, object value) {
282            if (value == null) {
283                return Expression.Equal(
284                    Expression.Convert(expression, typeof(object)),
285                    Expression.Constant(null)
286                );
287            }
288
289            // TODO: need to add special cases for valuetypes and nullables
290            ParameterExpression temp = Expression.Parameter(typeof(object), null);
291
292            Expression init = Expression.Assign(
293                temp,
294                Expression.Property(
295                    Expression.Constant(new WeakReference(value)),
296                    typeof(WeakReference).GetProperty("Target")
297                )
298            );
299
300            return Expression.Block(
301                new ParameterExpression[] { temp },
302                init,
303                Expression.AndAlso(
304                //check that WeekReference was not collected.
305                    Expression.NotEqual(
306                        temp,
307                        Expression.Constant(null)
308                    ),
309                    Expression.Equal(
310                        temp,
311                        Expression.Convert(expression, typeof(object))
312                    )
313                )
314            );
315        }
316    }
317}