Facilitate the merge
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Compiler / LambdaCompiler.Unary.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
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.
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 labIfNull = _ilg.DefineLabel();
112                             Label labEnd = _ilg.DefineLabel();
113                             LocalBuilder loc = GetLocal(operandType);
114
115                             // store values (reverse order since they are already on the stack)
116                             _ilg.Emit(OpCodes.Stloc, loc);
117
118                             // test for null
119                             _ilg.Emit(OpCodes.Ldloca, loc);
120                             _ilg.EmitHasValue(operandType);
121                             _ilg.Emit(OpCodes.Brfalse_S, labEnd);
122
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));
128
129                             // construct result
130                             ConstructorInfo ci = resultType.GetConstructor(new Type[] { typeof(bool) });
131                             _ilg.Emit(OpCodes.Newobj, ci);
132                             _ilg.Emit(OpCodes.Stloc, loc);
133
134                             _ilg.MarkLabel(labEnd);
135                             _ilg.Emit(OpCodes.Ldloc, loc);
136                             FreeLocal(loc);
137                             return;
138                         }
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);
151
152                             // check for null
153                             _ilg.Emit(OpCodes.Stloc, loc);
154                             _ilg.Emit(OpCodes.Ldloca, loc);
155                             _ilg.EmitHasValue(operandType);
156                             _ilg.Emit(OpCodes.Brfalse_S, labIfNull);
157
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);
163
164                             // construct result
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);
169
170                             // if null then create a default one
171                             _ilg.MarkLabel(labIfNull);
172                             _ilg.Emit(OpCodes.Ldloca, loc);
173                             _ilg.Emit(OpCodes.Initobj, resultType);
174
175                             _ilg.MarkLabel(labEnd);
176                             _ilg.Emit(OpCodes.Ldloc, loc);
177                             FreeLocal(loc);
178                             return;
179                         }
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);
185                         }
186                         return;
187                     default:
188                         throw Error.UnhandledUnary(op);
189                 }
190             } else {
191                 switch (op) {
192                     case ExpressionType.Not:
193                         if (operandType == typeof(bool)) {
194                             _ilg.Emit(OpCodes.Ldc_I4_0);
195                             _ilg.Emit(OpCodes.Ceq);
196                         } else {
197                             _ilg.Emit(OpCodes.Not);
198                         }
199                         break;
200                     case ExpressionType.OnesComplement:
201                         _ilg.Emit(OpCodes.Not);
202                         break;
203                     case ExpressionType.IsFalse:
204                         _ilg.Emit(OpCodes.Ldc_I4_0);
205                         _ilg.Emit(OpCodes.Ceq);
206                         // Not an arithmetic operation -> no conversion
207                         return;
208                     case ExpressionType.IsTrue:
209                         _ilg.Emit(OpCodes.Ldc_I4_1);
210                         _ilg.Emit(OpCodes.Ceq);
211                         // Not an arithmetic operation -> no conversion
212                         return;
213                     case ExpressionType.UnaryPlus:
214                         _ilg.Emit(OpCodes.Nop);
215                         break;
216                     case ExpressionType.Negate:
217                     case ExpressionType.NegateChecked:
218                         _ilg.Emit(OpCodes.Neg);
219                         break;
220                     case ExpressionType.TypeAs:
221                         if (operandType.IsValueType) {
222                             _ilg.Emit(OpCodes.Box, operandType);
223                         }
224                         _ilg.Emit(OpCodes.Isinst, resultType);
225                         if (TypeUtils.IsNullableType(resultType)) {
226                             _ilg.Emit(OpCodes.Unbox_Any, resultType);
227                         }
228                         // Not an arithmetic operation -> no conversion
229                         return;
230                     case ExpressionType.Increment:
231                         EmitConstantOne(resultType);
232                         _ilg.Emit(OpCodes.Add);
233                         break;
234                     case ExpressionType.Decrement:
235                         EmitConstantOne(resultType);
236                         _ilg.Emit(OpCodes.Sub);
237                         break;
238                     default:
239                         throw Error.UnhandledUnary(op);
240                 }
241
242                 EmitConvertArithmeticResult(op, resultType);
243             }
244         }
245
246         private void EmitConstantOne(Type type) {
247             switch (Type.GetTypeCode(type)) {
248                 case TypeCode.UInt16:
249                 case TypeCode.UInt32:
250                 case TypeCode.Int16:
251                 case TypeCode.Int32:
252                     _ilg.Emit(OpCodes.Ldc_I4_1);
253                     break;
254                 case TypeCode.Int64:
255                 case TypeCode.UInt64:
256                     _ilg.Emit(OpCodes.Ldc_I8, (long)1);
257                     break;
258                 case TypeCode.Single:
259                     _ilg.Emit(OpCodes.Ldc_R4, 1.0f);
260                     break;
261                 case TypeCode.Double:
262                     _ilg.Emit(OpCodes.Ldc_R8, 1.0d);
263                     break;
264                 default:
265                     // we only have to worry about aritmetic types, see
266                     // TypeUtils.IsArithmetic
267                     throw ContractUtils.Unreachable;
268             }
269         }
270
271         private void EmitUnboxUnaryExpression(Expression expr) {
272             var node = (UnaryExpression)expr;
273             Debug.Assert(node.Type.IsValueType && !TypeUtils.IsNullableType(node.Type));
274
275             // Unbox_Any leaves the value on the stack
276             EmitExpression(node.Operand);
277             _ilg.Emit(OpCodes.Unbox_Any, node.Type);
278         }
279
280         private void EmitConvertUnaryExpression(Expression expr, CompilationFlags flags) {
281             EmitConvert((UnaryExpression)expr, flags);
282         }
283
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.
293                 //
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.
301
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();
308                     }
309
310                     UnaryExpression e = Expression.Convert(
311                         Expression.Call(
312                             node.Method,
313                             Expression.Convert(node.Operand, pis[0].ParameterType)
314                         ),
315                         node.Type
316                     );
317
318                     EmitConvert(e, flags);
319                 } else {
320                     EmitUnaryMethod(node, flags);
321                 }
322             } else if (node.Type == typeof(void)) {
323                 EmitExpressionAsVoid(node.Operand, flags);
324             } else {
325                 if (TypeUtils.AreEquivalent(node.Operand.Type, node.Type)) {
326                     EmitExpression(node.Operand, flags);
327                 } else {
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);
331                 }
332             }
333         }
334
335
336         private void EmitUnaryMethod(UnaryExpression node, CompilationFlags flags) {
337             if (node.IsLifted) {
338                 ParameterExpression v = Expression.Variable(TypeUtils.GetNonNullableType(node.Operand.Type), null);
339                 MethodCallExpression mc = Expression.Call(node.Method, v);
340
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);
344             } else {
345                 EmitMethodCallExpression(Expression.Call(node.Method, node.Operand), flags);
346             }
347         }
348     }
349 }