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
;
71 void EmitArrayLength (EmitContext ec
)
74 ec
.ig
.Emit (OpCodes
.Ldlen
);
77 void EmitTypeAs (EmitContext ec
)
81 ec
.EmitIsInst (operand
, type
);
83 if (type
.IsNullable ())
84 ec
.ig
.Emit (OpCodes
.Unbox_Any
, type
);
87 void EmitLiftedUnary (EmitContext ec
)
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
);
101 ec
.EmitNullableInitialize (to
);
103 ig
.Emit (OpCodes
.Br
, done
);
105 ig
.MarkLabel (has_value
);
107 ec
.EmitNullableGetValueOrDefault (from);
109 EmitUnaryOperator (ec
);
111 ec
.EmitNullableNew (Type
);
116 void EmitUnaryOperator (EmitContext ec
)
121 case ExpressionType
.Not
:
122 if (operand
.Type
.GetNotNullableType () == typeof (bool)) {
123 ig
.Emit (OpCodes
.Ldc_I4_0
);
124 ig
.Emit (OpCodes
.Ceq
);
126 ig
.Emit (OpCodes
.Not
);
128 case ExpressionType
.Negate
:
129 ig
.Emit (OpCodes
.Neg
);
131 case ExpressionType
.NegateChecked
:
132 ig
.Emit (OpCodes
.Ldc_I4_M1
);
133 ig
.Emit (IsUnsigned (operand
.Type
) ? OpCodes
.Mul_Ovf_Un
: OpCodes
.Mul_Ovf
);
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 ());
145 void EmitConvert (EmitContext ec
)
147 var from = operand
.Type
;
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
))
160 else if (IsPrimitiveConversion (from, target
))
161 EmitPrimitiveConversion (ec
);
163 throw new NotImplementedException ();
166 void EmitConvertFromNullableToNullable (EmitContext ec
)
168 EmitLiftedUnary (ec
);
171 void EmitConvertToNullable (EmitContext ec
)
180 if (operand
.Type
!= Type
.GetNotNullableType ()) {
181 EmitPrimitiveConversion (ec
,
183 Type
.GetNotNullableType ());
186 ec
.EmitNullableNew (Type
);
189 void EmitConvertFromNullable (EmitContext ec
)
197 ec
.EmitCall (operand
, operand
.Type
.GetMethod ("get_Value"));
199 if (operand
.Type
.GetNotNullableType () != Type
) {
200 EmitPrimitiveConversion (ec
,
201 operand
.Type
.GetNotNullableType (),
208 return operand
.Type
.IsValueType
&& !Type
.IsValueType
;
211 void EmitBox (EmitContext ec
)
213 ec
.ig
.Emit (OpCodes
.Box
, operand
.Type
);
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
)
232 } else if (IsUnBoxing ()) {
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
);
244 ec
.ig
.Emit (is_unsigned
? unsigned_checked
: signed_checked
);
247 void EmitPrimitiveConversion (EmitContext 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
)) {
260 EmitPrimitiveConversion (ec
,
265 OpCodes
.Conv_Ovf_I1_Un
);
268 EmitPrimitiveConversion (ec
,
273 OpCodes
.Conv_Ovf_U1_Un
);
276 EmitPrimitiveConversion (ec
,
281 OpCodes
.Conv_Ovf_I2_Un
);
283 case TypeCode
.UInt16
:
284 EmitPrimitiveConversion (ec
,
289 OpCodes
.Conv_Ovf_U2_Un
);
292 EmitPrimitiveConversion (ec
,
297 OpCodes
.Conv_Ovf_I4_Un
);
299 case TypeCode
.UInt32
:
300 EmitPrimitiveConversion (ec
,
305 OpCodes
.Conv_Ovf_U4_Un
);
308 EmitPrimitiveConversion (ec
,
313 OpCodes
.Conv_Ovf_I8_Un
);
315 case TypeCode
.UInt64
:
316 EmitPrimitiveConversion (ec
,
321 OpCodes
.Conv_Ovf_U8_Un
);
323 case TypeCode
.Single
:
325 ec
.ig
.Emit (OpCodes
.Conv_R_Un
);
326 ec
.ig
.Emit (OpCodes
.Conv_R4
);
328 case TypeCode
.Double
:
330 ec
.ig
.Emit (OpCodes
.Conv_R_Un
);
331 ec
.ig
.Emit (OpCodes
.Conv_R8
);
334 throw new NotImplementedException (this.Type
.ToString ());
338 void EmitArithmeticUnary (EmitContext ec
)
342 EmitUnaryOperator (ec
);
344 EmitLiftedUnary (ec
);
347 void EmitUserDefinedLiftedToNullOperator (EmitContext ec
)
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
);
365 var temp
= ig
.DeclareLocal (Type
);
366 ec
.EmitNullableInitialize (temp
);
371 void EmitUserDefinedLiftedOperator (EmitContext ec
)
373 var local
= ec
.EmitStored (operand
);
374 ec
.EmitNullableGetValue (local
);
375 ec
.EmitCall (method
);
378 void EmitUserDefinedOperator (EmitContext ec
)
382 ec
.EmitCall (method
);
383 } else if (IsLiftedToNull
) {
384 EmitUserDefinedLiftedToNullOperator (ec
);
386 EmitUserDefinedLiftedOperator (ec
);
389 void EmitQuote (EmitContext ec
)
393 ec
.EmitReadGlobal (operand
, typeof (Expression
));
395 if (ec
.HasHoistedLocals
)
396 ec
.EmitLoadHoistedLocalsStore ();
398 ec
.ig
.Emit (OpCodes
.Ldnull
);
400 ec
.EmitIsolateExpression ();
403 internal override void Emit (EmitContext ec
)
405 if (method
!= null) {
406 EmitUserDefinedOperator (ec
);
410 switch (this.NodeType
) {
411 case ExpressionType
.ArrayLength
:
412 EmitArrayLength (ec
);
414 case ExpressionType
.TypeAs
:
417 case ExpressionType
.Convert
:
418 case ExpressionType
.ConvertChecked
:
421 case ExpressionType
.Not
:
422 case ExpressionType
.Negate
:
423 case ExpressionType
.NegateChecked
:
424 case ExpressionType
.UnaryPlus
:
425 EmitArithmeticUnary (ec
);
427 case ExpressionType
.Quote
:
431 throw new NotImplementedException (this.NodeType
.ToString ());