2002-03-04 Miguel de Icaza <miguel@ximian.com>
[mono.git] / mcs / mcs / 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                 public void DefineEnum (object parent_builder)
77                 {
78                         TypeAttributes attr = TypeAttributes.Class | TypeAttributes.Sealed;
79
80                         UnderlyingType = RootContext.TypeManager.LookupType (BaseType);
81                         
82                         if (UnderlyingType != TypeManager.int32_type &&
83                             UnderlyingType != TypeManager.uint32_type &&
84                             UnderlyingType != TypeManager.int64_type &&
85                             UnderlyingType != TypeManager.uint64_type &&
86                             UnderlyingType != TypeManager.short_type &&
87                             UnderlyingType != TypeManager.ushort_type &&
88                             UnderlyingType != TypeManager.byte_type  &&
89                             UnderlyingType != TypeManager.sbyte_type) {
90                                 Report.Error (1008, Location,
91                                               "Type byte, sbyte, short, ushort, int, uint, " +
92                                               "long, or ulong expected");
93                                 return;
94                         }
95
96                         if (parent_builder is ModuleBuilder) {
97                                 ModuleBuilder builder = (ModuleBuilder) parent_builder;
98
99                                 if ((ModFlags & Modifiers.PUBLIC) != 0)
100                                         attr |= TypeAttributes.Public;
101                                 else
102                                         attr |= TypeAttributes.NotPublic;
103                                 
104                                 TypeBuilder = builder.DefineType (Name, attr, TypeManager.enum_type);
105                         } else {
106                                 TypeBuilder builder = (System.Reflection.Emit.TypeBuilder) parent_builder;
107
108                                 if ((ModFlags & Modifiers.PUBLIC) != 0)
109                                         attr |= TypeAttributes.NestedPublic;
110                                 else
111                                         attr |= TypeAttributes.NestedPrivate;
112
113                                 
114                                 TypeBuilder = builder.DefineNestedType (
115                                         Basename, attr, TypeManager.enum_type);
116                         }
117
118                         TypeBuilder.DefineField ("value__", UnderlyingType,
119                                                  FieldAttributes.Public | FieldAttributes.SpecialName
120                                                  | FieldAttributes.RTSpecialName);
121
122                         RootContext.TypeManager.AddEnumType (Name, TypeBuilder, this);
123
124                         return;
125                 }
126
127                 bool IsValidEnumConstant (Expression e)
128                 {
129                         if (!(e is Constant))
130                                 return false;
131
132                         if (e is IntConstant || e is UIntConstant || e is LongConstant ||
133                             e is ByteConstant || e is SByteConstant || e is ShortConstant ||
134                             e is UShortConstant || e is ULongConstant || e is EnumConstant)
135                                 return true;
136                         else
137                                 return false;
138                 }
139
140                 object GetNextDefaultValue (object default_value)
141                 {
142                         if (UnderlyingType == TypeManager.int32_type) {
143                                 int i = (int) default_value;
144                                 
145                                 if (i < System.Int32.MaxValue)
146                                         return ++i;
147                                 else
148                                         return null;
149                         } else if (UnderlyingType == TypeManager.uint32_type) {
150                                 uint i = (uint) default_value;
151
152                                 if (i < System.UInt32.MaxValue)
153                                         return ++i;
154                                 else
155                                         return null;
156                         } else if (UnderlyingType == TypeManager.int64_type) {
157                                 long i = (long) default_value;
158
159                                 if (i < System.Int64.MaxValue)
160                                         return ++i;
161                                 else
162                                         return null;
163                         } else if (UnderlyingType == TypeManager.uint64_type) {
164                                 ulong i = (ulong) default_value;
165
166                                 if (i < System.UInt64.MaxValue)
167                                         return ++i;
168                                 else
169                                         return null;
170                         } else if (UnderlyingType == TypeManager.short_type) {
171                                 short i = (short) default_value;
172
173                                 if (i < System.Int16.MaxValue)
174                                         return ++i;
175                                 else
176                                         return null;
177                         } else if (UnderlyingType == TypeManager.ushort_type) {
178                                 ushort i = (ushort) default_value;
179
180                                 if (i < System.UInt16.MaxValue)
181                                         return ++i;
182                                 else
183                                         return null;
184                         } else if (UnderlyingType == TypeManager.byte_type) {
185                                 byte i = (byte) default_value;
186
187                                 if (i < System.Byte.MaxValue)
188                                         return ++i;
189                                 else
190                                         return null;
191                         } else if (UnderlyingType == TypeManager.sbyte_type) {
192                                 sbyte i = (sbyte) default_value;
193
194                                 if (i < System.SByte.MaxValue)
195                                         return ++i;
196                                 else
197                                         return null;
198                         }
199
200                         return null;
201                 }
202
203                 void Error_ConstantValueCannotBeConverted (object val, Location loc)
204                 {
205                         if (val is Constant)
206                                 Report.Error (31, loc, "Constant value '" + ((Constant) val).AsString () +
207                                               "' cannot be converted" +
208                                               " to a " + TypeManager.CSharpName (UnderlyingType));
209                         else 
210                                 Report.Error (31, loc, "Constant value '" + val +
211                                               "' cannot be converted" +
212                                               " to a " + TypeManager.CSharpName (UnderlyingType));
213                         return;
214                 }
215
216                 /// <summary>
217                 ///  This is used to lookup the value of an enum member. If the member is undefined,
218                 ///  it attempts to define it and return its value
219                 /// </summary>
220                 public object LookupEnumValue (EmitContext ec, string name, Location loc)
221                 {
222                         object default_value = null;
223                         Constant c = null;
224
225                         default_value = member_to_value [name];
226
227                         if (default_value != null)
228                                 return default_value;
229
230                         if (!defined_names.Contains (name)) {
231                                 Report.Error (117, loc, "'"+ Name + "' does not contain a definition for '"
232                                               + name + "'");
233                                 return null;
234                         }
235
236                         //
237                         // So if the above doesn't happen, we have a member that is undefined
238                         // We now proceed to define it 
239                         //
240                         Expression val = this [name];
241
242                         if (val == null) {
243                                 
244                                 int idx = ordered_enums.IndexOf (name);
245
246                                 if (idx == 0)
247                                         default_value = 0;
248                                 else {
249                                         for (int i = 0; i < idx; ++i) {
250                                                 string n = (string) ordered_enums [i];
251                                                 Location m_loc = (Mono.CSharp.Location)
252                                                         member_to_location [n];
253                                                 default_value = LookupEnumValue (ec, n, m_loc);
254                                         }
255                                         
256                                         default_value = GetNextDefaultValue (default_value);
257                                 }
258                                 
259                         } else {
260                                 val = val.Resolve (ec);
261                                 
262                                 if (val == null) {
263                                         Report.Error (-12, loc, "Definition is circular.");
264                                         return null;
265                                 }       
266
267                                 if (IsValidEnumConstant (val)) {
268                                         c = (Constant) val;
269                                         default_value = c.GetValue ();
270                                         
271                                         if (default_value == null) {
272                                                 Error_ConstantValueCannotBeConverted (c, loc);
273                                                 return null;
274                                         }
275                                         
276                                 } else {
277                                         Report.Error (
278                                                 1008, loc,
279                                                 "Type byte, sbyte, short, ushort, int, uint, long, or " +
280                                                 "ulong expected");
281                                         return null;
282                                 }
283                         }
284
285                         FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
286                                         | FieldAttributes.Literal;
287                         
288                         FieldBuilder fb = TypeBuilder.DefineField (name, UnderlyingType, attr);
289                         
290                         try {
291                                 default_value = Convert.ChangeType (default_value, UnderlyingType);
292                         } catch {
293                                 Error_ConstantValueCannotBeConverted (c, loc);
294                                 return null;
295                         }
296
297                         fb.SetConstant (default_value);
298                         field_builders.Add (fb);
299                         member_to_value [name] = default_value;
300
301                         if (!TypeManager.RegisterFieldValue (fb, default_value))
302                                 return null;
303                         
304                         return default_value;
305                 }
306                 
307                 public override bool Define (TypeContainer parent)
308                 {
309                         //
310                         // If there was an error during DefineEnum, return
311                         //
312                         if (TypeBuilder == null)
313                                 return false;
314                         
315                         EmitContext ec = new EmitContext (parent, this, Location, null,
316                                                           UnderlyingType, ModFlags, false);
317                         
318                         object default_value = 0;
319                         
320                         FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
321                                              | FieldAttributes.Literal;
322
323                         
324                         foreach (string name in ordered_enums) {
325                                 //
326                                 // Have we already been defined, thanks to some cross-referencing ?
327                                 // 
328                                 if (member_to_value.Contains (name))
329                                         continue;
330                                 
331                                 Location loc = (Mono.CSharp.Location) member_to_location [name];
332
333                                 if (this [name] != null) {
334                                         default_value = LookupEnumValue (ec, name, loc);
335
336                                         if (default_value == null)
337                                                 return true;
338
339                                 } else {
340                                         FieldBuilder fb = TypeBuilder.DefineField (
341                                                 name, UnderlyingType, attr);
342                                         
343                                         if (default_value == null) {
344                                            Report.Error (543, loc, "Enumerator value for '" + name + "' is too large to " +
345                                                               "fit in its type");
346                                                 return false;
347                                         }
348                                         
349                                         try {
350                                                 default_value = Convert.ChangeType (default_value, UnderlyingType);
351                                         } catch {
352                                                 Error_ConstantValueCannotBeConverted (default_value, loc);
353                                                 return false;
354                                         }
355
356                                         fb.SetConstant (default_value);
357                                         field_builders.Add (fb);
358                                         member_to_value [name] = default_value;
359                                         
360                                         if (!TypeManager.RegisterFieldValue (fb, default_value))
361                                                 return false;
362                                 }
363
364                                 default_value = GetNextDefaultValue (default_value);
365                         }
366                         
367                         Attribute.ApplyAttributes (ec, TypeBuilder, this, OptAttributes, Location);
368
369                         return true;
370                 }
371                 
372                 //
373                 // Hack around System.Reflection as found everywhere else
374                 //
375                 public MemberInfo [] FindMembers (MemberTypes mt, BindingFlags bf,
376                                                   MemberFilter filter, object criteria)
377                 {
378                         ArrayList members = new ArrayList ();
379
380                         if ((mt & MemberTypes.Field) != 0) {
381                                 foreach (FieldBuilder fb in field_builders)
382                                         if (filter (fb, criteria) == true)
383                                                 members.Add (fb);
384                         }
385
386                         int count = members.Count;
387
388                         if (count > 0) {
389                                 MemberInfo [] mi = new MemberInfo [count];
390                                 members.CopyTo (mi, 0);
391                                 return mi;
392                         }
393
394                         return null;
395                 }
396
397                 public ArrayList ValueNames {
398                         get {
399                                 return ordered_enums;
400                         }
401                 }
402
403                 // indexer
404                 public Expression this [string name] {
405                         get {
406                                 return (Expression) defined_names [name];
407                         }
408                 }
409         }
410 }