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