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