1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
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.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
15 using System; using Microsoft;
18 using System.Diagnostics;
20 using System.Dynamic.Utils;
22 using Microsoft.Scripting.Utils;
26 namespace System.Linq.Expressions {
28 namespace Microsoft.Linq.Expressions {
31 /// Represents an operation between an expression and a type.
34 [DebuggerTypeProxy(typeof(Expression.TypeBinaryExpressionProxy))]
36 public sealed class TypeBinaryExpression : Expression {
37 private readonly Expression _expression;
38 private readonly Type _typeOperand;
39 private readonly ExpressionType _nodeKind;
41 internal TypeBinaryExpression(Expression expression, Type typeOperand, ExpressionType nodeKind) {
42 _expression = expression;
43 _typeOperand = typeOperand;
48 /// Gets the static type of the expression that this <see cref="Expression" /> represents.
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); }
56 /// Returns the node type of this Expression. Extension nodes should return
57 /// ExpressionType.Extension when overriding this method.
59 /// <returns>The <see cref="ExpressionType"/> of the expression.</returns>
60 public sealed override ExpressionType NodeType {
61 get { return _nodeKind; }
65 /// Gets the expression operand of a type test operation.
67 public Expression Expression {
68 get { return _expression; }
72 /// Gets the type operand of a type test operation.
74 public Type TypeOperand {
75 get { return _typeOperand; }
78 #region Reduce TypeEqual
80 internal Expression ReduceTypeEqual() {
81 Type cType = Expression.Type;
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()));
89 // Can check the value right now for constants.
90 if (Expression.NodeType == ExpressionType.Constant) {
91 return ReduceConstantTypeEqual();
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));
100 return Expression.ReferenceNotEqual(Expression, Expression.Constant(null, Expression.Type));
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);
110 // Create a temp so we only evaluate the left side once
111 parameter = Expression.Parameter(typeof(object));
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));
119 return Expression.Block(
121 Expression.Assign(parameter, expression),
122 ByValParameterTypeEqual(parameter)
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"));
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);
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(
147 Expression.Constant(_typeOperand.GetNonNullableType(), typeof(Type))
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);
158 return Expression.Constant(_typeOperand.GetNonNullableType() == ce.Value.GetType());
164 internal override Expression Accept(ExpressionVisitor visitor) {
165 return visitor.VisitTypeBinary(this);
169 public partial class Expression {
171 /// Creates a <see cref="TypeBinaryExpression"/>.
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);
181 return new TypeBinaryExpression(expression, type, ExpressionType.TypeIs);
185 /// Creates a <see cref="TypeBinaryExpression"/> that compares run-time type identity.
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);
195 return new TypeBinaryExpression(expression, type, ExpressionType.TypeEqual);