2008-04-23 Jb Evain <jbevain@novell.com>
[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                         }
104                 }
105
106                 void EmitConvert (EmitContext ec)
107                 {
108                         if (Type == operand.Type)
109                                 return;
110
111                         operand.Emit (ec);
112
113                         if (IsCast ())
114                                 EmitCast (ec);
115                         else
116                                 throw new NotImplementedException ();
117                 }
118
119                 bool IsCast ()
120                 {
121                         return CheckReferenceConversion (operand.Type, Type);
122                 }
123
124                 bool IsBoxing ()
125                 {
126                         return operand.Type.IsValueType && !Type.IsValueType;
127                 }
128
129                 bool IsUnBoxing ()
130                 {
131                         return !operand.Type.IsValueType && Type.IsValueType;
132                 }
133
134                 void EmitCast (EmitContext ec)
135                 {
136                         var ig = ec.ig;
137
138                         if (IsBoxing ()) {
139                                 ig.Emit (OpCodes.Box, Type);
140                         } else if (IsUnBoxing ()) {
141                                 ig.Emit (OpCodes.Unbox_Any, Type);
142                         } else
143                                 ec.ig.Emit (OpCodes.Castclass, Type);
144                 }
145
146                 internal override void Emit (EmitContext ec)
147                 {
148                         switch (this.NodeType) {
149                         case ExpressionType.ArrayLength:
150                                 EmitArrayLength (ec);
151                                 return;
152                         case ExpressionType.TypeAs:
153                                 EmitTypeAs (ec);
154                                 return;
155                         case ExpressionType.Not:
156                         case ExpressionType.Negate:
157                         case ExpressionType.NegateChecked:
158                         case ExpressionType.UnaryPlus:
159                                 if (!is_lifted) {
160                                         operand.Emit (ec);
161                                         EmitUnaryOperator (ec);
162                                 } else
163                                         throw new NotImplementedException ();
164                                 return;
165                         case ExpressionType.Convert:
166                         case ExpressionType.ConvertChecked:
167                                 EmitConvert (ec);
168                                 return;
169                         default:
170                                 throw new NotImplementedException (this.NodeType.ToString ());
171                         }
172                 }
173         }
174 }