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