1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
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.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using System.Diagnostics;
18 using System.Dynamic.Utils;
25 namespace Microsoft.Scripting.Ast {
27 namespace System.Linq.Expressions {
30 /// Represents an operation between an expression and a type.
33 [DebuggerTypeProxy(typeof(Expression.TypeBinaryExpressionProxy))]
35 public sealed class TypeBinaryExpression : Expression {
36 private readonly Expression _expression;
37 private readonly Type _typeOperand;
38 private readonly ExpressionType _nodeKind;
40 internal TypeBinaryExpression(Expression expression, Type typeOperand, ExpressionType nodeKind) {
41 _expression = expression;
42 _typeOperand = typeOperand;
47 /// Gets the static type of the expression that this <see cref="Expression" /> represents.
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); }
55 /// Returns the node type of this Expression. Extension nodes should return
56 /// ExpressionType.Extension when overriding this method.
58 /// <returns>The <see cref="ExpressionType"/> of the expression.</returns>
59 public sealed override ExpressionType NodeType {
60 get { return _nodeKind; }
64 /// Gets the expression operand of a type test operation.
66 public Expression Expression {
67 get { return _expression; }
71 /// Gets the type operand of a type test operation.
73 public Type TypeOperand {
74 get { return _typeOperand; }
77 #region Reduce TypeEqual
79 internal Expression ReduceTypeEqual() {
80 Type cType = Expression.Type;
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()));
88 // Can check the value right now for constants.
89 if (Expression.NodeType == ExpressionType.Constant) {
90 return ReduceConstantTypeEqual();
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));
99 return Expression.ReferenceNotEqual(Expression, Expression.Constant(null, Expression.Type));
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);
109 // Create a temp so we only evaluate the left side once
110 parameter = Expression.Parameter(typeof(object));
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));
118 return Expression.Block(
120 Expression.Assign(parameter, expression),
121 ByValParameterTypeEqual(parameter)
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"));
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);
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(
146 Expression.Constant(_typeOperand.GetNonNullableType(), typeof(Type))
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);
157 return Expression.Constant(_typeOperand.GetNonNullableType() == ce.Value.GetType());
164 /// Dispatches to the specific visit method for this node type.
166 protected internal override Expression Accept(ExpressionVisitor visitor) {
167 return visitor.VisitTypeBinary(this);
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.
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) {
181 if (NodeType == ExpressionType.TypeIs) {
182 return Expression.TypeIs(expression, TypeOperand);
184 return Expression.TypeEqual(expression, TypeOperand);
188 public partial class Expression {
190 /// Creates a <see cref="TypeBinaryExpression"/>.
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();
200 return new TypeBinaryExpression(expression, type, ExpressionType.TypeIs);
204 /// Creates a <see cref="TypeBinaryExpression"/> that compares run-time type identity.
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();
214 return new TypeBinaryExpression(expression, type, ExpressionType.TypeEqual);