5 // Jb Evain (jbevain@novell.com)
7 // (C) 2008 Novell, Inc. (http://www.novell.com)
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.
30 using System.Reflection;
31 using System.Reflection.Emit;
33 namespace System.Linq.Expressions {
35 public sealed class UnaryExpression : Expression {
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;
68 this.is_lifted = is_lifted;
72 void EmitArrayLength (EmitContext ec)
75 ec.ig.Emit (OpCodes.Ldlen);
78 void EmitTypeAs (EmitContext ec)
82 ec.EmitIsInst (operand, type);
84 if (type.IsNullable ())
85 ec.ig.Emit (OpCodes.Unbox_Any, type);
88 void EmitLiftedUnary (EmitContext ec)
92 var from = ec.EmitStored (operand);
93 var to = ig.DeclareLocal (Type);
95 var has_value = ig.DefineLabel ();
96 var done = ig.DefineLabel ();
98 ec.EmitNullableHasValue (from);
99 ig.Emit (OpCodes.Brtrue, has_value);
102 ec.EmitNullableInitialize (to);
104 ig.Emit (OpCodes.Br, done);
106 ig.MarkLabel (has_value);
108 ec.EmitNullableGetValueOrDefault (from);
110 EmitUnaryOperator (ec);
112 ec.EmitNullableNew (Type);
117 void EmitUnaryOperator (EmitContext ec)
122 case ExpressionType.Not:
123 if (operand.Type.GetNotNullableType () == typeof (bool)) {
124 ig.Emit (OpCodes.Ldc_I4_0);
125 ig.Emit (OpCodes.Ceq);
127 ig.Emit (OpCodes.Not);
129 case ExpressionType.Negate:
130 ig.Emit (OpCodes.Neg);
132 case ExpressionType.NegateChecked:
133 ig.Emit (OpCodes.Ldc_I4_M1);
134 ig.Emit (IsUnsigned (operand.Type) ? OpCodes.Mul_Ovf_Un : OpCodes.Mul_Ovf);
136 case ExpressionType.Convert:
137 case ExpressionType.ConvertChecked:
138 // Called when converting from nullable from nullable
139 EmitPrimitiveConversion (ec,
140 operand.Type.GetNotNullableType (),
141 Type.GetNotNullableType ());
146 void EmitConvert (EmitContext ec)
148 var from = operand.Type;
153 else if (from.IsNullable () && !target.IsNullable ())
154 EmitConvertFromNullable (ec);
155 else if (!from.IsNullable () && target.IsNullable ())
156 EmitConvertToNullable (ec);
157 else if (from.IsNullable () && target.IsNullable ())
158 EmitConvertFromNullableToNullable (ec);
159 else if (IsReferenceConversion (from, target))
161 else if (IsPrimitiveConversion (from, target))
162 EmitPrimitiveConversion (ec);
164 throw new NotImplementedException ();
167 void EmitConvertFromNullableToNullable (EmitContext ec)
169 EmitLiftedUnary (ec);
172 void EmitConvertToNullable (EmitContext ec)
181 if (operand.Type != Type.GetNotNullableType ()) {
182 EmitPrimitiveConversion (ec,
184 Type.GetNotNullableType ());
187 ec.EmitNullableNew (Type);
190 void EmitConvertFromNullable (EmitContext ec)
198 ec.EmitCall (operand, operand.Type.GetMethod ("get_Value"));
200 if (operand.Type.GetNotNullableType () != Type) {
201 EmitPrimitiveConversion (ec,
202 operand.Type.GetNotNullableType (),
209 return operand.Type.IsValueType && !Type.IsValueType;
212 void EmitBox (EmitContext ec)
214 ec.ig.Emit (OpCodes.Box, operand.Type);
219 return !operand.Type.IsValueType && Type.IsValueType;
222 void EmitUnbox (EmitContext ec)
224 ec.ig.Emit (OpCodes.Unbox_Any, Type);
227 void EmitCast (EmitContext ec)
233 } else if (IsUnBoxing ()) {
236 ec.ig.Emit (OpCodes.Castclass, Type);
239 void EmitPrimitiveConversion (EmitContext ec, bool is_unsigned,
240 OpCode signed, OpCode unsigned, OpCode signed_checked, OpCode unsigned_checked)
242 if (this.NodeType != ExpressionType.ConvertChecked)
243 ec.ig.Emit (is_unsigned ? unsigned : signed);
245 ec.ig.Emit (is_unsigned ? unsigned_checked : signed_checked);
248 void EmitPrimitiveConversion (EmitContext ec)
252 EmitPrimitiveConversion (ec, operand.Type, Type);
255 void EmitPrimitiveConversion (EmitContext ec, Type from, Type to)
257 var is_unsigned = IsUnsigned (from);
259 switch (Type.GetTypeCode (to)) {
261 EmitPrimitiveConversion (ec,
266 OpCodes.Conv_Ovf_I1_Un);
269 EmitPrimitiveConversion (ec,
274 OpCodes.Conv_Ovf_U1_Un);
277 EmitPrimitiveConversion (ec,
282 OpCodes.Conv_Ovf_I2_Un);
284 case TypeCode.UInt16:
285 EmitPrimitiveConversion (ec,
290 OpCodes.Conv_Ovf_U2_Un);
293 EmitPrimitiveConversion (ec,
298 OpCodes.Conv_Ovf_I4_Un);
300 case TypeCode.UInt32:
301 EmitPrimitiveConversion (ec,
306 OpCodes.Conv_Ovf_U4_Un);
309 EmitPrimitiveConversion (ec,
314 OpCodes.Conv_Ovf_I8_Un);
316 case TypeCode.UInt64:
317 EmitPrimitiveConversion (ec,
322 OpCodes.Conv_Ovf_U8_Un);
324 case TypeCode.Single:
326 ec.ig.Emit (OpCodes.Conv_R_Un);
327 ec.ig.Emit (OpCodes.Conv_R4);
329 case TypeCode.Double:
331 ec.ig.Emit (OpCodes.Conv_R_Un);
332 ec.ig.Emit (OpCodes.Conv_R8);
335 throw new NotImplementedException (this.Type.ToString ());
339 void EmitArithmeticUnary (EmitContext ec)
343 EmitUnaryOperator (ec);
345 EmitLiftedUnary (ec);
348 void EmitUserDefinedLiftedToNullOperator (EmitContext ec)
351 var local = ec.EmitStored (operand);
353 var ret = ig.DefineLabel ();
354 var done = ig.DefineLabel ();
356 ec.EmitNullableHasValue (local);
357 ig.Emit (OpCodes.Brfalse, ret);
359 ec.EmitNullableGetValueOrDefault (local);
360 ec.EmitCall (method);
361 ec.EmitNullableNew (Type);
362 ig.Emit (OpCodes.Br, done);
366 var temp = ig.DeclareLocal (Type);
367 ec.EmitNullableInitialize (temp);
372 void EmitUserDefinedLiftedOperator (EmitContext ec)
374 var local = ec.EmitStored (operand);
375 ec.EmitNullableGetValue (local);
376 ec.EmitCall (method);
379 void EmitUserDefinedOperator (EmitContext ec)
383 ec.EmitCall (method);
384 } else if (IsLiftedToNull) {
385 EmitUserDefinedLiftedToNullOperator (ec);
387 EmitUserDefinedLiftedOperator (ec);
390 void EmitQuote (EmitContext ec)
394 ec.EmitReadGlobal (operand, typeof (Expression));
396 if (ec.HasHoistedLocals)
397 ec.EmitLoadHoistedLocalsStore ();
399 ec.ig.Emit (OpCodes.Ldnull);
401 ec.EmitIsolateExpression ();
404 internal override void Emit (EmitContext ec)
406 if (method != null) {
407 EmitUserDefinedOperator (ec);
411 switch (this.NodeType) {
412 case ExpressionType.ArrayLength:
413 EmitArrayLength (ec);
415 case ExpressionType.TypeAs:
418 case ExpressionType.Convert:
419 case ExpressionType.ConvertChecked:
422 case ExpressionType.Not:
423 case ExpressionType.Negate:
424 case ExpressionType.NegateChecked:
425 case ExpressionType.UnaryPlus:
426 EmitArithmeticUnary (ec);
428 case ExpressionType.Quote:
432 throw new NotImplementedException (this.NodeType.ToString ());