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