Merge pull request #496 from nicolas-raoul/unit-test-for-issue2907
[mono.git] / mcs / class / System.Core / System.Linq.Expressions / UnaryExpression.cs
1 //
2 // UnaryExpression.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@novell.com)
6 //
7 // (C) 2008 Novell, Inc. (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Reflection;
31 using System.Reflection.Emit;
32
33 namespace System.Linq.Expressions {
34
35         public sealed class UnaryExpression : Expression {
36
37                 Expression operand;
38                 MethodInfo method;
39                 bool is_lifted;
40
41                 public Expression Operand {
42                         get { return operand; }
43                 }
44
45                 public MethodInfo Method {
46                         get { return method; }
47                 }
48
49                 public bool IsLifted {
50                         get { return is_lifted; }
51                 }
52
53                 public bool IsLiftedToNull {
54                         get { return is_lifted && this.Type.IsNullable (); }
55                 }
56
57                 internal UnaryExpression (ExpressionType node_type, Expression operand, Type type)
58                         : base (node_type, type)
59                 {
60                         this.operand = operand;
61                 }
62
63                 internal UnaryExpression (ExpressionType node_type, Expression operand, Type type, MethodInfo method, bool is_lifted)
64                         : base (node_type, type)
65                 {
66                         this.operand = operand;
67                         this.method = method;
68                         this.is_lifted = is_lifted;
69                 }
70
71 #if !FULL_AOT_RUNTIME
72                 void EmitArrayLength (EmitContext ec)
73                 {
74                         operand.Emit (ec);
75                         ec.ig.Emit (OpCodes.Ldlen);
76                 }
77
78                 void EmitTypeAs (EmitContext ec)
79                 {
80                         var type = this.Type;
81
82                         ec.EmitIsInst (operand, type);
83
84                         if (type.IsNullable ())
85                                 ec.ig.Emit (OpCodes.Unbox_Any, type);
86                 }
87
88                 void EmitLiftedUnary (EmitContext ec)
89                 {
90                         var ig = ec.ig;
91
92                         var from = ec.EmitStored (operand);
93                         var to = ig.DeclareLocal (Type);
94
95                         var has_value = ig.DefineLabel ();
96                         var done = ig.DefineLabel ();
97
98                         ec.EmitNullableHasValue (from);
99                         ig.Emit (OpCodes.Brtrue, has_value);
100
101                         // if not has value
102                         ec.EmitNullableInitialize (to);
103
104                         ig.Emit (OpCodes.Br, done);
105
106                         ig.MarkLabel (has_value);
107                         // if has value
108                         ec.EmitNullableGetValueOrDefault (from);
109
110                         EmitUnaryOperator (ec);
111
112                         ec.EmitNullableNew (Type);
113
114                         ig.MarkLabel (done);
115                 }
116
117                 void EmitUnaryOperator (EmitContext ec)
118                 {
119                         var ig = ec.ig;
120
121                         switch (NodeType) {
122                         case ExpressionType.Not:
123                                 if (operand.Type.GetNotNullableType () == typeof (bool)) {
124                                         ig.Emit (OpCodes.Ldc_I4_0);
125                                         ig.Emit (OpCodes.Ceq);
126                                 } else
127                                         ig.Emit (OpCodes.Not);
128                                 break;
129                         case ExpressionType.Negate:
130                                 ig.Emit (OpCodes.Neg);
131                                 break;
132                         case ExpressionType.NegateChecked:
133                                 ig.Emit (OpCodes.Ldc_I4_M1);
134                                 ig.Emit (IsUnsigned (operand.Type) ? OpCodes.Mul_Ovf_Un : OpCodes.Mul_Ovf);
135                                 break;
136                         case ExpressionType.Convert:
137                         case ExpressionType.ConvertChecked:
138                                 // Called when converting from nullable from nullable
139                                 EmitPrimitiveConversion (ec,
140                                         operand.Type.GetNotNullableType (),
141                                         Type.GetNotNullableType ());
142                                 break;
143                         }
144                 }
145
146                 void EmitConvert (EmitContext ec)
147                 {
148                         var from = operand.Type;
149                         var target = Type;
150
151                         if (from == target)
152                                 operand.Emit (ec);
153                         else if (from.IsNullable () && !target.IsNullable ())
154                                 EmitConvertFromNullable (ec);
155                         else if (!from.IsNullable () && target.IsNullable ())
156                                 EmitConvertToNullable (ec);
157                         else if (from.IsNullable () && target.IsNullable ())
158                                 EmitConvertFromNullableToNullable (ec);
159                         else if (IsReferenceConversion (from, target))
160                                 EmitCast (ec);
161                         else if (IsPrimitiveConversion (from, target))
162                                 EmitPrimitiveConversion (ec);
163                         else
164                                 throw new NotImplementedException ();
165                 }
166
167                 void EmitConvertFromNullableToNullable (EmitContext ec)
168                 {
169                         EmitLiftedUnary (ec);
170                 }
171
172                 void EmitConvertToNullable (EmitContext ec)
173                 {
174                         ec.Emit (operand);
175
176                         if (IsUnBoxing ()) {
177                                 EmitUnbox (ec);
178                                 return;
179                         }
180
181                         if (operand.Type != Type.GetNotNullableType ()) {
182                                 EmitPrimitiveConversion (ec,
183                                         operand.Type,
184                                         Type.GetNotNullableType ());
185                         }
186
187                         ec.EmitNullableNew (Type);
188                 }
189
190                 void EmitConvertFromNullable (EmitContext ec)
191                 {
192                         if (IsBoxing ()) {
193                                 ec.Emit (operand);
194                                 EmitBox (ec);
195                                 return;
196                         }
197
198                         ec.EmitCall (operand, operand.Type.GetMethod ("get_Value"));
199
200                         if (operand.Type.GetNotNullableType () != Type) {
201                                 EmitPrimitiveConversion (ec,
202                                         operand.Type.GetNotNullableType (),
203                                         Type);
204                         }
205                 }
206
207                 bool IsBoxing ()
208                 {
209                         return operand.Type.IsValueType && !Type.IsValueType;
210                 }
211
212                 void EmitBox (EmitContext ec)
213                 {
214                         ec.ig.Emit (OpCodes.Box, operand.Type);
215                 }
216
217                 bool IsUnBoxing ()
218                 {
219                         return !operand.Type.IsValueType && Type.IsValueType;
220                 }
221
222                 void EmitUnbox (EmitContext ec)
223                 {
224                         ec.ig.Emit (OpCodes.Unbox_Any, Type);
225                 }
226
227                 void EmitCast (EmitContext ec)
228                 {
229                         operand.Emit (ec);
230
231                         if (IsBoxing ()) {
232                                 EmitBox (ec);
233                         } else if (IsUnBoxing ()) {
234                                 EmitUnbox (ec);
235                         } else
236                                 ec.ig.Emit (OpCodes.Castclass, Type);
237                 }
238
239                 void EmitPrimitiveConversion (EmitContext ec, bool is_unsigned,
240                         OpCode signed, OpCode unsigned, OpCode signed_checked, OpCode unsigned_checked)
241                 {
242                         if (this.NodeType != ExpressionType.ConvertChecked)
243                                 ec.ig.Emit (is_unsigned ? unsigned : signed);
244                         else
245                                 ec.ig.Emit (is_unsigned ? unsigned_checked : signed_checked);
246                 }
247
248                 void EmitPrimitiveConversion (EmitContext ec)
249                 {
250                         operand.Emit (ec);
251
252                         EmitPrimitiveConversion (ec, operand.Type, Type);
253                 }
254
255                 void EmitPrimitiveConversion (EmitContext ec, Type from, Type to)
256                 {
257                         var is_unsigned = IsUnsigned (from);
258
259                         switch (Type.GetTypeCode (to)) {
260                         case TypeCode.SByte:
261                                 EmitPrimitiveConversion (ec,
262                                         is_unsigned,
263                                         OpCodes.Conv_I1,
264                                         OpCodes.Conv_U1,
265                                         OpCodes.Conv_Ovf_I1,
266                                         OpCodes.Conv_Ovf_I1_Un);
267                                 return;
268                         case TypeCode.Byte:
269                                 EmitPrimitiveConversion (ec,
270                                         is_unsigned,
271                                         OpCodes.Conv_I1,
272                                         OpCodes.Conv_U1,
273                                         OpCodes.Conv_Ovf_U1,
274                                         OpCodes.Conv_Ovf_U1_Un);
275                                 return;
276                         case TypeCode.Int16:
277                                 EmitPrimitiveConversion (ec,
278                                         is_unsigned,
279                                         OpCodes.Conv_I2,
280                                         OpCodes.Conv_U2,
281                                         OpCodes.Conv_Ovf_I2,
282                                         OpCodes.Conv_Ovf_I2_Un);
283                                 return;
284                         case TypeCode.UInt16:
285                                 EmitPrimitiveConversion (ec,
286                                         is_unsigned,
287                                         OpCodes.Conv_I2,
288                                         OpCodes.Conv_U2,
289                                         OpCodes.Conv_Ovf_U2,
290                                         OpCodes.Conv_Ovf_U2_Un);
291                                 return;
292                         case TypeCode.Int32:
293                                 EmitPrimitiveConversion (ec,
294                                         is_unsigned,
295                                         OpCodes.Conv_I4,
296                                         OpCodes.Conv_U4,
297                                         OpCodes.Conv_Ovf_I4,
298                                         OpCodes.Conv_Ovf_I4_Un);
299                                 return;
300                         case TypeCode.UInt32:
301                                 EmitPrimitiveConversion (ec,
302                                         is_unsigned,
303                                         OpCodes.Conv_I4,
304                                         OpCodes.Conv_U4,
305                                         OpCodes.Conv_Ovf_U4,
306                                         OpCodes.Conv_Ovf_U4_Un);
307                                 return;
308                         case TypeCode.Int64:
309                                 EmitPrimitiveConversion (ec,
310                                         is_unsigned,
311                                         OpCodes.Conv_I8,
312                                         OpCodes.Conv_U8,
313                                         OpCodes.Conv_Ovf_I8,
314                                         OpCodes.Conv_Ovf_I8_Un);
315                                 return;
316                         case TypeCode.UInt64:
317                                 EmitPrimitiveConversion (ec,
318                                         is_unsigned,
319                                         OpCodes.Conv_I8,
320                                         OpCodes.Conv_U8,
321                                         OpCodes.Conv_Ovf_U8,
322                                         OpCodes.Conv_Ovf_U8_Un);
323                                 return;
324                         case TypeCode.Single:
325                                 if (is_unsigned)
326                                         ec.ig.Emit (OpCodes.Conv_R_Un);
327                                 ec.ig.Emit (OpCodes.Conv_R4);
328                                 return;
329                         case TypeCode.Double:
330                                 if (is_unsigned)
331                                         ec.ig.Emit (OpCodes.Conv_R_Un);
332                                 ec.ig.Emit (OpCodes.Conv_R8);
333                                 return;
334                         default:
335                                 throw new NotImplementedException (this.Type.ToString ());
336                         }
337                 }
338
339                 void EmitArithmeticUnary (EmitContext ec)
340                 {
341                         if (!IsLifted) {
342                                 operand.Emit (ec);
343                                 EmitUnaryOperator (ec);
344                         } else
345                                 EmitLiftedUnary (ec);
346                 }
347
348                 void EmitUserDefinedLiftedToNullOperator (EmitContext ec)
349                 {
350                         var ig = ec.ig;
351                         var local = ec.EmitStored (operand);
352
353                         var ret = ig.DefineLabel ();
354                         var done = ig.DefineLabel ();
355
356                         ec.EmitNullableHasValue (local);
357                         ig.Emit (OpCodes.Brfalse, ret);
358
359                         ec.EmitNullableGetValueOrDefault (local);
360                         ec.EmitCall (method);
361                         ec.EmitNullableNew (Type);
362                         ig.Emit (OpCodes.Br, done);
363
364                         ig.MarkLabel (ret);
365
366                         var temp = ig.DeclareLocal (Type);
367                         ec.EmitNullableInitialize (temp);
368
369                         ig.MarkLabel (done);
370                 }
371
372                 void EmitUserDefinedLiftedOperator (EmitContext ec)
373                 {
374                         var local = ec.EmitStored (operand);
375                         ec.EmitNullableGetValue (local);
376                         ec.EmitCall (method);
377                 }
378
379                 void EmitUserDefinedOperator (EmitContext ec)
380                 {
381                         if (!IsLifted) {
382                                 ec.Emit (operand);
383                                 ec.EmitCall (method);
384                         } else if (IsLiftedToNull) {
385                                 EmitUserDefinedLiftedToNullOperator (ec);
386                         } else
387                                 EmitUserDefinedLiftedOperator (ec);
388                 }
389
390                 void EmitQuote (EmitContext ec)
391                 {
392                         ec.EmitScope ();
393
394                         ec.EmitReadGlobal (operand, typeof (Expression));
395
396                         if (ec.HasHoistedLocals)
397                                 ec.EmitLoadHoistedLocalsStore ();
398                         else
399                                 ec.ig.Emit (OpCodes.Ldnull);
400
401                         ec.EmitIsolateExpression ();
402                 }
403
404                 internal override void Emit (EmitContext ec)
405                 {
406                         if (method != null) {
407                                 EmitUserDefinedOperator (ec);
408                                 return;
409                         }
410
411                         switch (this.NodeType) {
412                         case ExpressionType.ArrayLength:
413                                 EmitArrayLength (ec);
414                                 return;
415                         case ExpressionType.TypeAs:
416                                 EmitTypeAs (ec);
417                                 return;
418                         case ExpressionType.Convert:
419                         case ExpressionType.ConvertChecked:
420                                 EmitConvert (ec);
421                                 return;
422                         case ExpressionType.Not:
423                         case ExpressionType.Negate:
424                         case ExpressionType.NegateChecked:
425                         case ExpressionType.UnaryPlus:
426                                 EmitArithmeticUnary (ec);
427                                 return;
428                         case ExpressionType.Quote:
429                                 EmitQuote (ec);
430                                 return;
431                         default:
432                                 throw new NotImplementedException (this.NodeType.ToString ());
433                         }
434                 }
435 #endif
436         }
437 }