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 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 TypeBinaryExpression(Expression expression, Type typeOperand, ExpressionType nodeKind)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 ReduceTypeEqual()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. ByValParameterTypeEqual(ParameterExpression value)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 ReduceConstantTypeEqual()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> Accept(ExpressionVisitor visitor)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> Update(Expression expression)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> TypeIs(Expression expression, Type type)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> TypeEqual(Expression expression, Type type)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 } 218