Merge branch 'master' of github.com:tgiphil/mono
[mono.git] / mcs / class / System.Core / System.Linq.Expressions / ConstantExpression.cs
1 //
2 // ConstantExpression.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@novell.com)
6 //   Miguel de Icaza (miguel@novell.com)
7 //
8 // Some code is based on the Mono C# compiler:
9 //   Marek Safar (marek.safar@seznam.cz)
10 //   Martin Baulig (martin@ximian.com)
11 //
12 // (C) 2001-2008 Novell, Inc. (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Reflection;
36 using System.Reflection.Emit;
37
38 namespace System.Linq.Expressions {
39
40         public sealed class ConstantExpression : Expression {
41                 object value;
42
43                 public object Value {
44                         get { return value; }
45                 }
46
47                 internal ConstantExpression (object value, Type type)
48                         : base (ExpressionType.Constant, type)
49                 {
50                         this.value = value;
51                 }
52
53                 internal override void Emit (EmitContext ec)
54                 {
55                         if (Type.IsNullable ()) {
56                                 EmitNullableConstant (ec, Type, value);
57                                 return;
58                         }
59
60                         EmitConstant (ec, Type, value);
61                 }
62
63                 void EmitNullableConstant (EmitContext ec, Type type, object value)
64                 {
65                         if (value == null) {
66                                 var ig = ec.ig;
67                                 var local = ig.DeclareLocal (type);
68                                 ig.Emit (OpCodes.Ldloca, local);
69                                 ig.Emit (OpCodes.Initobj, type);
70                                 ig.Emit (OpCodes.Ldloc, local);
71                         } else {
72                                 EmitConstant (ec, type.GetFirstGenericArgument (), value);
73                                 ec.EmitNullableNew (type);
74                         }
75                 }
76
77                 void EmitConstant (EmitContext ec, Type type, object value)
78                 {
79                         var ig = ec.ig;
80
81                         switch (Type.GetTypeCode (type)){
82                         case TypeCode.Byte:
83                                 ig.Emit (OpCodes.Ldc_I4, (int) ((byte)value));
84                                 return;
85
86                         case TypeCode.SByte:
87                                 ig.Emit (OpCodes.Ldc_I4, (int) ((sbyte)value));
88                                 return;
89
90                         case TypeCode.Int16:
91                                 ig.Emit (OpCodes.Ldc_I4, (int) ((short)value));
92                                 return;
93
94                         case TypeCode.UInt16:
95                                 ig.Emit (OpCodes.Ldc_I4, (int) ((ushort)value));
96                                 return;
97
98                         case TypeCode.Int32:
99                                 ig.Emit (OpCodes.Ldc_I4, (int) value);
100                                 return;
101
102                         case TypeCode.UInt32:
103                                 ig.Emit (OpCodes.Ldc_I4, unchecked ((int) ((uint)Value)));
104                                 return;
105
106                         case TypeCode.Int64:
107                                 ig.Emit (OpCodes.Ldc_I8, (long) value);
108                                 return;
109
110                         case TypeCode.UInt64:
111                                 ig.Emit (OpCodes.Ldc_I8, unchecked ((long) ((ulong)value)));
112                                 return;
113
114                         case TypeCode.Boolean:
115                                 if ((bool) Value)
116                                         ig.Emit (OpCodes.Ldc_I4_1);
117                                 else
118                                         ec.ig.Emit (OpCodes.Ldc_I4_0);
119                                 return;
120
121                         case TypeCode.Char:
122                                 ig.Emit (OpCodes.Ldc_I4, (int) ((char) value));
123                                 return;
124
125                         case TypeCode.Single:
126                                 ig.Emit (OpCodes.Ldc_R4, (float) value);
127                                 return;
128
129                         case TypeCode.Double:
130                                 ig.Emit (OpCodes.Ldc_R8, (double) value);
131                                 return;
132
133                         case TypeCode.Decimal: {
134                                 Decimal v = (decimal) value;
135                                 int [] words = Decimal.GetBits (v);
136                                 int power = (words [3] >> 16) & 0xff;
137                                 Type ti = typeof (int);
138
139                                 if (power == 0 && v <= int.MaxValue && v >= int.MinValue) {
140                                         ig.Emit (OpCodes.Ldc_I4, (int) v);
141
142                                         ig.Emit (OpCodes.Newobj, typeof (Decimal).GetConstructor (new Type [1] { ti }));
143                                         return;
144                                 }
145                                 ig.Emit (OpCodes.Ldc_I4, words [0]);
146                                 ig.Emit (OpCodes.Ldc_I4, words [1]);
147                                 ig.Emit (OpCodes.Ldc_I4, words [2]);
148                                 // sign
149                                 ig.Emit (OpCodes.Ldc_I4, words [3] >> 31);
150
151                                 // power
152                                 ig.Emit (OpCodes.Ldc_I4, power);
153
154                                 ig.Emit (OpCodes.Newobj, typeof (Decimal).GetConstructor (new Type [5] { ti, ti, ti, typeof(bool), typeof(byte) }));
155                                 return;
156                         }
157
158                         case TypeCode.DateTime: {
159                                 var date = (DateTime) value;
160                                 var local = ig.DeclareLocal (typeof (DateTime));
161
162                                 ig.Emit (OpCodes.Ldloca, local);
163                                 ig.Emit (OpCodes.Ldc_I8, date.Ticks);
164                                 ig.Emit (OpCodes.Ldc_I4, (int) date.Kind);
165                                 ig.Emit (OpCodes.Call, typeof (DateTime).GetConstructor (new [] { typeof (long), typeof (DateTimeKind) }));
166                                 ig.Emit (OpCodes.Ldloc, local);
167
168                                 return;
169                         }
170
171                         case TypeCode.DBNull:
172                                 ig.Emit (OpCodes.Ldsfld, typeof (DBNull).GetField ("Value", BindingFlags.Public | BindingFlags.Static));
173                                 return;
174
175                         case TypeCode.String:
176                                 EmitIfNotNull (ec, c => c.ig.Emit (OpCodes.Ldstr, (string) value));
177                                 return;
178
179                         case TypeCode.Object:
180                                 EmitIfNotNull (ec, c => c.EmitReadGlobal (value));
181                                 return;
182                         }
183
184                         throw new NotImplementedException (String.Format ("No support for constants of type {0} yet", Type));
185                 }
186
187                 void EmitIfNotNull (EmitContext ec, Action<EmitContext> emit)
188                 {
189                         if (value == null) {
190                                 ec.ig.Emit (OpCodes.Ldnull);
191                                 return;
192                         }
193
194                         emit (ec);
195                 }
196         }
197 }