copied mono-api-diff.cs from mono-2-2 branch so new patch can be applied and history...
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Ast / TypeBinaryExpression.cs
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  * ***************************************************************************/
15
16 using System;
17 using System.Diagnostics;
18 using System.Dynamic.Utils;
19
20 #if SILVERLIGHT
21 using System.Core;
22 #endif
23
24 #if CLR2
25 namespace Microsoft.Scripting.Ast {
26 #else
27 namespace System.Linq.Expressions {
28 #endif
29     /// <summary>
30     /// Represents an operation between an expression and a type. 
31     /// </summary>
32 #if !SILVERLIGHT
33     [DebuggerTypeProxy(typeof(Expression.TypeBinaryExpressionProxy))]
34 #endif
35     public sealed class TypeBinaryExpression : Expression {
36         private readonly Expression _expression;
37         private readonly Type _typeOperand;
38         private readonly ExpressionType _nodeKind;
39
40         internal TypeBinaryExpression(Expression expression, Type typeOperand, ExpressionType nodeKind) {
41             _expression = expression;
42             _typeOperand = typeOperand;
43             _nodeKind = nodeKind;
44         }
45
46         /// <summary>
47         /// Gets the static type of the expression that this <see cref="Expression" /> represents.
48         /// </summary>
49         /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
50         public sealed override Type Type {
51             get { return typeof(bool); }
52         }
53
54         /// <summary>
55         /// Returns the node type of this Expression. Extension nodes should return
56         /// ExpressionType.Extension when overriding this method.
57         /// </summary>
58         /// <returns>The <see cref="ExpressionType"/> of the expression.</returns>
59         public sealed override ExpressionType NodeType {
60             get { return _nodeKind; }
61         }
62
63         /// <summary>
64         /// Gets the expression operand of a type test operation.
65         /// </summary>
66         public Expression Expression {
67             get { return _expression; }
68         }
69
70         /// <summary>
71         /// Gets the type operand of a type test operation.
72         /// </summary>
73         public Type TypeOperand {
74             get { return _typeOperand; }
75         }
76
77         #region Reduce TypeEqual
78
79         internal Expression ReduceTypeEqual() {
80             Type cType = Expression.Type;
81
82             // For value types (including Void, but not nullables), we can
83             // determine the result now
84             if (cType.IsValueType && !cType.IsNullableType()) {
85                 return Expression.Block(Expression, Expression.Constant(cType == _typeOperand.GetNonNullableType()));
86             }
87
88             // Can check the value right now for constants.
89             if (Expression.NodeType == ExpressionType.Constant) {
90                 return ReduceConstantTypeEqual();
91             }
92
93             // If the operand type is a sealed reference type or a nullable
94             // type, it will match if value is not null
95             if (cType.IsSealed && (cType == _typeOperand)) {
96                 if (cType.IsNullableType()) {
97                     return Expression.NotEqual(Expression, Expression.Constant(null, Expression.Type));
98                 } else {
99                     return Expression.ReferenceNotEqual(Expression, Expression.Constant(null, Expression.Type));
100                 }
101             }
102
103             // expression is a ByVal parameter. Can safely reevaluate.
104             var parameter = Expression as ParameterExpression;
105             if (parameter != null && !parameter.IsByRef) {
106                 return ByValParameterTypeEqual(parameter);
107             }
108
109             // Create a temp so we only evaluate the left side once
110             parameter = Expression.Parameter(typeof(object));
111
112             // Convert to object if necessary
113             var expression = Expression;
114             if (!TypeUtils.AreReferenceAssignable(typeof(object), expression.Type)) {
115                 expression = Expression.Convert(expression, typeof(object));
116             }
117
118             return Expression.Block(
119                 new[] { parameter },
120                 Expression.Assign(parameter, expression),
121                 ByValParameterTypeEqual(parameter)
122             );
123         }
124
125         // Helper that is used when re-eval of LHS is safe.
126         private Expression ByValParameterTypeEqual(ParameterExpression value) {
127             Expression getType = Expression.Call(value, typeof(object).GetMethod("GetType"));
128             
129             // In remoting scenarios, obj.GetType() can return an interface.
130             // But there's a bug in the JIT32's optimized "obj.GetType() ==
131             // typeof(ISomething)" codegen, causing it to always return false.
132             // We workaround the bug by generating different, less optimal IL
133             // if TypeOperand is an interface.
134             if (_typeOperand.IsInterface) {
135                 var temp = Expression.Parameter(typeof(Type));
136                 getType = Expression.Block(new[] { temp }, Expression.Assign(temp, getType), temp);
137             }
138
139             // We use reference equality when comparing to null for correctness
140             // (don't invoke a user defined operator), and reference equality
141             // on types for performance (so the JIT can optimize the IL).
142             return Expression.AndAlso(
143                 Expression.ReferenceNotEqual(value, Expression.Constant(null)),
144                 Expression.ReferenceEqual(
145                     getType, 
146                     Expression.Constant(_typeOperand.GetNonNullableType(), typeof(Type))
147                 )
148             );
149         }
150
151         private Expression ReduceConstantTypeEqual() {
152             ConstantExpression ce = Expression as ConstantExpression;
153             //TypeEqual(null, T) always returns false.
154             if (ce.Value == null) {
155                 return Expression.Constant(false);
156             } else {
157                 return Expression.Constant(_typeOperand.GetNonNullableType() == ce.Value.GetType());
158             }
159         }
160
161         #endregion
162
163         /// <summary>
164         /// Dispatches to the specific visit method for this node type.
165         /// </summary>
166         protected internal override Expression Accept(ExpressionVisitor visitor) {
167             return visitor.VisitTypeBinary(this);
168         }
169
170         /// <summary>
171         /// Creates a new expression that is like this one, but using the
172         /// supplied children. If all of the children are the same, it will
173         /// return this expression.
174         /// </summary>
175         /// <param name="expression">The <see cref="Expression" /> property of the result.</param>
176         /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
177         public TypeBinaryExpression Update(Expression expression) {
178             if (expression == Expression) {
179                 return this;
180             }
181             if (NodeType == ExpressionType.TypeIs) {
182                 return Expression.TypeIs(expression, TypeOperand);
183             }
184             return Expression.TypeEqual(expression, TypeOperand);
185         }
186     }
187
188     public partial class Expression {
189         /// <summary>
190         /// Creates a <see cref="TypeBinaryExpression"/>.
191         /// </summary>
192         /// <param name="expression">An <see cref="Expression"/> to set the <see cref="Expression"/> property equal to.</param>
193         /// <param name="type">A <see cref="Type"/> to set the <see cref="TypeBinaryExpression.TypeOperand"/> property equal to.</param>
194         /// <returns>A <see cref="TypeBinaryExpression"/> for which the <see cref="NodeType"/> property is equal to <see cref="TypeIs"/> and for which the <see cref="Expression"/> and <see cref="TypeBinaryExpression.TypeOperand"/> properties are set to the specified values.</returns>
195         public static TypeBinaryExpression TypeIs(Expression expression, Type type) {
196             RequiresCanRead(expression, "expression");
197             ContractUtils.RequiresNotNull(type, "type");
198             if (type.IsByRef) throw Error.TypeMustNotBeByRef();
199
200             return new TypeBinaryExpression(expression, type, ExpressionType.TypeIs);
201         }
202
203         /// <summary>
204         /// Creates a <see cref="TypeBinaryExpression"/> that compares run-time type identity.
205         /// </summary>
206         /// <param name="expression">An <see cref="Expression"/> to set the <see cref="Expression"/> property equal to.</param>
207         /// <param name="type">A <see cref="Type"/> to set the <see cref="TypeBinaryExpression.TypeOperand"/> property equal to.</param>
208         /// <returns>A <see cref="TypeBinaryExpression"/> for which the <see cref="NodeType"/> property is equal to <see cref="TypeEqual"/> and for which the <see cref="Expression"/> and <see cref="TypeBinaryExpression.TypeOperand"/> properties are set to the specified values.</returns>
209         public static TypeBinaryExpression TypeEqual(Expression expression, Type type) {
210             RequiresCanRead(expression, "expression");
211             ContractUtils.RequiresNotNull(type, "type");
212             if (type.IsByRef) throw Error.TypeMustNotBeByRef();
213
214             return new TypeBinaryExpression(expression, type, ExpressionType.TypeEqual);
215         }
216     }
217 }