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