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.EmitNullableHasValue (from);
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.EmitNullableGetValueOrDefault (from);
159                         ec.EmitNullableNew (to.LocalType);
160
161                         ig.MarkLabel (done);
162                 }
163
164                 void EmitConvertToNullable (EmitContext ec)
165                 {
166                         ec.Emit (operand);
167
168                         if (IsUnBoxing ()) {
169                                 EmitUnbox (ec);
170                                 return;
171                         }
172
173                         ec.EmitNullableNew (MakeNullableType (operand.Type));
174                 }
175
176                 void EmitConvertFromNullable (EmitContext ec)
177                 {
178                         if (IsBoxing ()) {
179                                 ec.Emit (operand);
180                                 EmitBox (ec);
181                                 return;
182                         }
183
184                         ec.EmitCall (operand, operand.Type.GetMethod ("get_Value"));
185                 }
186
187                 bool IsBoxing ()
188                 {
189                         return operand.Type.IsValueType && !Type.IsValueType;
190                 }
191
192                 void EmitBox (EmitContext ec)
193                 {
194                         ec.ig.Emit (OpCodes.Box, operand.Type);
195                 }
196
197                 bool IsUnBoxing ()
198                 {
199                         return !operand.Type.IsValueType && Type.IsValueType;
200                 }
201
202                 void EmitUnbox (EmitContext ec)
203                 {
204                         ec.ig.Emit (OpCodes.Unbox_Any, Type);
205                 }
206
207                 void EmitCast (EmitContext ec)
208                 {
209                         operand.Emit (ec);
210
211                         if (IsBoxing ()) {
212                                 EmitBox (ec);
213                         } else if (IsUnBoxing ()) {
214                                 EmitUnbox (ec);
215                         } else
216                                 ec.ig.Emit (OpCodes.Castclass, Type);
217                 }
218
219                 void EmitPrimitiveConversion (EmitContext ec,
220                         OpCode signed, OpCode unsigned, OpCode signed_checked, OpCode unsigned_checked)
221                 {
222                         operand.Emit (ec);
223
224                         bool is_unsigned = IsUnsigned (operand.Type);
225
226                         if (this.NodeType != ExpressionType.ConvertChecked)
227                                 ec.ig.Emit (is_unsigned ? unsigned : signed);
228                         else
229                                 ec.ig.Emit (is_unsigned ? unsigned_checked : signed_checked);
230                 }
231
232                 void EmitPrimitiveConversion (EmitContext ec)
233                 {
234                         switch (Type.GetTypeCode (this.Type)) {
235                         case TypeCode.SByte:
236                                 EmitPrimitiveConversion (ec,
237                                         OpCodes.Conv_I1,
238                                         OpCodes.Conv_U1,
239                                         OpCodes.Conv_Ovf_I1,
240                                         OpCodes.Conv_Ovf_I1_Un);
241                                 return;
242                         case TypeCode.Byte:
243                                 EmitPrimitiveConversion (ec,
244                                         OpCodes.Conv_I1,
245                                         OpCodes.Conv_U1,
246                                         OpCodes.Conv_Ovf_U1,
247                                         OpCodes.Conv_Ovf_U1_Un);
248                                 return;
249                         case TypeCode.Int16:
250                                 EmitPrimitiveConversion (ec,
251                                         OpCodes.Conv_I2,
252                                         OpCodes.Conv_U2,
253                                         OpCodes.Conv_Ovf_I2,
254                                         OpCodes.Conv_Ovf_I2_Un);
255                                 return;
256                         case TypeCode.UInt16:
257                                 EmitPrimitiveConversion (ec,
258                                         OpCodes.Conv_I2,
259                                         OpCodes.Conv_U2,
260                                         OpCodes.Conv_Ovf_U2,
261                                         OpCodes.Conv_Ovf_U2_Un);
262                                 return;
263                         case TypeCode.Int32:
264                                 EmitPrimitiveConversion (ec,
265                                         OpCodes.Conv_I4,
266                                         OpCodes.Conv_U4,
267                                         OpCodes.Conv_Ovf_I4,
268                                         OpCodes.Conv_Ovf_I4_Un);
269                                 return;
270                         case TypeCode.UInt32:
271                                 EmitPrimitiveConversion (ec,
272                                         OpCodes.Conv_I4,
273                                         OpCodes.Conv_U4,
274                                         OpCodes.Conv_Ovf_U4,
275                                         OpCodes.Conv_Ovf_U4_Un);
276                                 return;
277                         case TypeCode.Int64:
278                                 EmitPrimitiveConversion (ec,
279                                         OpCodes.Conv_I8,
280                                         OpCodes.Conv_U8,
281                                         OpCodes.Conv_Ovf_I8,
282                                         OpCodes.Conv_Ovf_I8_Un);
283                                 return;
284                         case TypeCode.UInt64:
285                                 EmitPrimitiveConversion (ec,
286                                         OpCodes.Conv_I8,
287                                         OpCodes.Conv_U8,
288                                         OpCodes.Conv_Ovf_U8,
289                                         OpCodes.Conv_Ovf_U8_Un);
290                                 return;
291                         case TypeCode.Single:
292                                 if (IsUnsigned (operand.Type))
293                                         ec.ig.Emit (OpCodes.Conv_R_Un);
294                                 ec.ig.Emit (OpCodes.Conv_R4);
295                                 return;
296                         case TypeCode.Double:
297                                 if (IsUnsigned (operand.Type))
298                                         ec.ig.Emit (OpCodes.Conv_R_Un);
299                                 ec.ig.Emit (OpCodes.Conv_R8);
300                                 return;
301                         default:
302                                 throw new NotImplementedException (this.Type.ToString ());
303                         }
304                 }
305
306                 internal override void Emit (EmitContext ec)
307                 {
308                         switch (this.NodeType) {
309                         case ExpressionType.ArrayLength:
310                                 EmitArrayLength (ec);
311                                 return;
312                         case ExpressionType.TypeAs:
313                                 EmitTypeAs (ec);
314                                 return;
315                         case ExpressionType.Convert:
316                         case ExpressionType.ConvertChecked:
317                                 EmitConvert (ec);
318                                 return;
319                         case ExpressionType.Not:
320                         case ExpressionType.Negate:
321                         case ExpressionType.NegateChecked:
322                         case ExpressionType.UnaryPlus:
323                                 if (!is_lifted) {
324                                         operand.Emit (ec);
325                                         EmitUnaryOperator (ec);
326                                 } else
327                                         throw new NotImplementedException ();
328                                 return;
329                         case ExpressionType.Quote:
330                                 ec.EmitReadGlobal (operand, typeof (Expression));
331                                 return;
332                         default:
333                                 throw new NotImplementedException (this.NodeType.ToString ());
334                         }
335                 }
336         }
337 }