Update mcs/class/Commons.Xml.Relaxng/Commons.Xml.Relaxng/RelaxngPattern.cs
[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                 void EmitArrayLength (EmitContext ec)
72                 {
73                         operand.Emit (ec);
74                         ec.ig.Emit (OpCodes.Ldlen);
75                 }
76
77                 void EmitTypeAs (EmitContext ec)
78                 {
79                         var type = this.Type;
80
81                         ec.EmitIsInst (operand, type);
82
83                         if (type.IsNullable ())
84                                 ec.ig.Emit (OpCodes.Unbox_Any, type);
85                 }
86
87                 void EmitLiftedUnary (EmitContext ec)
88                 {
89                         var ig = ec.ig;
90
91                         var from = ec.EmitStored (operand);
92                         var to = ig.DeclareLocal (Type);
93
94                         var has_value = ig.DefineLabel ();
95                         var done = ig.DefineLabel ();
96
97                         ec.EmitNullableHasValue (from);
98                         ig.Emit (OpCodes.Brtrue, has_value);
99
100                         // if not has value
101                         ec.EmitNullableInitialize (to);
102
103                         ig.Emit (OpCodes.Br, done);
104
105                         ig.MarkLabel (has_value);
106                         // if has value
107                         ec.EmitNullableGetValueOrDefault (from);
108
109                         EmitUnaryOperator (ec);
110
111                         ec.EmitNullableNew (Type);
112
113                         ig.MarkLabel (done);
114                 }
115
116                 void EmitUnaryOperator (EmitContext ec)
117                 {
118                         var ig = ec.ig;
119
120                         switch (NodeType) {
121                         case ExpressionType.Not:
122                                 if (operand.Type.GetNotNullableType () == typeof (bool)) {
123                                         ig.Emit (OpCodes.Ldc_I4_0);
124                                         ig.Emit (OpCodes.Ceq);
125                                 } else
126                                         ig.Emit (OpCodes.Not);
127                                 break;
128                         case ExpressionType.Negate:
129                                 ig.Emit (OpCodes.Neg);
130                                 break;
131                         case ExpressionType.NegateChecked:
132                                 ig.Emit (OpCodes.Ldc_I4_M1);
133                                 ig.Emit (IsUnsigned (operand.Type) ? OpCodes.Mul_Ovf_Un : OpCodes.Mul_Ovf);
134                                 break;
135                         case ExpressionType.Convert:
136                         case ExpressionType.ConvertChecked:
137                                 // Called when converting from nullable from nullable
138                                 EmitPrimitiveConversion (ec,
139                                         operand.Type.GetNotNullableType (),
140                                         Type.GetNotNullableType ());
141                                 break;
142                         }
143                 }
144
145                 void EmitConvert (EmitContext ec)
146                 {
147                         var from = operand.Type;
148                         var target = Type;
149
150                         if (from == target)
151                                 operand.Emit (ec);
152                         else if (from.IsNullable () && !target.IsNullable ())
153                                 EmitConvertFromNullable (ec);
154                         else if (!from.IsNullable () && target.IsNullable ())
155                                 EmitConvertToNullable (ec);
156                         else if (from.IsNullable () && target.IsNullable ())
157                                 EmitConvertFromNullableToNullable (ec);
158                         else if (IsReferenceConversion (from, target))
159                                 EmitCast (ec);
160                         else if (IsPrimitiveConversion (from, target))
161                                 EmitPrimitiveConversion (ec);
162                         else
163                                 throw new NotImplementedException ();
164                 }
165
166                 void EmitConvertFromNullableToNullable (EmitContext ec)
167                 {
168                         EmitLiftedUnary (ec);
169                 }
170
171                 void EmitConvertToNullable (EmitContext ec)
172                 {
173                         ec.Emit (operand);
174
175                         if (IsUnBoxing ()) {
176                                 EmitUnbox (ec);
177                                 return;
178                         }
179
180                         if (operand.Type != Type.GetNotNullableType ()) {
181                                 EmitPrimitiveConversion (ec,
182                                         operand.Type,
183                                         Type.GetNotNullableType ());
184                         }
185
186                         ec.EmitNullableNew (Type);
187                 }
188
189                 void EmitConvertFromNullable (EmitContext ec)
190                 {
191                         if (IsBoxing ()) {
192                                 ec.Emit (operand);
193                                 EmitBox (ec);
194                                 return;
195                         }
196
197                         ec.EmitCall (operand, operand.Type.GetMethod ("get_Value"));
198
199                         if (operand.Type.GetNotNullableType () != Type) {
200                                 EmitPrimitiveConversion (ec,
201                                         operand.Type.GetNotNullableType (),
202                                         Type);
203                         }
204                 }
205
206                 bool IsBoxing ()
207                 {
208                         return operand.Type.IsValueType && !Type.IsValueType;
209                 }
210
211                 void EmitBox (EmitContext ec)
212                 {
213                         ec.ig.Emit (OpCodes.Box, operand.Type);
214                 }
215
216                 bool IsUnBoxing ()
217                 {
218                         return !operand.Type.IsValueType && Type.IsValueType;
219                 }
220
221                 void EmitUnbox (EmitContext ec)
222                 {
223                         ec.ig.Emit (OpCodes.Unbox_Any, Type);
224                 }
225
226                 void EmitCast (EmitContext ec)
227                 {
228                         operand.Emit (ec);
229
230                         if (IsBoxing ()) {
231                                 EmitBox (ec);
232                         } else if (IsUnBoxing ()) {
233                                 EmitUnbox (ec);
234                         } else
235                                 ec.ig.Emit (OpCodes.Castclass, Type);
236                 }
237
238                 void EmitPrimitiveConversion (EmitContext ec, bool is_unsigned,
239                         OpCode signed, OpCode unsigned, OpCode signed_checked, OpCode unsigned_checked)
240                 {
241                         if (this.NodeType != ExpressionType.ConvertChecked)
242                                 ec.ig.Emit (is_unsigned ? unsigned : signed);
243                         else
244                                 ec.ig.Emit (is_unsigned ? unsigned_checked : signed_checked);
245                 }
246
247                 void EmitPrimitiveConversion (EmitContext ec)
248                 {
249                         operand.Emit (ec);
250
251                         EmitPrimitiveConversion (ec, operand.Type, Type);
252                 }
253
254                 void EmitPrimitiveConversion (EmitContext ec, Type from, Type to)
255                 {
256                         var is_unsigned = IsUnsigned (from);
257
258                         switch (Type.GetTypeCode (to)) {
259                         case TypeCode.SByte:
260                                 EmitPrimitiveConversion (ec,
261                                         is_unsigned,
262                                         OpCodes.Conv_I1,
263                                         OpCodes.Conv_U1,
264                                         OpCodes.Conv_Ovf_I1,
265                                         OpCodes.Conv_Ovf_I1_Un);
266                                 return;
267                         case TypeCode.Byte:
268                                 EmitPrimitiveConversion (ec,
269                                         is_unsigned,
270                                         OpCodes.Conv_I1,
271                                         OpCodes.Conv_U1,
272                                         OpCodes.Conv_Ovf_U1,
273                                         OpCodes.Conv_Ovf_U1_Un);
274                                 return;
275                         case TypeCode.Int16:
276                                 EmitPrimitiveConversion (ec,
277                                         is_unsigned,
278                                         OpCodes.Conv_I2,
279                                         OpCodes.Conv_U2,
280                                         OpCodes.Conv_Ovf_I2,
281                                         OpCodes.Conv_Ovf_I2_Un);
282                                 return;
283                         case TypeCode.UInt16:
284                                 EmitPrimitiveConversion (ec,
285                                         is_unsigned,
286                                         OpCodes.Conv_I2,
287                                         OpCodes.Conv_U2,
288                                         OpCodes.Conv_Ovf_U2,
289                                         OpCodes.Conv_Ovf_U2_Un);
290                                 return;
291                         case TypeCode.Int32:
292                                 EmitPrimitiveConversion (ec,
293                                         is_unsigned,
294                                         OpCodes.Conv_I4,
295                                         OpCodes.Conv_U4,
296                                         OpCodes.Conv_Ovf_I4,
297                                         OpCodes.Conv_Ovf_I4_Un);
298                                 return;
299                         case TypeCode.UInt32:
300                                 EmitPrimitiveConversion (ec,
301                                         is_unsigned,
302                                         OpCodes.Conv_I4,
303                                         OpCodes.Conv_U4,
304                                         OpCodes.Conv_Ovf_U4,
305                                         OpCodes.Conv_Ovf_U4_Un);
306                                 return;
307                         case TypeCode.Int64:
308                                 EmitPrimitiveConversion (ec,
309                                         is_unsigned,
310                                         OpCodes.Conv_I8,
311                                         OpCodes.Conv_U8,
312                                         OpCodes.Conv_Ovf_I8,
313                                         OpCodes.Conv_Ovf_I8_Un);
314                                 return;
315                         case TypeCode.UInt64:
316                                 EmitPrimitiveConversion (ec,
317                                         is_unsigned,
318                                         OpCodes.Conv_I8,
319                                         OpCodes.Conv_U8,
320                                         OpCodes.Conv_Ovf_U8,
321                                         OpCodes.Conv_Ovf_U8_Un);
322                                 return;
323                         case TypeCode.Single:
324                                 if (is_unsigned)
325                                         ec.ig.Emit (OpCodes.Conv_R_Un);
326                                 ec.ig.Emit (OpCodes.Conv_R4);
327                                 return;
328                         case TypeCode.Double:
329                                 if (is_unsigned)
330                                         ec.ig.Emit (OpCodes.Conv_R_Un);
331                                 ec.ig.Emit (OpCodes.Conv_R8);
332                                 return;
333                         default:
334                                 throw new NotImplementedException (this.Type.ToString ());
335                         }
336                 }
337
338                 void EmitArithmeticUnary (EmitContext ec)
339                 {
340                         if (!IsLifted) {
341                                 operand.Emit (ec);
342                                 EmitUnaryOperator (ec);
343                         } else
344                                 EmitLiftedUnary (ec);
345                 }
346
347                 void EmitUserDefinedLiftedToNullOperator (EmitContext ec)
348                 {
349                         var ig = ec.ig;
350                         var local = ec.EmitStored (operand);
351
352                         var ret = ig.DefineLabel ();
353                         var done = ig.DefineLabel ();
354
355                         ec.EmitNullableHasValue (local);
356                         ig.Emit (OpCodes.Brfalse, ret);
357
358                         ec.EmitNullableGetValueOrDefault (local);
359                         ec.EmitCall (method);
360                         ec.EmitNullableNew (Type);
361                         ig.Emit (OpCodes.Br, done);
362
363                         ig.MarkLabel (ret);
364
365                         var temp = ig.DeclareLocal (Type);
366                         ec.EmitNullableInitialize (temp);
367
368                         ig.MarkLabel (done);
369                 }
370
371                 void EmitUserDefinedLiftedOperator (EmitContext ec)
372                 {
373                         var local = ec.EmitStored (operand);
374                         ec.EmitNullableGetValue (local);
375                         ec.EmitCall (method);
376                 }
377
378                 void EmitUserDefinedOperator (EmitContext ec)
379                 {
380                         if (!IsLifted) {
381                                 ec.Emit (operand);
382                                 ec.EmitCall (method);
383                         } else if (IsLiftedToNull) {
384                                 EmitUserDefinedLiftedToNullOperator (ec);
385                         } else
386                                 EmitUserDefinedLiftedOperator (ec);
387                 }
388
389                 void EmitQuote (EmitContext ec)
390                 {
391                         ec.EmitScope ();
392
393                         ec.EmitReadGlobal (operand, typeof (Expression));
394
395                         if (ec.HasHoistedLocals)
396                                 ec.EmitLoadHoistedLocalsStore ();
397                         else
398                                 ec.ig.Emit (OpCodes.Ldnull);
399
400                         ec.EmitIsolateExpression ();
401                 }
402
403                 internal override void Emit (EmitContext ec)
404                 {
405                         if (method != null) {
406                                 EmitUserDefinedOperator (ec);
407                                 return;
408                         }
409
410                         switch (this.NodeType) {
411                         case ExpressionType.ArrayLength:
412                                 EmitArrayLength (ec);
413                                 return;
414                         case ExpressionType.TypeAs:
415                                 EmitTypeAs (ec);
416                                 return;
417                         case ExpressionType.Convert:
418                         case ExpressionType.ConvertChecked:
419                                 EmitConvert (ec);
420                                 return;
421                         case ExpressionType.Not:
422                         case ExpressionType.Negate:
423                         case ExpressionType.NegateChecked:
424                         case ExpressionType.UnaryPlus:
425                                 EmitArithmeticUnary (ec);
426                                 return;
427                         case ExpressionType.Quote:
428                                 EmitQuote (ec);
429                                 return;
430                         default:
431                                 throw new NotImplementedException (this.NodeType.ToString ());
432                         }
433                 }
434         }
435 }