2010-04-07 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.Core / System.Linq.Expressions / UnaryExpression.cs
blob11e6240afca4b70ded4a82ad505686b9327ad32d
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
29 using System;
30 using System.Reflection;
31 using System.Reflection.Emit;
33 namespace System.Linq.Expressions {
35 public sealed class UnaryExpression : Expression {
37 Expression operand;
38 MethodInfo method;
39 bool is_lifted;
41 public Expression Operand {
42 get { return operand; }
45 public MethodInfo Method {
46 get { return method; }
49 public bool IsLifted {
50 get { return is_lifted; }
53 public bool IsLiftedToNull {
54 get { return is_lifted && this.Type.IsNullable (); }
57 internal UnaryExpression (ExpressionType node_type, Expression operand, Type type)
58 : base (node_type, type)
60 this.operand = operand;
63 internal UnaryExpression (ExpressionType node_type, Expression operand, Type type, MethodInfo method, bool is_lifted)
64 : base (node_type, type)
66 this.operand = operand;
67 this.method = method;
68 this.is_lifted = is_lifted;
71 void EmitArrayLength (EmitContext ec)
73 operand.Emit (ec);
74 ec.ig.Emit (OpCodes.Ldlen);
77 void EmitTypeAs (EmitContext ec)
79 var type = this.Type;
81 ec.EmitIsInst (operand, type);
83 if (type.IsNullable ())
84 ec.ig.Emit (OpCodes.Unbox_Any, type);
87 void EmitLiftedUnary (EmitContext ec)
89 var ig = ec.ig;
91 var from = ec.EmitStored (operand);
92 var to = ig.DeclareLocal (Type);
94 var has_value = ig.DefineLabel ();
95 var done = ig.DefineLabel ();
97 ec.EmitNullableHasValue (from);
98 ig.Emit (OpCodes.Brtrue, has_value);
100 // if not has value
101 ec.EmitNullableInitialize (to);
103 ig.Emit (OpCodes.Br, done);
105 ig.MarkLabel (has_value);
106 // if has value
107 ec.EmitNullableGetValueOrDefault (from);
109 EmitUnaryOperator (ec);
111 ec.EmitNullableNew (Type);
113 ig.MarkLabel (done);
116 void EmitUnaryOperator (EmitContext ec)
118 var ig = ec.ig;
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;
145 void EmitConvert (EmitContext ec)
147 var from = operand.Type;
148 var target = Type;
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 ();
166 void EmitConvertFromNullableToNullable (EmitContext ec)
168 EmitLiftedUnary (ec);
171 void EmitConvertToNullable (EmitContext ec)
173 ec.Emit (operand);
175 if (IsUnBoxing ()) {
176 EmitUnbox (ec);
177 return;
180 if (operand.Type != Type.GetNotNullableType ()) {
181 EmitPrimitiveConversion (ec,
182 operand.Type,
183 Type.GetNotNullableType ());
186 ec.EmitNullableNew (Type);
189 void EmitConvertFromNullable (EmitContext ec)
191 if (IsBoxing ()) {
192 ec.Emit (operand);
193 EmitBox (ec);
194 return;
197 ec.EmitCall (operand, operand.Type.GetMethod ("get_Value"));
199 if (operand.Type.GetNotNullableType () != Type) {
200 EmitPrimitiveConversion (ec,
201 operand.Type.GetNotNullableType (),
202 Type);
206 bool IsBoxing ()
208 return operand.Type.IsValueType && !Type.IsValueType;
211 void EmitBox (EmitContext ec)
213 ec.ig.Emit (OpCodes.Box, operand.Type);
216 bool IsUnBoxing ()
218 return !operand.Type.IsValueType && Type.IsValueType;
221 void EmitUnbox (EmitContext ec)
223 ec.ig.Emit (OpCodes.Unbox_Any, Type);
226 void EmitCast (EmitContext ec)
228 operand.Emit (ec);
230 if (IsBoxing ()) {
231 EmitBox (ec);
232 } else if (IsUnBoxing ()) {
233 EmitUnbox (ec);
234 } else
235 ec.ig.Emit (OpCodes.Castclass, Type);
238 void EmitPrimitiveConversion (EmitContext ec, bool is_unsigned,
239 OpCode signed, OpCode unsigned, OpCode signed_checked, OpCode unsigned_checked)
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);
247 void EmitPrimitiveConversion (EmitContext ec)
249 operand.Emit (ec);
251 EmitPrimitiveConversion (ec, operand.Type, Type);
254 void EmitPrimitiveConversion (EmitContext ec, Type from, Type to)
256 var is_unsigned = IsUnsigned (from);
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 ());
338 void EmitArithmeticUnary (EmitContext ec)
340 if (!IsLifted) {
341 operand.Emit (ec);
342 EmitUnaryOperator (ec);
343 } else
344 EmitLiftedUnary (ec);
347 void EmitUserDefinedLiftedToNullOperator (EmitContext ec)
349 var ig = ec.ig;
350 var local = ec.EmitStored (operand);
352 var ret = ig.DefineLabel ();
353 var done = ig.DefineLabel ();
355 ec.EmitNullableHasValue (local);
356 ig.Emit (OpCodes.Brfalse, ret);
358 ec.EmitNullableGetValueOrDefault (local);
359 ec.EmitCall (method);
360 ec.EmitNullableNew (Type);
361 ig.Emit (OpCodes.Br, done);
363 ig.MarkLabel (ret);
365 var temp = ig.DeclareLocal (Type);
366 ec.EmitNullableInitialize (temp);
368 ig.MarkLabel (done);
371 void EmitUserDefinedLiftedOperator (EmitContext ec)
373 var local = ec.EmitStored (operand);
374 ec.EmitNullableGetValue (local);
375 ec.EmitCall (method);
378 void EmitUserDefinedOperator (EmitContext ec)
380 if (!IsLifted) {
381 ec.Emit (operand);
382 ec.EmitCall (method);
383 } else if (IsLiftedToNull) {
384 EmitUserDefinedLiftedToNullOperator (ec);
385 } else
386 EmitUserDefinedLiftedOperator (ec);
389 void EmitQuote (EmitContext ec)
391 ec.EmitScope ();
393 ec.EmitReadGlobal (operand, typeof (Expression));
395 if (ec.HasHoistedLocals)
396 ec.EmitLoadHoistedLocalsStore ();
397 else
398 ec.ig.Emit (OpCodes.Ldnull);
400 ec.EmitIsolateExpression ();
403 internal override void Emit (EmitContext ec)
405 if (method != null) {
406 EmitUserDefinedOperator (ec);
407 return;
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 ());