refactoring
[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 && IsNullable (this.Type); }
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 (IsNullable (type))
84                                 ec.ig.Emit (OpCodes.Unbox_Any, type);
85                 }
86
87                 void EmitUnaryOperator (EmitContext ec)
88                 {
89                         var ig = ec.ig;
90
91                         switch (NodeType) {
92                         case ExpressionType.Not:
93                                 if (GetNotNullableOf (operand.Type) == typeof (bool)) {
94                                         ig.Emit (OpCodes.Ldc_I4_0);
95                                         ig.Emit (OpCodes.Ceq);
96                                 } else
97                                         ig.Emit (OpCodes.Not);
98                                 break;
99                         case ExpressionType.Negate:
100                         case ExpressionType.NegateChecked:
101                                 ig.Emit (OpCodes.Neg);
102                                 break;
103                         case ExpressionType.Convert:
104                         case ExpressionType.ConvertChecked:
105                                 EmitConvert (ec);
106                                 break;
107                         }
108                 }
109
110                 void EmitConvert (EmitContext ec)
111                 {
112                         var from = operand.Type;
113                         var target = Type;
114
115                         if (from == target)
116                                 operand.Emit (ec);
117                         else if (IsNullable (from) && !IsNullable (target))
118                                 EmitConvertFromNullable (ec);
119                         else if (!IsNullable (from) && IsNullable (target))
120                                 EmitConvertToNullable (ec);
121                         else if (IsNullable (from) && IsNullable (target))
122                                 EmitConvertFromNullableToNullable (ec);
123                         else if (IsReferenceConversion (from, target))
124                                 EmitCast (ec);
125                         else if (IsPrimitiveConversion (from, target))
126                                 EmitPrimitiveConversion (ec);
127                         else
128                                 throw new NotImplementedException ();
129                 }
130
131                 static Type MakeNullableType (Type type)
132                 {
133                         return typeof (Nullable<>).MakeGenericType (type);
134                 }
135
136                 void EmitConvertFromNullableToNullable (EmitContext ec)
137                 {
138                         var ig = ec.ig;
139
140                         var from = ec.EmitStored (operand);
141                         var to = ig.DeclareLocal (Type);
142
143                         var has_value = ig.DefineLabel ();
144                         var done = ig.DefineLabel ();
145
146                         ec.EmitCall (from, from.LocalType.GetMethod ("get_HasValue"));
147                         ig.Emit (OpCodes.Brtrue, has_value);
148
149                         // if not has value
150                         ig.Emit (OpCodes.Ldloca, to);
151                         ig.Emit (OpCodes.Initobj, to.LocalType);
152                         ig.Emit (OpCodes.Ldloc, to);
153
154                         ig.Emit (OpCodes.Br, done);
155
156                         ig.MarkLabel (has_value);
157                         // if has value
158                         ec.EmitCall (from, from.LocalType.GetMethod ("GetValueOrDefault", Type.EmptyTypes));
159                         ig.Emit (OpCodes.Newobj, to.LocalType.GetConstructor (
160                                 new [] { GetNotNullableOf (to.LocalType) }));
161
162                         ig.MarkLabel (done);
163                 }
164
165                 void EmitConvertToNullable (EmitContext ec)
166                 {
167                         ec.Emit (operand);
168                         ec.ig.Emit (OpCodes.Newobj,
169                                 MakeNullableType (operand.Type).GetConstructor (new [] { operand.Type }));
170                 }
171
172                 void EmitConvertFromNullable (EmitContext ec)
173                 {
174                         ec.EmitCall (operand, operand.Type.GetMethod ("get_Value"));
175                 }
176
177                 bool IsBoxing ()
178                 {
179                         return operand.Type.IsValueType && !Type.IsValueType;
180                 }
181
182                 bool IsUnBoxing ()
183                 {
184                         return !operand.Type.IsValueType && Type.IsValueType;
185                 }
186
187                 void EmitCast (EmitContext ec)
188                 {
189                         operand.Emit (ec);
190
191                         if (IsBoxing ()) {
192                                 ec.ig.Emit (OpCodes.Box, operand.Type);
193                         } else if (IsUnBoxing ()) {
194                                 ec.ig.Emit (OpCodes.Unbox_Any, Type);
195                         } else
196                                 ec.ig.Emit (OpCodes.Castclass, Type);
197                 }
198
199                 void EmitPrimitiveConversion (EmitContext ec,
200                         OpCode signed, OpCode unsigned, OpCode signed_checked, OpCode unsigned_checked)
201                 {
202                         operand.Emit (ec);
203
204                         bool is_unsigned = IsUnsigned (operand.Type);
205
206                         if (this.NodeType != ExpressionType.ConvertChecked)
207                                 ec.ig.Emit (is_unsigned ? unsigned : signed);
208                         else
209                                 ec.ig.Emit (is_unsigned ? unsigned_checked : signed_checked);
210                 }
211
212                 void EmitPrimitiveConversion (EmitContext ec)
213                 {
214                         switch (Type.GetTypeCode (this.Type)) {
215                         case TypeCode.SByte:
216                                 EmitPrimitiveConversion (ec,
217                                         OpCodes.Conv_I1,
218                                         OpCodes.Conv_U1,
219                                         OpCodes.Conv_Ovf_I1,
220                                         OpCodes.Conv_Ovf_I1_Un);
221                                 return;
222                         case TypeCode.Byte:
223                                 EmitPrimitiveConversion (ec,
224                                         OpCodes.Conv_I1,
225                                         OpCodes.Conv_U1,
226                                         OpCodes.Conv_Ovf_U1,
227                                         OpCodes.Conv_Ovf_U1_Un);
228                                 return;
229                         case TypeCode.Int16:
230                                 EmitPrimitiveConversion (ec,
231                                         OpCodes.Conv_I2,
232                                         OpCodes.Conv_U2,
233                                         OpCodes.Conv_Ovf_I2,
234                                         OpCodes.Conv_Ovf_I2_Un);
235                                 return;
236                         case TypeCode.UInt16:
237                                 EmitPrimitiveConversion (ec,
238                                         OpCodes.Conv_I2,
239                                         OpCodes.Conv_U2,
240                                         OpCodes.Conv_Ovf_U2,
241                                         OpCodes.Conv_Ovf_U2_Un);
242                                 return;
243                         case TypeCode.Int32:
244                                 EmitPrimitiveConversion (ec,
245                                         OpCodes.Conv_I4,
246                                         OpCodes.Conv_U4,
247                                         OpCodes.Conv_Ovf_I4,
248                                         OpCodes.Conv_Ovf_I4_Un);
249                                 return;
250                         case TypeCode.UInt32:
251                                 EmitPrimitiveConversion (ec,
252                                         OpCodes.Conv_I4,
253                                         OpCodes.Conv_U4,
254                                         OpCodes.Conv_Ovf_U4,
255                                         OpCodes.Conv_Ovf_U4_Un);
256                                 return;
257                         case TypeCode.Int64:
258                                 EmitPrimitiveConversion (ec,
259                                         OpCodes.Conv_I8,
260                                         OpCodes.Conv_U8,
261                                         OpCodes.Conv_Ovf_I8,
262                                         OpCodes.Conv_Ovf_I8_Un);
263                                 return;
264                         case TypeCode.UInt64:
265                                 EmitPrimitiveConversion (ec,
266                                         OpCodes.Conv_I8,
267                                         OpCodes.Conv_U8,
268                                         OpCodes.Conv_Ovf_U8,
269                                         OpCodes.Conv_Ovf_U8_Un);
270                                 return;
271                         case TypeCode.Single:
272                                 if (IsUnsigned (operand.Type))
273                                         ec.ig.Emit (OpCodes.Conv_R_Un);
274                                 ec.ig.Emit (OpCodes.Conv_R4);
275                                 return;
276                         case TypeCode.Double:
277                                 if (IsUnsigned (operand.Type))
278                                         ec.ig.Emit (OpCodes.Conv_R_Un);
279                                 ec.ig.Emit (OpCodes.Conv_R8);
280                                 return;
281                         default:
282                                 throw new NotImplementedException (this.Type.ToString ());
283                         }
284                 }
285
286                 internal override void Emit (EmitContext ec)
287                 {
288                         switch (this.NodeType) {
289                         case ExpressionType.ArrayLength:
290                                 EmitArrayLength (ec);
291                                 return;
292                         case ExpressionType.TypeAs:
293                                 EmitTypeAs (ec);
294                                 return;
295                         case ExpressionType.Convert:
296                         case ExpressionType.ConvertChecked:
297                                 EmitConvert (ec);
298                                 return;
299                         case ExpressionType.Not:
300                         case ExpressionType.Negate:
301                         case ExpressionType.NegateChecked:
302                         case ExpressionType.UnaryPlus:
303                                 if (!is_lifted) {
304                                         operand.Emit (ec);
305                                         EmitUnaryOperator (ec);
306                                 } else
307                                         throw new NotImplementedException ();
308                                 return;
309                         case ExpressionType.Quote:
310                                 ec.EmitReadGlobal (operand, typeof (Expression));
311                                 return;
312                         default:
313                                 throw new NotImplementedException (this.NodeType.ToString ());
314                         }
315                 }
316         }
317 }