2001-11-18 Ravi Pratap <ravi@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 CIR {
18
19         public class Enum : DeclSpace {
20
21                 ArrayList ordered_enums;
22                 public readonly string BaseType;
23                 public readonly string EnumName;
24                 int mod_flags;
25                 public TypeBuilder EnumBuilder;
26                 public Attributes  OptAttributes;
27                 
28                 Type UnderlyingType;
29
30                 public readonly RootContext RootContext;
31
32                 Hashtable member_to_location;
33                 ArrayList field_builders;
34
35                 public const int AllowedModifiers =
36                         Modifiers.NEW |
37                         Modifiers.PUBLIC |
38                         Modifiers.PROTECTED |
39                         Modifiers.INTERNAL |
40                         Modifiers.PRIVATE;
41
42                 public Enum (RootContext rc, string type, int mod_flags, string name, Attributes attrs, Location l)
43                         : base (name, l)
44                 {
45                         RootContext = rc;
46                         this.BaseType = type;
47                         this.EnumName = name;
48                         this.mod_flags = Modifiers.Check (AllowedModifiers, mod_flags, Modifiers.PUBLIC);
49                         OptAttributes = attrs;
50                         ordered_enums = new ArrayList ();
51                         member_to_location = new Hashtable ();
52                         field_builders = new ArrayList ();
53                 }
54
55                 // <summary>
56                 //   Adds @name to the enumeration space, with @expr
57                 //   being its definition.  
58                 // </summary>
59                 public AdditionResult AddEnumMember (string name, Expression expr, Location loc)
60                 {
61                         if (defined_names.Contains (name))
62                                 return AdditionResult.NameExists;
63
64                         DefineName (name, expr);
65
66                         ordered_enums.Add (name);
67                         member_to_location.Add (name, loc);
68                         
69                         return AdditionResult.Success;
70                 }
71
72                 public void DefineEnum (object parent_builder)
73                 {
74                         TypeAttributes attr = TypeAttributes.Class | TypeAttributes.Sealed;
75
76                         UnderlyingType = RootContext.TypeManager.LookupType (BaseType);
77
78                         if (UnderlyingType != TypeManager.int32_type && UnderlyingType != TypeManager.uint32_type &&
79                             UnderlyingType != TypeManager.int64_type && UnderlyingType != TypeManager.uint64_type &&
80                             UnderlyingType != TypeManager.short_type && UnderlyingType != TypeManager.ushort_type &&
81                             UnderlyingType != TypeManager.byte_type  && UnderlyingType != TypeManager.sbyte_type) {
82                                 Report.Error (1008, Location,
83                                               "Type byte, sbyte, short, ushort, int, uint, long, or ulong expected");
84                                 return;
85                         }
86
87                         if (parent_builder is ModuleBuilder) {
88                                 ModuleBuilder builder = (ModuleBuilder) parent_builder;
89
90                                 if ((ModFlags & Modifiers.PUBLIC) != 0)
91                                         attr |= TypeAttributes.Public;
92                                 else
93                                         attr |= TypeAttributes.NotPublic;
94                                 
95                                 EnumBuilder = builder.DefineType (EnumName, attr, TypeManager.enum_type);
96
97                         } else {
98                                 TypeBuilder builder = (TypeBuilder) parent_builder;
99
100                                 if ((ModFlags & Modifiers.PUBLIC) != 0)
101                                         attr |= TypeAttributes.NestedPublic;
102                                 else
103                                         attr |= TypeAttributes.NestedPrivate;
104                                 
105                                 EnumBuilder = builder.DefineNestedType (EnumName, attr, TypeManager.enum_type);
106                         }
107
108                         EnumBuilder.DefineField ("value__", UnderlyingType,
109                                                  FieldAttributes.Public | FieldAttributes.SpecialName);
110
111                         RootContext.TypeManager.AddEnumType (EnumName, EnumBuilder, this);
112
113                         return;
114                 }
115
116                 bool IsValidEnumLiteral (Expression e)
117                 {
118                         if (!(e is Literal))
119                                 return false;
120
121                         if (e is IntLiteral || e is UIntLiteral || e is LongLiteral || e is ULongLiteral)
122                                 return true;
123                         else
124                                 return false;
125                 }
126
127                 object GetNextDefaultValue (object default_value)
128                 {
129                         if (UnderlyingType == TypeManager.int32_type) {
130                                 int i = (int) default_value;
131                                 
132                                 if (i < System.Int32.MaxValue)
133                                         return ++i;
134                                 else
135                                         return null;
136                         } else if (UnderlyingType == TypeManager.uint32_type) {
137                                 uint i = (uint) default_value;
138
139                                 if (i < System.UInt32.MaxValue)
140                                         return ++i;
141                                 else
142                                         return null;
143                         } else if (UnderlyingType == TypeManager.int64_type) {
144                                 long i = (long) default_value;
145
146                                 if (i < System.Int64.MaxValue)
147                                         return ++i;
148                                 else
149                                         return null;
150                         } else if (UnderlyingType == TypeManager.uint64_type) {
151                                 ulong i = (ulong) default_value;
152
153                                 if (i < System.UInt64.MaxValue)
154                                         return ++i;
155                                 else
156                                         return null;
157                         } else if (UnderlyingType == TypeManager.short_type) {
158                                 short i = (short) default_value;
159
160                                 if (i < System.Int16.MaxValue)
161                                         return ++i;
162                                 else
163                                         return null;
164                         } else if (UnderlyingType == TypeManager.ushort_type) {
165                                 ushort i = (ushort) default_value;
166
167                                 if (i < System.UInt16.MaxValue)
168                                         return ++i;
169                                 else
170                                         return null;
171                         } else if (UnderlyingType == TypeManager.byte_type) {
172                                 byte i = (byte) default_value;
173
174                                 if (i < System.Byte.MaxValue)
175                                         return ++i;
176                                 else
177                                         return null;
178                         } else if (UnderlyingType == TypeManager.sbyte_type) {
179                                 sbyte i = (sbyte) default_value;
180
181                                 if (i < System.SByte.MaxValue)
182                                         return ++i;
183                                 else
184                                         return null;
185                         }
186
187                         return null;
188                 }
189
190                 void error31 (Literal l, Location loc)
191                 {
192                         Report.Error (31, loc, "Constant value '" + l.AsString () +
193                                       "' cannot be converted" +
194                                       " to a " + TypeManager.CSharpName (UnderlyingType));
195                         return;
196                 }
197                 
198                 public void Populate (TypeContainer tc)
199                 {
200                         //
201                         // If there was an error during DefineEnum, return
202                         //
203                         if (EnumBuilder == null){
204                                 return;
205                         }
206                         
207                         EmitContext ec = new EmitContext (tc, null, UnderlyingType, ModFlags);
208                         
209                         object default_value = 0;
210                         
211                         FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
212                                              | FieldAttributes.Literal;
213
214                         
215                         foreach (string name in ordered_enums) {
216                                 Expression e = this [name];
217                                 Location loc = (Location) member_to_location [name];
218
219                                 if (e != null) {
220                                         e = Expression.Reduce (ec, e);
221
222                                         if (IsValidEnumLiteral (e)) {
223                                                 Literal l = (Literal) e;
224                                                 default_value = l.GetValue ();
225
226                                                 if (default_value == null) {
227                                                         error31 (l, loc);
228                                                         return;
229                                                 }
230                                                 
231                                         } else {
232                                                 Report.Error (1008, loc,
233                                                   "Type byte, sbyte, short, ushort, int, uint, long, or ulong expected");
234                                                 return;
235                                         }
236                                 }
237                                 
238                                 FieldBuilder fb = EnumBuilder.DefineField (name, UnderlyingType, attr);
239
240                                 if (default_value == null) {
241                                         Report.Error (543, loc, "Enumerator value for '" + name + "' is too large to " +
242                                                       "fit in its type");
243                                         return;
244                                 }
245
246                                 try {
247                                         default_value = Convert.ChangeType (default_value, UnderlyingType);
248                                 } catch {
249                                         error31 ((Literal) e, loc);
250                                         return;
251                                 }
252
253                                 fb.SetConstant (default_value);
254                                 field_builders.Add (fb);
255
256                                 if (!TypeManager.RegisterField (fb, default_value))
257                                         return;
258
259                                 default_value = GetNextDefaultValue (default_value);
260                         }
261
262                         if (OptAttributes == null)
263                                 return;
264                         
265                         if (OptAttributes.AttributeSections == null)
266                                 return;
267                         
268                         foreach (AttributeSection asec in OptAttributes.AttributeSections) {
269                                 if (asec.Attributes == null)
270                                         continue;
271                                 
272                                 foreach (Attribute a in asec.Attributes) {
273                                         CustomAttributeBuilder cb = a.Resolve (ec);
274
275                                         if (cb == null)
276                                                 continue;
277                                         
278                                         EnumBuilder.SetCustomAttribute (cb);
279                                 }
280                         }
281                 }
282                 
283                 //
284                 // Hack around System.Reflection as found everywhere else
285                 //
286                 public MemberInfo [] FindMembers (MemberTypes mt, BindingFlags bf, MemberFilter filter, object criteria)
287                 {
288                         ArrayList members = new ArrayList ();
289
290                         if ((mt & MemberTypes.Field) != 0) {
291                                 foreach (FieldBuilder fb in field_builders)
292                                         if (filter (fb, criteria) == true)
293                                                 members.Add (fb);
294                         }
295
296                         int count = members.Count;
297
298                         if (count > 0) {
299                                 MemberInfo [] mi = new MemberInfo [count];
300                                 members.CopyTo (mi, 0);
301                                 return mi;
302                         }
303
304                         return null;
305                 }
306
307                 public void CloseEnum ()
308                 {
309                         EnumBuilder.CreateType ();
310                 }
311                 
312                 public ArrayList ValueNames {
313                         get {
314                                 return ordered_enums;
315                         }
316                 }
317
318                 public int ModFlags {
319                         get {
320                                 return mod_flags;
321                         }
322                 }
323                 
324                 // indexer
325                 public Expression this [string name] {
326                         get {
327                                 return (Expression) defined_names [name];
328                         }
329                 }
330         }
331 }