2002-05-31 Rafael Teixeira <rafaelteixeirabr@hotmail.com>
[mono.git] / mcs / mbas / enum.cs
1 //
2 // enum.cs: Enum handling.
3 //
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 //         Ravi Pratap     (ravi@ximian.com)
6 //
7 // Licensed under the terms of the GNU GPL
8 //
9 // (C) 2001 Ximian, Inc (http://www.ximian.com)
10 //
11
12 using System;
13 using System.Collections;
14 using System.Reflection;
15 using System.Reflection.Emit;
16
17 namespace Mono.CSharp {
18
19         /// <summary>
20         ///   Enumeration container
21         /// </summary>
22         public class Enum : DeclSpace {
23
24                 ArrayList ordered_enums;
25                 public readonly string BaseType;
26                 public Attributes  OptAttributes;
27                 
28                 public Type UnderlyingType;
29
30                 Hashtable member_to_location;
31
32                 //
33                 // This is for members that have been defined
34                 //
35                 Hashtable member_to_value;
36                 
37                 ArrayList field_builders;
38                 
39                 public const int AllowedModifiers =
40                         Modifiers.NEW |
41                         Modifiers.PUBLIC |
42                         Modifiers.PROTECTED |
43                         Modifiers.INTERNAL |
44                         Modifiers.PRIVATE;
45
46                 public Enum (TypeContainer parent, string type, int mod_flags, string name, Attributes attrs, Location l)
47                         : base (parent, name, l)
48                 {
49                         this.BaseType = type;
50                         ModFlags = Modifiers.Check (AllowedModifiers, mod_flags, Modifiers.PUBLIC, l);
51                         OptAttributes = attrs;
52
53                         ordered_enums = new ArrayList ();
54                         member_to_location = new Hashtable ();
55                         member_to_value = new Hashtable ();
56                         field_builders = new ArrayList ();
57                 }
58
59                 /// <summary>
60                 ///   Adds @name to the enumeration space, with @expr
61                 ///   being its definition.  
62                 /// </summary>
63                 public AdditionResult AddEnumMember (string name, Expression expr, Location loc)
64                 {
65                         if (defined_names.Contains (name))
66                                 return AdditionResult.NameExists;
67
68                         DefineName (name, expr);
69
70                         ordered_enums.Add (name);
71                         member_to_location.Add (name, loc);
72                         
73                         return AdditionResult.Success;
74                 }
75
76                 //
77                 // This is used by corlib compilation: we map from our
78                 // type to a type that is consumable by the DefineField
79                 //
80                 Type MapToInternalType (Type t)
81                 {
82                         if (t == TypeManager.int32_type)
83                                 return typeof (int);
84                         if (t == TypeManager.int64_type)
85                                 return typeof (long);
86                         if (t == TypeManager.uint32_type)
87                                 return typeof (uint);
88                         if (t == TypeManager.uint64_type)
89                                 return typeof (ulong);
90                         if (t == TypeManager.float_type)
91                                 return typeof (float);
92                         if (t == TypeManager.double_type)
93                                 return typeof (double);
94                         if (t == TypeManager.byte_type)
95                                 return typeof (byte);
96                         if (t == TypeManager.sbyte_type)
97                                 return typeof (sbyte);
98                         if (t == TypeManager.char_type)
99                                 return typeof (char);
100                         if (t == TypeManager.short_type)
101                                 return typeof (short);
102                         if (t == TypeManager.ushort_type)
103                                 return typeof (ushort);
104
105                         throw new Exception ();
106                 }
107                 
108                 public override TypeBuilder DefineType ()
109                 {
110                         if (TypeBuilder != null)
111                                 return TypeBuilder;
112                         
113                         TypeAttributes attr = TypeAttributes.Class | TypeAttributes.Sealed;
114
115                         UnderlyingType = TypeManager.LookupType (BaseType);
116                         
117                         if (UnderlyingType != TypeManager.int32_type &&
118                             UnderlyingType != TypeManager.uint32_type &&
119                             UnderlyingType != TypeManager.int64_type &&
120                             UnderlyingType != TypeManager.uint64_type &&
121                             UnderlyingType != TypeManager.short_type &&
122                             UnderlyingType != TypeManager.ushort_type &&
123                             UnderlyingType != TypeManager.byte_type  &&
124                             UnderlyingType != TypeManager.sbyte_type) {
125                                 Report.Error (1008, Location,
126                                               "Type byte, sbyte, short, ushort, int, uint, " +
127                                               "long, or ulong expected (got: " +
128                                               TypeManager.CSharpName (UnderlyingType) + ")");
129                                 return null;
130                         }
131
132                         if (IsTopLevel) {
133                                 ModuleBuilder builder = CodeGen.ModuleBuilder;
134
135                                 if ((ModFlags & Modifiers.PUBLIC) != 0)
136                                         attr |= TypeAttributes.Public;
137                                 else
138                                         attr |= TypeAttributes.NotPublic;
139                                 
140                                 TypeBuilder = builder.DefineType (Name, attr, TypeManager.enum_type);
141                         } else {
142                                 TypeBuilder builder = Parent.TypeBuilder;
143
144                                 if ((ModFlags & Modifiers.PUBLIC) != 0)
145                                         attr |= TypeAttributes.NestedPublic;
146                                 else
147                                         attr |= TypeAttributes.NestedPrivate;
148
149                                 
150                                 TypeBuilder = builder.DefineNestedType (
151                                         Basename, attr, TypeManager.enum_type);
152                         }
153
154                         //
155                         // Call MapToInternalType for corlib
156                         //
157                         TypeBuilder.DefineField ("value__", UnderlyingType,
158                                                  FieldAttributes.Public | FieldAttributes.SpecialName
159                                                  | FieldAttributes.RTSpecialName);
160
161                         TypeManager.AddEnumType (Name, TypeBuilder, this);
162
163                         return TypeBuilder;
164                 }
165
166                 bool IsValidEnumConstant (Expression e)
167                 {
168                         if (!(e is Constant))
169                                 return false;
170
171                         if (e is IntConstant || e is UIntConstant || e is LongConstant ||
172                             e is ByteConstant || e is SByteConstant || e is ShortConstant ||
173                             e is UShortConstant || e is ULongConstant || e is EnumConstant)
174                                 return true;
175                         else
176                                 return false;
177                 }
178
179                 object GetNextDefaultValue (object default_value)
180                 {
181                         if (UnderlyingType == TypeManager.int32_type) {
182                                 int i = (int) default_value;
183                                 
184                                 if (i < System.Int32.MaxValue)
185                                         return ++i;
186                                 else
187                                         return null;
188                         } else if (UnderlyingType == TypeManager.uint32_type) {
189                                 uint i = (uint) default_value;
190
191                                 if (i < System.UInt32.MaxValue)
192                                         return ++i;
193                                 else
194                                         return null;
195                         } else if (UnderlyingType == TypeManager.int64_type) {
196                                 long i = (long) default_value;
197
198                                 if (i < System.Int64.MaxValue)
199                                         return ++i;
200                                 else
201                                         return null;
202                         } else if (UnderlyingType == TypeManager.uint64_type) {
203                                 ulong i = (ulong) default_value;
204
205                                 if (i < System.UInt64.MaxValue)
206                                         return ++i;
207                                 else
208                                         return null;
209                         } else if (UnderlyingType == TypeManager.short_type) {
210                                 short i = (short) default_value;
211
212                                 if (i < System.Int16.MaxValue)
213                                         return ++i;
214                                 else
215                                         return null;
216                         } else if (UnderlyingType == TypeManager.ushort_type) {
217                                 ushort i = (ushort) default_value;
218
219                                 if (i < System.UInt16.MaxValue)
220                                         return ++i;
221                                 else
222                                         return null;
223                         } else if (UnderlyingType == TypeManager.byte_type) {
224                                 byte i = (byte) default_value;
225
226                                 if (i < System.Byte.MaxValue)
227                                         return ++i;
228                                 else
229                                         return null;
230                         } else if (UnderlyingType == TypeManager.sbyte_type) {
231                                 sbyte i = (sbyte) default_value;
232
233                                 if (i < System.SByte.MaxValue)
234                                         return ++i;
235                                 else
236                                         return null;
237                         }
238
239                         return null;
240                 }
241
242                 void Error_ConstantValueCannotBeConverted (object val, Location loc)
243                 {
244                         if (val is Constant)
245                                 Report.Error (31, loc, "Constant value '" + ((Constant) val).AsString () +
246                                               "' cannot be converted" +
247                                               " to a " + TypeManager.CSharpName (UnderlyingType));
248                         else 
249                                 Report.Error (31, loc, "Constant value '" + val +
250                                               "' cannot be converted" +
251                                               " to a " + TypeManager.CSharpName (UnderlyingType));
252                         return;
253                 }
254
255                 /// <summary>
256                 ///  This is used to lookup the value of an enum member. If the member is undefined,
257                 ///  it attempts to define it and return its value
258                 /// </summary>
259                 public object LookupEnumValue (EmitContext ec, string name, Location loc)
260                 {
261                         object default_value = null;
262                         Constant c = null;
263
264                         default_value = member_to_value [name];
265
266                         if (default_value != null)
267                                 return default_value;
268
269                         if (!defined_names.Contains (name)) {
270                                 Report.Error (117, loc, "'"+ Name + "' does not contain a definition for '"
271                                               + name + "'");
272                                 return null;
273                         }
274
275                         //
276                         // So if the above doesn't happen, we have a member that is undefined
277                         // We now proceed to define it 
278                         //
279                         Expression val = this [name];
280
281                         if (val == null) {
282                                 
283                                 int idx = ordered_enums.IndexOf (name);
284
285                                 if (idx == 0)
286                                         default_value = 0;
287                                 else {
288                                         for (int i = 0; i < idx; ++i) {
289                                                 string n = (string) ordered_enums [i];
290                                                 Location m_loc = (Mono.CSharp.Location)
291                                                         member_to_location [n];
292                                                 default_value = LookupEnumValue (ec, n, m_loc);
293                                         }
294                                         
295                                         default_value = GetNextDefaultValue (default_value);
296                                 }
297                                 
298                         } else {
299                                 bool old = ec.InEnumContext;
300                                 ec.InEnumContext = true;
301                                 val = val.Resolve (ec);
302                                 ec.InEnumContext = old;
303                                 
304                                 if (val == null) {
305                                         Report.Error (-12, loc, "Definition is circular.");
306                                         return null;
307                                 }
308
309                                 if (IsValidEnumConstant (val)) {
310                                         c = (Constant) val;
311                                         default_value = c.GetValue ();
312                                         
313                                         if (default_value == null) {
314                                                 Error_ConstantValueCannotBeConverted (c, loc);
315                                                 return null;
316                                         }
317                                         
318                                 } else {
319                                         Report.Error (
320                                                 1008, loc,
321                                                 "Type byte, sbyte, short, ushort, int, uint, long, or " +
322                                                 "ulong expected (have: " + val + ")");
323                                         return null;
324                                 }
325                         }
326
327                         FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
328                                         | FieldAttributes.Literal;
329                         
330                         FieldBuilder fb = TypeBuilder.DefineField (name, UnderlyingType, attr);
331
332                         try {
333                                 default_value = Convert.ChangeType (default_value, UnderlyingType);
334                         } catch {
335                                 Error_ConstantValueCannotBeConverted (c, loc);
336                                 return null;
337                         }
338
339                         fb.SetConstant (default_value);
340                         field_builders.Add (fb);
341                         member_to_value [name] = default_value;
342
343                         if (!TypeManager.RegisterFieldValue (fb, default_value))
344                                 return null;
345                         
346                         return default_value;
347                 }
348                 
349                 public override bool Define (TypeContainer parent)
350                 {
351                         //
352                         // If there was an error during DefineEnum, return
353                         //
354                         if (TypeBuilder == null)
355                                 return false;
356                         
357                         EmitContext ec = new EmitContext (parent, this, Location, null,
358                                                           UnderlyingType, ModFlags, false);
359                         
360                         object default_value = 0;
361                         
362                         FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
363                                              | FieldAttributes.Literal;
364
365                         
366                         foreach (string name in ordered_enums) {
367                                 //
368                                 // Have we already been defined, thanks to some cross-referencing ?
369                                 // 
370                                 if (member_to_value.Contains (name))
371                                         continue;
372                                 
373                                 Location loc = (Mono.CSharp.Location) member_to_location [name];
374
375                                 if (this [name] != null) {
376                                         default_value = LookupEnumValue (ec, name, loc);
377
378                                         if (default_value == null)
379                                                 return true;
380
381                                 } else {
382                                         FieldBuilder fb = TypeBuilder.DefineField (
383                                                 name, UnderlyingType, attr);
384                                         
385                                         if (default_value == null) {
386                                            Report.Error (543, loc, "Enumerator value for '" + name + "' is too large to " +
387                                                               "fit in its type");
388                                                 return false;
389                                         }
390                                         
391                                         try {
392                                                 default_value = Convert.ChangeType (default_value, UnderlyingType);
393                                         } catch {
394                                                 Error_ConstantValueCannotBeConverted (default_value, loc);
395                                                 return false;
396                                         }
397
398                                         fb.SetConstant (default_value);
399                                         field_builders.Add (fb);
400                                         member_to_value [name] = default_value;
401                                         
402                                         if (!TypeManager.RegisterFieldValue (fb, default_value))
403                                                 return false;
404                                 }
405
406                                 default_value = GetNextDefaultValue (default_value);
407                         }
408                         
409                         Attribute.ApplyAttributes (ec, TypeBuilder, this, OptAttributes, Location);
410
411                         return true;
412                 }
413                 
414                 //
415                 // Hack around System.Reflection as found everywhere else
416                 //
417                 public MemberInfo [] FindMembers (MemberTypes mt, BindingFlags bf,
418                                                   MemberFilter filter, object criteria)
419                 {
420                         ArrayList members = new ArrayList ();
421
422                         if ((mt & MemberTypes.Field) != 0) {
423                                 foreach (FieldBuilder fb in field_builders)
424                                         if (filter (fb, criteria) == true)
425                                                 members.Add (fb);
426                         }
427
428                         int count = members.Count;
429
430                         if (count > 0) {
431                                 MemberInfo [] mi = new MemberInfo [count];
432                                 members.CopyTo (mi, 0);
433                                 return mi;
434                         }
435
436                         return null;
437                 }
438
439                 public ArrayList ValueNames {
440                         get {
441                                 return ordered_enums;
442                         }
443                 }
444
445                 // indexer
446                 public Expression this [string name] {
447                         get {
448                                 return (Expression) defined_names [name];
449                         }
450                 }
451         }
452 }