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