e7e9dc172542250e671c1dcb931cb339164a7f0d
[mono.git] / mcs / class / referencesource / System.Core / Microsoft / Scripting / Compiler / LambdaCompiler.Unary.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 using System.Reflection;
20 using System.Reflection.Emit;
21 using System.Runtime.CompilerServices;
22
23 #if SILVERLIGHT
24 using System.Core;
25 #endif
26
27 #if CLR2
28 namespace Microsoft.Scripting.Ast.Compiler {
29 #else
30 namespace System.Linq.Expressions.Compiler {
31 #endif
32     partial class LambdaCompiler {
33
34         private void EmitQuoteUnaryExpression(Expression expr) {
35             EmitQuote((UnaryExpression)expr);
36         }
37
38
39         private void EmitQuote(UnaryExpression quote) {
40             // emit the quoted expression as a runtime constant
41             EmitConstant(quote.Operand, quote.Type);
42
43             // Heuristic: only emit the tree rewrite logic if we have hoisted
44             // locals.
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"));
50
51                 if (quote.Type != typeof(Expression)) {
52                     _ilg.Emit(OpCodes.Castclass, quote.Type);
53                 }
54             }
55         }
56
57         private void EmitThrowUnaryExpression(Expression expr) {
58             EmitThrow((UnaryExpression)expr, CompilationFlags.EmitAsDefaultType);
59         }
60
61         private void EmitThrow(UnaryExpression expr, CompilationFlags flags) {
62             if (expr.Operand == null) {
63                 CheckRethrow();
64
65                 _ilg.Emit(OpCodes.Rethrow);
66             } else {
67                 EmitExpression(expr.Operand);
68                 _ilg.Emit(OpCodes.Throw);
69             }
70
71             EmitUnreachable(expr, flags);
72         }
73
74         private void EmitUnaryExpression(Expression expr, CompilationFlags flags) {
75             EmitUnary((UnaryExpression)expr, flags);
76         }
77
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);
85                 _ilg.EmitInt(0);
86                 _ilg.EmitConvertToType(typeof(int), node.Operand.Type, false);
87                 _ilg.Emit(OpCodes.Ldloc, loc);
88                 FreeLocal(loc);
89                 EmitBinaryOperator(ExpressionType.SubtractChecked, node.Operand.Type, node.Operand.Type, node.Type, false);
90             } else {
91                 EmitExpression(node.Operand);
92                 EmitUnaryOperator(node.NodeType, node.Operand.Type, node.Type);
93             }
94         }
95
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);
99
100             if (op == ExpressionType.ArrayLength) {
101                 _ilg.Emit(OpCodes.Ldlen);
102                 return;
103             }
104
105             if (operandIsNullable) {
106                 switch (op) {
107                     case ExpressionType.Not: {
108                             if (operandType != typeof(bool?)) {
109                                 goto case ExpressionType.Negate;
110                             }
111                             Label labEnd = _ilg.DefineLabel();
112                             LocalBuilder loc = GetLocal(operandType);
113
114                             // store values (reverse order since they are already on the stack)
115                             _ilg.Emit(OpCodes.Stloc, loc);
116
117                             // test for null
118                             _ilg.Emit(OpCodes.Ldloca, loc);
119                             _ilg.EmitHasValue(operandType);
120                             _ilg.Emit(OpCodes.Brfalse_S, labEnd);
121
122                             // do op on non-null value
123                             _ilg.Emit(OpCodes.Ldloca, loc);
124                             _ilg.EmitGetValueOrDefault(operandType);
125                             Type nnOperandType = TypeUtils.GetNonNullableType(operandType);
126                             EmitUnaryOperator(op, nnOperandType, typeof(bool));
127
128                             // construct result
129                             ConstructorInfo ci = resultType.GetConstructor(new Type[] { typeof(bool) });
130                             _ilg.Emit(OpCodes.Newobj, ci);
131                             _ilg.Emit(OpCodes.Stloc, loc);
132
133                             _ilg.MarkLabel(labEnd);
134                             _ilg.Emit(OpCodes.Ldloc, loc);
135                             FreeLocal(loc);
136                             return;
137                         }
138                     case ExpressionType.UnaryPlus:
139                     case ExpressionType.NegateChecked:
140                     case ExpressionType.Negate:
141                     case ExpressionType.Increment:
142                     case ExpressionType.Decrement:
143                     case ExpressionType.OnesComplement:
144                     case ExpressionType.IsFalse:
145                     case ExpressionType.IsTrue: {
146                             Debug.Assert(TypeUtils.AreEquivalent(operandType, resultType));
147                             Label labIfNull = _ilg.DefineLabel();
148                             Label labEnd = _ilg.DefineLabel();
149                             LocalBuilder loc = GetLocal(operandType);
150
151                             // check for null
152                             _ilg.Emit(OpCodes.Stloc, loc);
153                             _ilg.Emit(OpCodes.Ldloca, loc);
154                             _ilg.EmitHasValue(operandType);
155                             _ilg.Emit(OpCodes.Brfalse_S, labIfNull);
156
157                             // apply operator to non-null value
158                             _ilg.Emit(OpCodes.Ldloca, loc);
159                             _ilg.EmitGetValueOrDefault(operandType);
160                             Type nnOperandType = TypeUtils.GetNonNullableType(resultType);
161                             EmitUnaryOperator(op, nnOperandType, nnOperandType);
162
163                             // construct result
164                             ConstructorInfo ci = resultType.GetConstructor(new Type[] { nnOperandType });
165                             _ilg.Emit(OpCodes.Newobj, ci);
166                             _ilg.Emit(OpCodes.Stloc, loc);
167                             _ilg.Emit(OpCodes.Br_S, labEnd);
168
169                             // if null then create a default one
170                             _ilg.MarkLabel(labIfNull);
171                             _ilg.Emit(OpCodes.Ldloca, loc);
172                             _ilg.Emit(OpCodes.Initobj, resultType);
173
174                             _ilg.MarkLabel(labEnd);
175                             _ilg.Emit(OpCodes.Ldloc, loc);
176                             FreeLocal(loc);
177                             return;
178                         }
179                     case ExpressionType.TypeAs:
180                         _ilg.Emit(OpCodes.Box, operandType);
181                         _ilg.Emit(OpCodes.Isinst, resultType);
182                         if (TypeUtils.IsNullableType(resultType)) {
183                             _ilg.Emit(OpCodes.Unbox_Any, resultType);
184                         }
185                         return;
186                     default:
187                         throw Error.UnhandledUnary(op);
188                 }
189             } else {
190                 switch (op) {
191                     case ExpressionType.Not:
192                         if (operandType == typeof(bool)) {
193                             _ilg.Emit(OpCodes.Ldc_I4_0);
194                             _ilg.Emit(OpCodes.Ceq);
195                         } else {
196                             _ilg.Emit(OpCodes.Not);
197                         }
198                         break;
199                     case ExpressionType.OnesComplement:
200                         _ilg.Emit(OpCodes.Not);
201                         break;
202                     case ExpressionType.IsFalse:
203                         _ilg.Emit(OpCodes.Ldc_I4_0);
204                         _ilg.Emit(OpCodes.Ceq);
205                         // Not an arithmetic operation -> no conversion
206                         return;
207                     case ExpressionType.IsTrue:
208                         _ilg.Emit(OpCodes.Ldc_I4_1);
209                         _ilg.Emit(OpCodes.Ceq);
210                         // Not an arithmetic operation -> no conversion
211                         return;
212                     case ExpressionType.UnaryPlus:
213                         _ilg.Emit(OpCodes.Nop);
214                         break;
215                     case ExpressionType.Negate:
216                     case ExpressionType.NegateChecked:
217                         _ilg.Emit(OpCodes.Neg);
218                         break;
219                     case ExpressionType.TypeAs:
220                         if (operandType.IsValueType) {
221                             _ilg.Emit(OpCodes.Box, operandType);
222                         }
223                         _ilg.Emit(OpCodes.Isinst, resultType);
224                         if (TypeUtils.IsNullableType(resultType)) {
225                             _ilg.Emit(OpCodes.Unbox_Any, resultType);
226                         }
227                         // Not an arithmetic operation -> no conversion
228                         return;
229                     case ExpressionType.Increment:
230                         EmitConstantOne(resultType);
231                         _ilg.Emit(OpCodes.Add);
232                         break;
233                     case ExpressionType.Decrement:
234                         EmitConstantOne(resultType);
235                         _ilg.Emit(OpCodes.Sub);
236                         break;
237                     default:
238                         throw Error.UnhandledUnary(op);
239                 }
240
241                 EmitConvertArithmeticResult(op, resultType);
242             }
243         }
244
245         private void EmitConstantOne(Type type) {
246             switch (Type.GetTypeCode(type)) {
247                 case TypeCode.UInt16:
248                 case TypeCode.UInt32:
249                 case TypeCode.Int16:
250                 case TypeCode.Int32:
251                     _ilg.Emit(OpCodes.Ldc_I4_1);
252                     break;
253                 case TypeCode.Int64:
254                 case TypeCode.UInt64:
255                     _ilg.Emit(OpCodes.Ldc_I8, (long)1);
256                     break;
257                 case TypeCode.Single:
258                     _ilg.Emit(OpCodes.Ldc_R4, 1.0f);
259                     break;
260                 case TypeCode.Double:
261                     _ilg.Emit(OpCodes.Ldc_R8, 1.0d);
262                     break;
263                 default:
264                     // we only have to worry about aritmetic types, see
265                     // TypeUtils.IsArithmetic
266                     throw ContractUtils.Unreachable;
267             }
268         }
269
270         private void EmitUnboxUnaryExpression(Expression expr) {
271             var node = (UnaryExpression)expr;
272             Debug.Assert(node.Type.IsValueType && !TypeUtils.IsNullableType(node.Type));
273
274             // Unbox_Any leaves the value on the stack
275             EmitExpression(node.Operand);
276             _ilg.Emit(OpCodes.Unbox_Any, node.Type);
277         }
278
279         private void EmitConvertUnaryExpression(Expression expr, CompilationFlags flags) {
280             EmitConvert((UnaryExpression)expr, flags);
281         }
282
283         private void EmitConvert(UnaryExpression node, CompilationFlags flags) {
284             if (node.Method != null) {
285                 // User-defined conversions are only lifted if both source and
286                 // destination types are value types.  The C# compiler gets this wrong.
287                 // In C#, if you have an implicit conversion from int->MyClass and you
288                 // "lift" the conversion to int?->MyClass then a null int? goes to a
289                 // null MyClass.  This is contrary to the specification, which states
290                 // that the correct behaviour is to unwrap the int?, throw an exception
291                 // if it is null, and then call the conversion.
292                 //
293                 // We cannot fix this in C# but there is no reason why we need to
294                 // propagate this 
295
296
297
298
299
300
301                 if (node.IsLifted && (!node.Type.IsValueType || !node.Operand.Type.IsValueType)) {
302                     ParameterInfo[] pis = node.Method.GetParametersCached();
303                     Debug.Assert(pis != null && pis.Length == 1);
304                     Type paramType = pis[0].ParameterType;
305                     if (paramType.IsByRef) {
306                         paramType = paramType.GetElementType();
307                     }
308
309                     UnaryExpression e = Expression.Convert(
310                         Expression.Call(
311                             node.Method,
312                             Expression.Convert(node.Operand, pis[0].ParameterType)
313                         ),
314                         node.Type
315                     );
316
317                     EmitConvert(e, flags);
318                 } else {
319                     EmitUnaryMethod(node, flags);
320                 }
321             } else if (node.Type == typeof(void)) {
322                 EmitExpressionAsVoid(node.Operand, flags);
323             } else {
324                 if (TypeUtils.AreEquivalent(node.Operand.Type, node.Type)) {
325                     EmitExpression(node.Operand, flags);
326                 } else {
327                     // A conversion is emitted after emitting the operand, no tail call is emitted
328                     EmitExpression(node.Operand);
329                     _ilg.EmitConvertToType(node.Operand.Type, node.Type, node.NodeType == ExpressionType.ConvertChecked);
330                 }
331             }
332         }
333
334
335         private void EmitUnaryMethod(UnaryExpression node, CompilationFlags flags) {
336             if (node.IsLifted) {
337                 ParameterExpression v = Expression.Variable(TypeUtils.GetNonNullableType(node.Operand.Type), null);
338                 MethodCallExpression mc = Expression.Call(node.Method, v);
339
340                 Type resultType = TypeUtils.GetNullableType(mc.Type);
341                 EmitLift(node.NodeType, resultType, mc, new ParameterExpression[] { v }, new Expression[] { node.Operand });
342                 _ilg.EmitConvertToType(resultType, node.Type, false);
343             } else {
344                 EmitMethodCallExpression(Expression.Call(node.Method, node.Operand), flags);
345             }
346         }
347     }
348 }