1 /* ****************************************************************************
\r
3 * Copyright (c) Microsoft Corporation.
\r
5 * This source code is subject to terms and conditions of the Microsoft Public License. A
\r
6 * copy of the license can be found in the License.html file at the root of this distribution. If
\r
7 * you cannot locate the Microsoft Public License, please send an email to
\r
8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
\r
9 * by the terms of the Microsoft Public License.
\r
11 * You must not remove this notice, or any other, from this software.
\r
14 * ***************************************************************************/
\r
15 using System; using Microsoft;
\r
18 using System.Collections.Generic;
\r
19 using System.Diagnostics;
\r
21 using System.Dynamic.Utils;
\r
22 using System.Linq.Expressions;
\r
24 using Microsoft.Scripting.Utils;
\r
25 using Microsoft.Linq.Expressions;
\r
27 using System.Runtime.CompilerServices;
\r
29 using Microsoft.Runtime.CompilerServices;
34 namespace System.Dynamic {
\r
36 namespace Microsoft.Scripting {
\r
40 /// Represents a set of binding restrictions on the <see cref="DynamicMetaObject"/>under which the dynamic binding is valid.
\r
43 [DebuggerTypeProxy(typeof(BindingRestrictionsProxy)), DebuggerDisplay("{DebugView}")]
\r
45 public abstract class BindingRestrictions {
\r
47 /// Represents an empty set of binding restrictions. This field is read only.
\r
49 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
\r
50 public static readonly BindingRestrictions Empty = new CustomRestriction(Expression.Constant(true));
\r
52 private const int TypeRestrictionHash = 0x10000000;
\r
53 private const int InstanceRestrictionHash = 0x20000000;
\r
54 private const int CustomRestrictionHash = 0x40000000;
\r
56 private BindingRestrictions() {
\r
59 // Overridden by specialized subclasses
\r
60 internal abstract Expression GetExpression();
\r
63 /// Merges the set of binding restrictions with the current binding restrictions.
\r
65 /// <param name="restrictions">The set of restrictions with which to merge the current binding restrictions.</param>
\r
66 /// <returns>The new set of binding restrictions.</returns>
\r
67 public BindingRestrictions Merge(BindingRestrictions restrictions) {
\r
68 ContractUtils.RequiresNotNull(restrictions, "restrictions");
\r
69 if (this == Empty) {
\r
70 return restrictions;
\r
72 if (restrictions == Empty) {
\r
75 return new MergedRestriction(this, restrictions);
\r
79 /// Creates the binding restriction that check the expression for runtime type identity.
\r
81 /// <param name="expression">The expression to test.</param>
\r
82 /// <param name="type">The exact type to test.</param>
\r
83 /// <returns>The new binding restrictions.</returns>
\r
84 public static BindingRestrictions GetTypeRestriction(Expression expression, Type type) {
\r
85 ContractUtils.RequiresNotNull(expression, "expression");
\r
86 ContractUtils.RequiresNotNull(type, "type");
\r
88 return new TypeRestriction(expression, type);
\r
92 /// The method takes a DynamicMetaObject, and returns an instance restriction for testing null if the object
\r
93 /// holds a null value, otherwise returns a type restriction.
\r
95 internal static BindingRestrictions GetTypeRestriction(DynamicMetaObject obj) {
\r
96 if (obj.Value == null && obj.HasValue) {
\r
97 return BindingRestrictions.GetInstanceRestriction(obj.Expression, null);
\r
99 return BindingRestrictions.GetTypeRestriction(obj.Expression, obj.LimitType);
\r
104 /// Creates the binding restriction that checks the expression for object instance identity.
\r
106 /// <param name="expression">The expression to test.</param>
\r
107 /// <param name="instance">The exact object instance to test.</param>
\r
108 /// <returns>The new binding restrictions.</returns>
\r
109 public static BindingRestrictions GetInstanceRestriction(Expression expression, object instance) {
\r
110 ContractUtils.RequiresNotNull(expression, "expression");
\r
112 return new InstanceRestriction(expression, instance);
\r
116 /// Creates the binding restriction that checks the expression for arbitrary immutable properties.
\r
118 /// <param name="expression">The expression expression the restrictions.</param>
\r
119 /// <returns>The new binding restrictions.</returns>
\r
121 /// By convention, the general restrictions created by this method must only test
\r
122 /// immutable object properties.
\r
124 public static BindingRestrictions GetExpressionRestriction(Expression expression) {
\r
125 ContractUtils.RequiresNotNull(expression, "expression");
\r
126 ContractUtils.Requires(expression.Type == typeof(bool), "expression");
\r
127 return new CustomRestriction(expression);
\r
131 /// Combines binding restrictions from the list of <see cref="DynamicMetaObject"/> instances into one set of restrictions.
\r
133 /// <param name="contributingObjects">The list of <see cref="DynamicMetaObject"/> instances from which to combine restrictions.</param>
\r
134 /// <returns>The new set of binding restrictions.</returns>
\r
135 public static BindingRestrictions Combine(IList<DynamicMetaObject> contributingObjects) {
\r
136 BindingRestrictions res = BindingRestrictions.Empty;
\r
137 if (contributingObjects != null) {
\r
138 foreach (DynamicMetaObject mo in contributingObjects) {
\r
140 res = res.Merge(mo.Restrictions);
\r
148 /// Builds a balanced tree of AndAlso nodes.
\r
149 /// We do this so the compiler won't stack overflow if we have many
\r
152 private sealed class TestBuilder {
\r
153 private readonly Set<BindingRestrictions> _unique = new Set<BindingRestrictions>();
\r
154 private readonly Stack<AndNode> _tests = new Stack<AndNode>();
\r
156 private struct AndNode {
\r
157 internal int Depth;
\r
158 internal Expression Node;
\r
161 internal void Append(BindingRestrictions restrictions) {
\r
162 if (_unique.Contains(restrictions)) {
\r
165 _unique.Add(restrictions);
\r
167 Push(restrictions.GetExpression(), 0);
\r
170 internal Expression ToExpression() {
\r
171 Expression result = _tests.Pop().Node;
\r
172 while (_tests.Count > 0) {
\r
173 result = Expression.AndAlso(_tests.Pop().Node, result);
\r
178 private void Push(Expression node, int depth) {
\r
179 while (_tests.Count > 0 && _tests.Peek().Depth == depth) {
\r
180 node = Expression.AndAlso(_tests.Pop().Node, node);
\r
183 _tests.Push(new AndNode { Node = node, Depth = depth });
\r
188 /// Creates the <see cref="Expression"/> representing the binding restrictions.
\r
190 /// <returns>The expression tree representing the restrictions.</returns>
\r
191 public Expression ToExpression() {
\r
192 // We could optimize this better, e.g. common subexpression elimination
\r
193 // But for now, it's good enough.
\r
195 if (this == Empty) {
\r
196 return Expression.Constant(true);
\r
199 var testBuilder = new TestBuilder();
\r
201 // Visit the tree, left to right.
\r
202 // Use an explicit stack so we don't stack overflow.
\r
204 // Left-most node is on top of the stack, so we always expand the
\r
205 // left most node each iteration.
\r
206 var stack = new Stack<BindingRestrictions>();
\r
209 var top = stack.Pop();
\r
210 var m = top as MergedRestriction;
\r
212 stack.Push(m.Right);
\r
213 stack.Push(m.Left);
\r
215 testBuilder.Append(top);
\r
217 } while (stack.Count > 0);
\r
219 return testBuilder.ToExpression();
\r
222 private sealed class MergedRestriction : BindingRestrictions {
\r
223 internal readonly BindingRestrictions Left;
\r
224 internal readonly BindingRestrictions Right;
\r
226 internal MergedRestriction(BindingRestrictions left, BindingRestrictions right) {
\r
230 internal override Expression GetExpression() {
\r
231 throw ContractUtils.Unreachable;
\r
235 private sealed class CustomRestriction : BindingRestrictions {
\r
236 private readonly Expression _expression;
\r
238 internal CustomRestriction(Expression expression) {
\r
239 _expression = expression;
\r
242 public override bool Equals(object obj) {
\r
243 var other = obj as CustomRestriction;
\r
244 return other != null && other._expression == _expression;
\r
247 public override int GetHashCode() {
\r
248 return CustomRestrictionHash ^ _expression.GetHashCode();
\r
251 internal override Expression GetExpression() {
\r
252 return _expression;
\r
256 private sealed class TypeRestriction : BindingRestrictions {
\r
257 private readonly Expression _expression;
\r
258 private readonly Type _type;
\r
260 internal TypeRestriction(Expression parameter, Type type) {
\r
261 _expression = parameter;
\r
265 public override bool Equals(object obj) {
\r
266 var other = obj as TypeRestriction;
\r
267 return other != null && TypeUtils.AreEquivalent(other._type, _type) && other._expression == _expression;
\r
270 public override int GetHashCode() {
\r
271 return TypeRestrictionHash ^ _expression.GetHashCode() ^ _type.GetHashCode();
\r
274 internal override Expression GetExpression() {
\r
275 return Expression.TypeEqual(_expression, _type);
\r
279 private sealed class InstanceRestriction : BindingRestrictions {
\r
280 private readonly Expression _expression;
\r
281 private readonly object _instance;
\r
283 internal InstanceRestriction(Expression parameter, object instance) {
\r
284 _expression = parameter;
\r
285 _instance = instance;
\r
288 public override bool Equals(object obj) {
\r
289 var other = obj as InstanceRestriction;
\r
290 return other != null && other._instance == _instance && other._expression == _expression;
\r
293 public override int GetHashCode() {
\r
294 return InstanceRestrictionHash ^ RuntimeHelpers.GetHashCode(_instance) ^ _expression.GetHashCode();
\r
297 internal override Expression GetExpression() {
\r
298 if (_instance == null) {
\r
299 return Expression.Equal(
\r
300 Expression.Convert(_expression, typeof(object)),
\r
301 Expression.Constant(null)
\r
305 ParameterExpression temp = Expression.Parameter(typeof(object), null);
\r
306 return Expression.Block(
\r
310 Expression.Property(
\r
311 Expression.Constant(new WeakReference(_instance)),
\r
312 typeof(WeakReference).GetProperty("Target")
\r
315 Expression.AndAlso(
\r
316 //check that WeekReference was not collected.
\r
317 Expression.NotEqual(temp, Expression.Constant(null)),
\r
319 Expression.Convert(_expression, typeof(object)),
\r
327 private string DebugView {
\r
328 get { return ToExpression().ToString(); }
\r
331 private sealed class BindingRestrictionsProxy {
\r
332 private readonly BindingRestrictions _node;
\r
334 public BindingRestrictionsProxy(BindingRestrictions node) {
\r
338 public bool IsEmpty {
\r
339 get { return _node == Empty; }
\r
342 public Expression Test {
\r
343 get { return _node.ToExpression(); }
\r
346 public BindingRestrictions[] Restrictions {
\r
348 var restrictions = new List<BindingRestrictions>();
\r
350 // Visit the tree, left to right
\r
352 // Left-most node is on top of the stack, so we always expand the
\r
353 // left most node each iteration.
\r
354 var stack = new Stack<BindingRestrictions>();
\r
357 var top = stack.Pop();
\r
358 var m = top as MergedRestriction;
\r
360 stack.Push(m.Right);
\r
361 stack.Push(m.Left);
\r
363 restrictions.Add(top);
\r
365 } while (stack.Count > 0);
\r
367 return restrictions.ToArray();
\r
371 public override string ToString() {
\r
372 // To prevent fxcop warning about this field
\r
373 return _node.DebugView;
\r