Fix object::GetType when remoting is enabled.
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Actions / BindingRestrictions.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
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.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 #if !FEATURE_CORE_DLR
17 using Microsoft.Scripting.Ast;
18 #else
19 using System.Linq.Expressions;
20 #endif
21
22 using System;
23 using System.Collections.Generic;
24 using System.Diagnostics;
25 using System.Dynamic.Utils;
26 using System.Runtime.CompilerServices;
27
28 namespace System.Dynamic {
29
30     /// <summary>
31     /// Represents a set of binding restrictions on the <see cref="DynamicMetaObject"/>under which the dynamic binding is valid.
32     /// </summary>
33     [DebuggerTypeProxy(typeof(BindingRestrictionsProxy)), DebuggerDisplay("{DebugView}")]
34     public abstract class BindingRestrictions {
35         /// <summary>
36         /// Represents an empty set of binding restrictions. This field is read only.
37         /// </summary>
38         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
39         public static readonly BindingRestrictions Empty = new CustomRestriction(Expression.Constant(true));
40
41         private const int TypeRestrictionHash = 0x10000000;
42         private const int InstanceRestrictionHash = 0x20000000;
43         private const int CustomRestrictionHash = 0x40000000;
44         
45         private BindingRestrictions() {
46         }
47
48         // Overridden by specialized subclasses
49         internal abstract Expression GetExpression();
50
51         /// <summary>
52         /// Merges the set of binding restrictions with the current binding restrictions.
53         /// </summary>
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");
58             if (this == Empty) {
59                 return restrictions;
60             }
61             if (restrictions == Empty) {
62                 return this;
63             }
64             return new MergedRestriction(this, restrictions);
65         }
66
67         /// <summary>
68         /// Creates the binding restriction that check the expression for runtime type identity.
69         /// </summary>
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");
76
77             return new TypeRestriction(expression, type);
78         }
79
80         /// <summary>
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.
83         /// </summary>
84         internal static BindingRestrictions GetTypeRestriction(DynamicMetaObject obj) {
85             if (obj.Value == null && obj.HasValue) {
86                 return BindingRestrictions.GetInstanceRestriction(obj.Expression, null);
87             } else {
88                 return BindingRestrictions.GetTypeRestriction(obj.Expression, obj.LimitType);
89             }
90         }
91
92         /// <summary>
93         /// Creates the binding restriction that checks the expression for object instance identity.
94         /// </summary>
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");
100
101             return new InstanceRestriction(expression, instance);
102         }
103
104         /// <summary>
105         /// Creates the binding restriction that checks the expression for arbitrary immutable properties.
106         /// </summary>
107         /// <param name="expression">The expression expression the restrictions.</param>
108         /// <returns>The new binding restrictions.</returns>
109         /// <remarks>
110         /// By convention, the general restrictions created by this method must only test
111         /// immutable object properties.
112         /// </remarks>
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);
117         }
118
119         /// <summary>
120         /// Combines binding restrictions from the list of <see cref="DynamicMetaObject"/> instances into one set of restrictions.
121         /// </summary>
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) {
128                     if (mo != null) {
129                         res = res.Merge(mo.Restrictions);
130                     }
131                 }
132             }
133             return res;
134         }
135
136         /// <summary>
137         /// Builds a balanced tree of AndAlso nodes.
138         /// We do this so the compiler won't stack overflow if we have many
139         /// restrictions.
140         /// </summary>
141         private sealed class TestBuilder {
142             private readonly Set<BindingRestrictions> _unique = new Set<BindingRestrictions>();
143             private readonly Stack<AndNode> _tests = new Stack<AndNode>();
144
145             private struct AndNode {
146                 internal int Depth;
147                 internal Expression Node;
148             }
149
150             internal void Append(BindingRestrictions restrictions) {
151                 if (_unique.Contains(restrictions)) {
152                     return;
153                 }
154                 _unique.Add(restrictions);
155
156                 Push(restrictions.GetExpression(), 0);
157             }
158
159             internal Expression ToExpression() {
160                 Expression result = _tests.Pop().Node;
161                 while (_tests.Count > 0) {
162                     result = Expression.AndAlso(_tests.Pop().Node, result);
163                 }
164                 return result;
165             }
166
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);
170                     depth++;
171                 }
172                 _tests.Push(new AndNode { Node = node, Depth = depth });
173             }
174         }
175
176         /// <summary>
177         /// Creates the <see cref="Expression"/> representing the binding restrictions.
178         /// </summary>
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.
183
184             if (this == Empty) {
185                 return Expression.Constant(true);
186             }
187
188             var testBuilder = new TestBuilder();
189
190             // Visit the tree, left to right.
191             // Use an explicit stack so we don't stack overflow.
192             //
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>();
196             stack.Push(this);
197             do {
198                 var top = stack.Pop();
199                 var m = top as MergedRestriction;
200                 if (m != null) {
201                     stack.Push(m.Right);
202                     stack.Push(m.Left);
203                 } else {
204                     testBuilder.Append(top);
205                 }
206             } while (stack.Count > 0);
207
208             return testBuilder.ToExpression();
209         }
210
211         private sealed class MergedRestriction : BindingRestrictions {
212             internal readonly BindingRestrictions Left;
213             internal readonly BindingRestrictions Right;
214
215             internal MergedRestriction(BindingRestrictions left, BindingRestrictions right) {
216                 Left = left;
217                 Right = right;
218             }
219             internal override Expression GetExpression() {
220                 throw ContractUtils.Unreachable;
221             }
222         }
223
224         private sealed class CustomRestriction : BindingRestrictions {
225             private readonly Expression _expression;
226
227             internal CustomRestriction(Expression expression) {
228                 _expression = expression;
229             }
230
231             public override bool Equals(object obj) {
232                 var other = obj as CustomRestriction;
233                 return other != null && other._expression == _expression;
234             }
235
236             public override int GetHashCode() {
237                 return CustomRestrictionHash ^ _expression.GetHashCode();
238             }
239
240             internal override Expression GetExpression() {
241                 return _expression;
242             }
243         }
244
245         private sealed class TypeRestriction : BindingRestrictions {
246             private readonly Expression _expression;
247             private readonly Type _type;
248
249             internal TypeRestriction(Expression parameter, Type type) {
250                 _expression = parameter;
251                 _type = type;
252             }
253
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;
257             }
258
259             public override int GetHashCode() {
260                 return TypeRestrictionHash ^ _expression.GetHashCode() ^ _type.GetHashCode();
261             }
262
263             internal override Expression GetExpression() {
264                 return Expression.TypeEqual(_expression, _type);
265             }
266         }
267
268         private sealed class InstanceRestriction : BindingRestrictions {
269             private readonly Expression _expression;
270             private readonly object _instance;
271
272             internal InstanceRestriction(Expression parameter, object instance) {
273                 _expression = parameter;
274                 _instance = instance;
275             }
276
277             public override bool Equals(object obj) {
278                 var other = obj as InstanceRestriction;
279                 return other != null && other._instance == _instance && other._expression == _expression;
280             }
281
282             public override int GetHashCode() {
283                 return InstanceRestrictionHash ^ ReferenceEqualityComparer<object>.Instance.GetHashCode(_instance) ^ _expression.GetHashCode();
284             }
285
286             internal override Expression GetExpression() {
287                 if (_instance == null) {
288                     return Expression.Equal(
289                         Expression.Convert(_expression, typeof(object)),
290                         Expression.Constant(null)
291                     );
292                 }
293
294                 ParameterExpression temp = Expression.Parameter(typeof(object), null);
295                 return Expression.Block(
296                     new[] { temp },
297                     Expression.Assign(
298                         temp,
299                         Expression.Property(
300                             Expression.Constant(new WeakReference(_instance)),
301                             typeof(WeakReference).GetProperty("Target")
302                         )
303                     ),
304                     Expression.AndAlso(
305                     //check that WeekReference was not collected.
306                         Expression.NotEqual(temp, Expression.Constant(null)),
307                         Expression.Equal(
308                             Expression.Convert(_expression, typeof(object)),
309                             temp
310                         )
311                     )
312                 );
313             }
314         }
315
316         private string DebugView {
317             get { return ToExpression().ToString(); }
318         }
319
320         private sealed class BindingRestrictionsProxy {
321             private readonly BindingRestrictions _node;
322
323             public BindingRestrictionsProxy(BindingRestrictions node) {
324                 _node = node;
325             }
326
327             public bool IsEmpty {
328                 get { return _node == Empty; }
329             }
330
331             public Expression Test {
332                 get { return _node.ToExpression(); }
333             }
334
335             public BindingRestrictions[] Restrictions {
336                 get {
337                     var restrictions = new List<BindingRestrictions>();
338
339                     // Visit the tree, left to right
340                     //
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>();
344                     stack.Push(_node);
345                     do {
346                         var top = stack.Pop();
347                         var m = top as MergedRestriction;
348                         if (m != null) {
349                             stack.Push(m.Right);
350                             stack.Push(m.Left);
351                         } else {
352                             restrictions.Add(top);
353                         }
354                     } while (stack.Count > 0);
355
356                     return restrictions.ToArray();
357                 }
358             }
359
360             public override string ToString() {
361                 // To prevent fxcop warning about this field
362                 return _node.DebugView;
363             }
364         }
365     }
366 }