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