1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. 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 Apache License, Version 2.0, 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 Apache License, Version 2.0.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using Microsoft.Scripting.Ast;
19 using System.Linq.Expressions;
23 using System.Collections.Generic;
24 using System.Diagnostics;
25 using System.Dynamic.Utils;
26 using System.Runtime.CompilerServices;
28 namespace System.Dynamic {
31 /// Represents a set of binding restrictions on the <see cref="DynamicMetaObject"/>under which the dynamic binding is valid.
33 [DebuggerTypeProxy(typeof(BindingRestrictionsProxy)), DebuggerDisplay("{DebugView}")]
34 public abstract class BindingRestrictions {
36 /// Represents an empty set of binding restrictions. This field is read only.
38 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
39 public static readonly BindingRestrictions Empty = new CustomRestriction(Expression.Constant(true));
41 private const int TypeRestrictionHash = 0x10000000;
42 private const int InstanceRestrictionHash = 0x20000000;
43 private const int CustomRestrictionHash = 0x40000000;
45 private BindingRestrictions() {
48 // Overridden by specialized subclasses
49 internal abstract Expression GetExpression();
52 /// Merges the set of binding restrictions with the current binding restrictions.
54 /// <param name="restrictions">The set of restrictions with which to merge the current binding restrictions.</param>
55 /// <returns>The new set of binding restrictions.</returns>
56 public BindingRestrictions Merge(BindingRestrictions restrictions) {
57 ContractUtils.RequiresNotNull(restrictions, "restrictions");
61 if (restrictions == Empty) {
64 return new MergedRestriction(this, restrictions);
68 /// Creates the binding restriction that check the expression for runtime type identity.
70 /// <param name="expression">The expression to test.</param>
71 /// <param name="type">The exact type to test.</param>
72 /// <returns>The new binding restrictions.</returns>
73 public static BindingRestrictions GetTypeRestriction(Expression expression, Type type) {
74 ContractUtils.RequiresNotNull(expression, "expression");
75 ContractUtils.RequiresNotNull(type, "type");
77 return new TypeRestriction(expression, type);
81 /// The method takes a DynamicMetaObject, and returns an instance restriction for testing null if the object
82 /// holds a null value, otherwise returns a type restriction.
84 internal static BindingRestrictions GetTypeRestriction(DynamicMetaObject obj) {
85 if (obj.Value == null && obj.HasValue) {
86 return BindingRestrictions.GetInstanceRestriction(obj.Expression, null);
88 return BindingRestrictions.GetTypeRestriction(obj.Expression, obj.LimitType);
93 /// Creates the binding restriction that checks the expression for object instance identity.
95 /// <param name="expression">The expression to test.</param>
96 /// <param name="instance">The exact object instance to test.</param>
97 /// <returns>The new binding restrictions.</returns>
98 public static BindingRestrictions GetInstanceRestriction(Expression expression, object instance) {
99 ContractUtils.RequiresNotNull(expression, "expression");
101 return new InstanceRestriction(expression, instance);
105 /// Creates the binding restriction that checks the expression for arbitrary immutable properties.
107 /// <param name="expression">The expression expression the restrictions.</param>
108 /// <returns>The new binding restrictions.</returns>
110 /// By convention, the general restrictions created by this method must only test
111 /// immutable object properties.
113 public static BindingRestrictions GetExpressionRestriction(Expression expression) {
114 ContractUtils.RequiresNotNull(expression, "expression");
115 ContractUtils.Requires(expression.Type == typeof(bool), "expression");
116 return new CustomRestriction(expression);
120 /// Combines binding restrictions from the list of <see cref="DynamicMetaObject"/> instances into one set of restrictions.
122 /// <param name="contributingObjects">The list of <see cref="DynamicMetaObject"/> instances from which to combine restrictions.</param>
123 /// <returns>The new set of binding restrictions.</returns>
124 public static BindingRestrictions Combine(IList<DynamicMetaObject> contributingObjects) {
125 BindingRestrictions res = BindingRestrictions.Empty;
126 if (contributingObjects != null) {
127 foreach (DynamicMetaObject mo in contributingObjects) {
129 res = res.Merge(mo.Restrictions);
137 /// Builds a balanced tree of AndAlso nodes.
138 /// We do this so the compiler won't stack overflow if we have many
141 private sealed class TestBuilder {
142 private readonly Set<BindingRestrictions> _unique = new Set<BindingRestrictions>();
143 private readonly Stack<AndNode> _tests = new Stack<AndNode>();
145 private struct AndNode {
147 internal Expression Node;
150 internal void Append(BindingRestrictions restrictions) {
151 if (_unique.Contains(restrictions)) {
154 _unique.Add(restrictions);
156 Push(restrictions.GetExpression(), 0);
159 internal Expression ToExpression() {
160 Expression result = _tests.Pop().Node;
161 while (_tests.Count > 0) {
162 result = Expression.AndAlso(_tests.Pop().Node, result);
167 private void Push(Expression node, int depth) {
168 while (_tests.Count > 0 && _tests.Peek().Depth == depth) {
169 node = Expression.AndAlso(_tests.Pop().Node, node);
172 _tests.Push(new AndNode { Node = node, Depth = depth });
177 /// Creates the <see cref="Expression"/> representing the binding restrictions.
179 /// <returns>The expression tree representing the restrictions.</returns>
180 public Expression ToExpression() {
181 // We could optimize this better, e.g. common subexpression elimination
182 // But for now, it's good enough.
185 return Expression.Constant(true);
188 var testBuilder = new TestBuilder();
190 // Visit the tree, left to right.
191 // Use an explicit stack so we don't stack overflow.
193 // Left-most node is on top of the stack, so we always expand the
194 // left most node each iteration.
195 var stack = new Stack<BindingRestrictions>();
198 var top = stack.Pop();
199 var m = top as MergedRestriction;
204 testBuilder.Append(top);
206 } while (stack.Count > 0);
208 return testBuilder.ToExpression();
211 private sealed class MergedRestriction : BindingRestrictions {
212 internal readonly BindingRestrictions Left;
213 internal readonly BindingRestrictions Right;
215 internal MergedRestriction(BindingRestrictions left, BindingRestrictions right) {
219 internal override Expression GetExpression() {
220 throw ContractUtils.Unreachable;
224 private sealed class CustomRestriction : BindingRestrictions {
225 private readonly Expression _expression;
227 internal CustomRestriction(Expression expression) {
228 _expression = expression;
231 public override bool Equals(object obj) {
232 var other = obj as CustomRestriction;
233 return other != null && other._expression == _expression;
236 public override int GetHashCode() {
237 return CustomRestrictionHash ^ _expression.GetHashCode();
240 internal override Expression GetExpression() {
245 private sealed class TypeRestriction : BindingRestrictions {
246 private readonly Expression _expression;
247 private readonly Type _type;
249 internal TypeRestriction(Expression parameter, Type type) {
250 _expression = parameter;
254 public override bool Equals(object obj) {
255 var other = obj as TypeRestriction;
256 return other != null && TypeUtils.AreEquivalent(other._type, _type) && other._expression == _expression;
259 public override int GetHashCode() {
260 return TypeRestrictionHash ^ _expression.GetHashCode() ^ _type.GetHashCode();
263 internal override Expression GetExpression() {
264 return Expression.TypeEqual(_expression, _type);
268 private sealed class InstanceRestriction : BindingRestrictions {
269 private readonly Expression _expression;
270 private readonly object _instance;
272 internal InstanceRestriction(Expression parameter, object instance) {
273 _expression = parameter;
274 _instance = instance;
277 public override bool Equals(object obj) {
278 var other = obj as InstanceRestriction;
279 return other != null && other._instance == _instance && other._expression == _expression;
282 public override int GetHashCode() {
283 return InstanceRestrictionHash ^ ReferenceEqualityComparer<object>.Instance.GetHashCode(_instance) ^ _expression.GetHashCode();
286 internal override Expression GetExpression() {
287 if (_instance == null) {
288 return Expression.Equal(
289 Expression.Convert(_expression, typeof(object)),
290 Expression.Constant(null)
294 ParameterExpression temp = Expression.Parameter(typeof(object), null);
295 return Expression.Block(
300 Expression.Constant(new WeakReference(_instance)),
301 typeof(WeakReference).GetProperty("Target")
305 //check that WeekReference was not collected.
306 Expression.NotEqual(temp, Expression.Constant(null)),
308 Expression.Convert(_expression, typeof(object)),
316 private string DebugView {
317 get { return ToExpression().ToString(); }
320 private sealed class BindingRestrictionsProxy {
321 private readonly BindingRestrictions _node;
323 public BindingRestrictionsProxy(BindingRestrictions node) {
327 public bool IsEmpty {
328 get { return _node == Empty; }
331 public Expression Test {
332 get { return _node.ToExpression(); }
335 public BindingRestrictions[] Restrictions {
337 var restrictions = new List<BindingRestrictions>();
339 // Visit the tree, left to right
341 // Left-most node is on top of the stack, so we always expand the
342 // left most node each iteration.
343 var stack = new Stack<BindingRestrictions>();
346 var top = stack.Pop();
347 var m = top as MergedRestriction;
352 restrictions.Add(top);
354 } while (stack.Count > 0);
356 return restrictions.ToArray();
360 public override string ToString() {
361 // To prevent fxcop warning about this field
362 return _node.DebugView;