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 * ***************************************************************************/
17 using System.Diagnostics;
18 using System.Dynamic.Utils;
19 using System.Reflection;
20 using System.Reflection.Emit;
21 using System.Runtime.CompilerServices;
28 namespace Microsoft.Scripting.Ast.Compiler {
30 namespace System.Linq.Expressions.Compiler {
32 partial class LambdaCompiler {
34 private void EmitQuoteUnaryExpression(Expression expr) {
35 EmitQuote((UnaryExpression)expr);
39 private void EmitQuote(UnaryExpression quote) {
40 // emit the quoted expression as a runtime constant
41 EmitConstant(quote.Operand, quote.Type);
43 // Heuristic: only emit the tree rewrite logic if we have hoisted
45 if (_scope.NearestHoistedLocals != null) {
46 // HoistedLocals is internal so emit as System.Object
47 EmitConstant(_scope.NearestHoistedLocals, typeof(object));
48 _scope.EmitGet(_scope.NearestHoistedLocals.SelfVariable);
49 _ilg.Emit(OpCodes.Call, typeof(RuntimeOps).GetMethod("Quote"));
51 if (quote.Type != typeof(Expression)) {
52 _ilg.Emit(OpCodes.Castclass, quote.Type);
57 private void EmitThrowUnaryExpression(Expression expr) {
58 EmitThrow((UnaryExpression)expr, CompilationFlags.EmitAsDefaultType);
61 private void EmitThrow(UnaryExpression expr, CompilationFlags flags) {
62 if (expr.Operand == null) {
65 _ilg.Emit(OpCodes.Rethrow);
67 EmitExpression(expr.Operand);
68 _ilg.Emit(OpCodes.Throw);
71 EmitUnreachable(expr, flags);
74 private void EmitUnaryExpression(Expression expr, CompilationFlags flags) {
75 EmitUnary((UnaryExpression)expr, flags);
78 private void EmitUnary(UnaryExpression node, CompilationFlags flags) {
79 if (node.Method != null) {
80 EmitUnaryMethod(node, flags);
81 } else if (node.NodeType == ExpressionType.NegateChecked && TypeUtils.IsInteger(node.Operand.Type)) {
82 EmitExpression(node.Operand);
83 LocalBuilder loc = GetLocal(node.Operand.Type);
84 _ilg.Emit(OpCodes.Stloc, loc);
86 _ilg.EmitConvertToType(typeof(int), node.Operand.Type, false);
87 _ilg.Emit(OpCodes.Ldloc, loc);
89 EmitBinaryOperator(ExpressionType.SubtractChecked, node.Operand.Type, node.Operand.Type, node.Type, false);
91 EmitExpression(node.Operand);
92 EmitUnaryOperator(node.NodeType, node.Operand.Type, node.Type);
96 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
97 private void EmitUnaryOperator(ExpressionType op, Type operandType, Type resultType) {
98 bool operandIsNullable = TypeUtils.IsNullableType(operandType);
100 if (op == ExpressionType.ArrayLength) {
101 _ilg.Emit(OpCodes.Ldlen);
105 if (operandIsNullable) {
107 case ExpressionType.Not: {
108 if (operandType != typeof(bool?))
109 goto case ExpressionType.Negate;
111 Label labIfNull = _ilg.DefineLabel();
112 Label labEnd = _ilg.DefineLabel();
113 LocalBuilder loc = GetLocal(operandType);
115 // store values (reverse order since they are already on the stack)
116 _ilg.Emit(OpCodes.Stloc, loc);
119 _ilg.Emit(OpCodes.Ldloca, loc);
120 _ilg.EmitHasValue(operandType);
121 _ilg.Emit(OpCodes.Brfalse_S, labEnd);
123 // do op on non-null value
124 _ilg.Emit(OpCodes.Ldloca, loc);
125 _ilg.EmitGetValueOrDefault(operandType);
126 Type nnOperandType = TypeUtils.GetNonNullableType(operandType);
127 EmitUnaryOperator(op, nnOperandType, typeof(bool));
130 ConstructorInfo ci = resultType.GetConstructor(new Type[] { typeof(bool) });
131 _ilg.Emit(OpCodes.Newobj, ci);
132 _ilg.Emit(OpCodes.Stloc, loc);
134 _ilg.MarkLabel(labEnd);
135 _ilg.Emit(OpCodes.Ldloc, loc);
139 case ExpressionType.UnaryPlus:
140 case ExpressionType.NegateChecked:
141 case ExpressionType.Negate:
142 case ExpressionType.Increment:
143 case ExpressionType.Decrement:
144 case ExpressionType.OnesComplement:
145 case ExpressionType.IsFalse:
146 case ExpressionType.IsTrue: {
147 Debug.Assert(TypeUtils.AreEquivalent(operandType, resultType));
148 Label labIfNull = _ilg.DefineLabel();
149 Label labEnd = _ilg.DefineLabel();
150 LocalBuilder loc = GetLocal(operandType);
153 _ilg.Emit(OpCodes.Stloc, loc);
154 _ilg.Emit(OpCodes.Ldloca, loc);
155 _ilg.EmitHasValue(operandType);
156 _ilg.Emit(OpCodes.Brfalse_S, labIfNull);
158 // apply operator to non-null value
159 _ilg.Emit(OpCodes.Ldloca, loc);
160 _ilg.EmitGetValueOrDefault(operandType);
161 Type nnOperandType = TypeUtils.GetNonNullableType(resultType);
162 EmitUnaryOperator(op, nnOperandType, nnOperandType);
165 ConstructorInfo ci = resultType.GetConstructor(new Type[] { nnOperandType });
166 _ilg.Emit(OpCodes.Newobj, ci);
167 _ilg.Emit(OpCodes.Stloc, loc);
168 _ilg.Emit(OpCodes.Br_S, labEnd);
170 // if null then create a default one
171 _ilg.MarkLabel(labIfNull);
172 _ilg.Emit(OpCodes.Ldloca, loc);
173 _ilg.Emit(OpCodes.Initobj, resultType);
175 _ilg.MarkLabel(labEnd);
176 _ilg.Emit(OpCodes.Ldloc, loc);
180 case ExpressionType.TypeAs:
181 _ilg.Emit(OpCodes.Box, operandType);
182 _ilg.Emit(OpCodes.Isinst, resultType);
183 if (TypeUtils.IsNullableType(resultType)) {
184 _ilg.Emit(OpCodes.Unbox_Any, resultType);
188 throw Error.UnhandledUnary(op);
192 case ExpressionType.Not:
193 if (operandType == typeof(bool)) {
194 _ilg.Emit(OpCodes.Ldc_I4_0);
195 _ilg.Emit(OpCodes.Ceq);
197 _ilg.Emit(OpCodes.Not);
200 case ExpressionType.OnesComplement:
201 _ilg.Emit(OpCodes.Not);
203 case ExpressionType.IsFalse:
204 _ilg.Emit(OpCodes.Ldc_I4_0);
205 _ilg.Emit(OpCodes.Ceq);
206 // Not an arithmetic operation -> no conversion
208 case ExpressionType.IsTrue:
209 _ilg.Emit(OpCodes.Ldc_I4_1);
210 _ilg.Emit(OpCodes.Ceq);
211 // Not an arithmetic operation -> no conversion
213 case ExpressionType.UnaryPlus:
214 _ilg.Emit(OpCodes.Nop);
216 case ExpressionType.Negate:
217 case ExpressionType.NegateChecked:
218 _ilg.Emit(OpCodes.Neg);
220 case ExpressionType.TypeAs:
221 if (operandType.IsValueType) {
222 _ilg.Emit(OpCodes.Box, operandType);
224 _ilg.Emit(OpCodes.Isinst, resultType);
225 if (TypeUtils.IsNullableType(resultType)) {
226 _ilg.Emit(OpCodes.Unbox_Any, resultType);
228 // Not an arithmetic operation -> no conversion
230 case ExpressionType.Increment:
231 EmitConstantOne(resultType);
232 _ilg.Emit(OpCodes.Add);
234 case ExpressionType.Decrement:
235 EmitConstantOne(resultType);
236 _ilg.Emit(OpCodes.Sub);
239 throw Error.UnhandledUnary(op);
242 EmitConvertArithmeticResult(op, resultType);
246 private void EmitConstantOne(Type type) {
247 switch (Type.GetTypeCode(type)) {
248 case TypeCode.UInt16:
249 case TypeCode.UInt32:
252 _ilg.Emit(OpCodes.Ldc_I4_1);
255 case TypeCode.UInt64:
256 _ilg.Emit(OpCodes.Ldc_I8, (long)1);
258 case TypeCode.Single:
259 _ilg.Emit(OpCodes.Ldc_R4, 1.0f);
261 case TypeCode.Double:
262 _ilg.Emit(OpCodes.Ldc_R8, 1.0d);
265 // we only have to worry about aritmetic types, see
266 // TypeUtils.IsArithmetic
267 throw ContractUtils.Unreachable;
271 private void EmitUnboxUnaryExpression(Expression expr) {
272 var node = (UnaryExpression)expr;
273 Debug.Assert(node.Type.IsValueType && !TypeUtils.IsNullableType(node.Type));
275 // Unbox_Any leaves the value on the stack
276 EmitExpression(node.Operand);
277 _ilg.Emit(OpCodes.Unbox_Any, node.Type);
280 private void EmitConvertUnaryExpression(Expression expr, CompilationFlags flags) {
281 EmitConvert((UnaryExpression)expr, flags);
284 private void EmitConvert(UnaryExpression node, CompilationFlags flags) {
285 if (node.Method != null) {
286 // User-defined conversions are only lifted if both source and
287 // destination types are value types. The C# compiler gets this wrong.
288 // In C#, if you have an implicit conversion from int->MyClass and you
289 // "lift" the conversion to int?->MyClass then a null int? goes to a
290 // null MyClass. This is contrary to the specification, which states
291 // that the correct behaviour is to unwrap the int?, throw an exception
292 // if it is null, and then call the conversion.
294 // We cannot fix this in C# but there is no reason why we need to
295 // propagate this bug into the expression tree API. Unfortunately
296 // this means that when the C# compiler generates the lambda
297 // (int? i)=>(MyClass)i, we will get different results for converting
298 // that lambda to a delegate directly and converting that lambda to
299 // an expression tree and then compiling it. We can live with this
300 // discrepancy however.
302 if (node.IsLifted && (!node.Type.IsValueType || !node.Operand.Type.IsValueType)) {
303 ParameterInfo[] pis = node.Method.GetParametersCached();
304 Debug.Assert(pis != null && pis.Length == 1);
305 Type paramType = pis[0].ParameterType;
306 if (paramType.IsByRef) {
307 paramType = paramType.GetElementType();
310 UnaryExpression e = Expression.Convert(
313 Expression.Convert(node.Operand, pis[0].ParameterType)
318 EmitConvert(e, flags);
320 EmitUnaryMethod(node, flags);
322 } else if (node.Type == typeof(void)) {
323 EmitExpressionAsVoid(node.Operand, flags);
325 if (TypeUtils.AreEquivalent(node.Operand.Type, node.Type)) {
326 EmitExpression(node.Operand, flags);
328 // A conversion is emitted after emitting the operand, no tail call is emitted
329 EmitExpression(node.Operand);
330 _ilg.EmitConvertToType(node.Operand.Type, node.Type, node.NodeType == ExpressionType.ConvertChecked);
336 private void EmitUnaryMethod(UnaryExpression node, CompilationFlags flags) {
338 ParameterExpression v = Expression.Variable(TypeUtils.GetNonNullableType(node.Operand.Type), null);
339 MethodCallExpression mc = Expression.Call(node.Method, v);
341 Type resultType = TypeUtils.GetNullableType(mc.Type);
342 EmitLift(node.NodeType, resultType, mc, new ParameterExpression[] { v }, new Expression[] { node.Operand });
343 _ilg.EmitConvertToType(resultType, node.Type, false);
345 EmitMethodCallExpression(Expression.Call(node.Method, node.Operand), flags);