2005-07-13 Maverson Eduardo Schulze Rosa <maverson@gmail.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 //         Anirban Bhattacharjee (banirban@novell.com)
7 //         Jambunathan K (kjambunathan@novell.com)
8 //
9 // Licensed under the terms of the GNU GPL
10 //
11 // (C) 2001 Ximian, Inc (http://www.ximian.com)
12 //
13
14 using System;
15 using System.Collections;
16 using System.Reflection;
17 using System.Reflection.Emit;
18
19 namespace Mono.MonoBASIC {
20
21         public class EnumMember : MemberCore
22         {
23                 Enum parent_enum;
24                 Expression expr;
25                 int index;
26                 
27                 FieldBuilder builder;
28                 object enum_value;
29
30                 bool in_transit = false;
31                 FieldAttributes field_attrs = FieldAttributes.Public | FieldAttributes.Static
32                                                 | FieldAttributes.Literal;
33
34                 public EnumMember (Enum parent_enum, Expression expr, string name,
35                                    Location loc, Attributes attrs, int index):
36                         base (name, attrs, loc)
37                 {
38                         this.parent_enum = parent_enum;
39                         this.expr = expr;
40                         this.index = index;
41                 }
42
43                 public override void ApplyAttributeBuilder(Attribute a, CustomAttributeBuilder cb)
44                 {
45                         builder.SetCustomAttribute (cb);
46                 }
47
48                 public override AttributeTargets AttributeTargets {
49                         get {
50                                 return AttributeTargets.Field;
51                         }
52                 }
53
54                 public override bool Define (TypeContainer tc)
55                 {
56                         throw new NotImplementedException ();
57                 }
58
59                 public object GetValue ()
60                 {
61                         if (builder == null) 
62                                 DefineMember ();
63
64                         return enum_value;
65                 }
66
67                 public FieldBuilder DefineMember ()
68                 {
69                         if (builder != null)
70                                 return builder;
71
72                         DoDefineMember ();
73
74                         if (builder == null)
75                                 parent_enum.RemoveEnumMember (this.Name);
76
77                         return builder;
78                 }
79
80                 public FieldBuilder DoDefineMember ()
81                 {
82                         object default_value = null;
83                         Constant c = null;
84                         Type UnderlyingType = parent_enum.UnderlyingType;
85                         EmitContext ec = parent_enum.EmitContext;
86                         
87                         default_value = enum_value;
88                         
89                         if (in_transit) {
90                                 Report.Error (30500, Location, "The evaluation of the constant value for `" +
91                                               parent_enum.Name + "." + Name + "' involves a circular definition.");
92                                 return null;
93                         }
94
95                         //
96                         // So if the above doesn't happen, we have a member that is undefined
97                         // We now proceed to define it 
98                         //
99                         Expression val = expr;
100                         int idx = index;
101
102                         if (val == null) {
103                                 if (idx == 0)
104                                         default_value = 0;
105                                 else {
106                                         int i = idx - 1;
107                                         EnumMember em = parent_enum [i];
108
109                                         in_transit = true;
110                                         default_value = em.GetValue ();
111                                         in_transit = false;
112
113                                         if (default_value == null)
114                                                 return null;
115                                         
116                                         default_value = Enum.GetNextDefaultValue (default_value, UnderlyingType);
117                                 }
118                                 
119                         } else {
120                                 bool old = ec.InEnumContext;
121                                 ec.InEnumContext = true;
122                                 in_transit = true;
123                                 val = val.Resolve (ec);
124                                 in_transit = false;
125                                 ec.InEnumContext = old;
126
127                                 if (val == null)
128                                         return null;
129
130                                 if (! Enum.IsValidEnumConstant (val)) {
131                                         Report.Error (
132                                                 30650, Location,
133                                                 "Type byte, sbyte, short, ushort, int, uint, long, or " +
134                                                 "ulong expected (have: " + val + ")");
135                                         return null;
136                                 }
137
138                                 c = (Constant) val;
139                         
140                                 default_value = c.GetValue ();
141
142                                 if (default_value == null) {
143                                         Enum.Error_ConstantValueCannotBeConverted (c, Location, UnderlyingType);
144                                         return null;
145                                 }
146                         }
147
148                         builder = parent_enum.TypeBuilder.DefineField (Name, parent_enum.TypeBuilder, field_attrs);
149
150                         try {
151                                 //FXME: ChangeType is not the right thing to do. 
152                                 default_value = TypeManager.ChangeType (default_value, UnderlyingType);
153                         } catch {
154                                 Enum.Error_ConstantValueCannotBeConverted (c, Location, UnderlyingType);
155                                 return null;
156                         }
157
158                         enum_value = default_value;
159
160                         builder.SetConstant (enum_value);
161
162                         if (!TypeManager.RegisterFieldValue (builder, enum_value))
163                                 return null;
164
165                         if (OptAttributes != null)
166                                 OptAttributes.Emit (ec, this);
167
168                         return builder;
169                 }
170
171         }
172         
173
174         /// <summary>
175         ///   Enumeration container
176         /// </summary>
177         public class Enum : DeclSpace {
178                 ArrayList ordered_enums;
179                 public Expression BaseType;
180                 public Type UnderlyingType;
181                 ArrayList field_builders;
182                 
183                 public const int AllowedModifiers =
184                         Modifiers.NEW |
185                         Modifiers.PUBLIC |
186                         Modifiers.PROTECTED |
187                         Modifiers.INTERNAL |
188                         Modifiers.PRIVATE;
189
190                 EmitContext emit_context;
191
192                 public Enum (TypeContainer parent, Expression type, int mod_flags, string name, Attributes attrs, Location l)
193                         : base (parent, name, attrs, l)
194                 {
195                         this.BaseType = type;
196                         ModFlags = Modifiers.Check (AllowedModifiers, mod_flags,
197                                                     IsTopLevel ? Modifiers.INTERNAL : Modifiers.PUBLIC, l);
198
199                         ordered_enums = new ArrayList ();
200                         field_builders = new ArrayList ();
201                 }
202
203                 public override AttributeTargets AttributeTargets {
204                         get {
205                                 return AttributeTargets.Enum;
206                         }
207                 }
208
209                 /// <summary>
210                 ///   Adds @name to the enumeration space, with @expr
211                 ///   being its definition.  
212                 /// </summary>
213                 public AdditionResult AddEnumMember (string name, Expression expr, Location loc,
214                                                      Attributes opt_attrs)
215                 {
216                         int index;
217                         EnumMember em;
218                         
219                         if (defined_names.Contains (name))
220                                 return AdditionResult.NameExists;
221
222                         index = ordered_enums.Add (name);
223                         em = new EnumMember (this, expr, name, loc, opt_attrs, index);
224                         DefineName (name, em);
225
226                         return AdditionResult.Success;
227                 }
228
229                 public void RemoveEnumMember (string name)
230                 {
231                         defined_names.Remove (name);
232                 }
233
234                 public override TypeBuilder DefineType ()
235                 {
236                         if (TypeBuilder != null)
237                                 return TypeBuilder;
238
239                         emit_context = new EmitContext (Parent, this, Location, null,
240                                                           UnderlyingType, ModFlags, false);
241
242                         TypeAttributes attr = Modifiers.TypeAttr (ModFlags, IsTopLevel);
243
244                         attr |= TypeAttributes.Class | TypeAttributes.Sealed;
245
246                         UnderlyingType = ResolveType (BaseType, false, Location);
247
248                         if (UnderlyingType != TypeManager.int32_type &&
249                             UnderlyingType != TypeManager.uint32_type &&
250                             UnderlyingType != TypeManager.int64_type &&
251                             UnderlyingType != TypeManager.uint64_type &&
252                             UnderlyingType != TypeManager.short_type &&
253                             UnderlyingType != TypeManager.ushort_type &&
254                             UnderlyingType != TypeManager.byte_type  &&
255                             UnderlyingType != TypeManager.sbyte_type) {
256                                 Report.Error (30650, Location,
257                                               "Type byte, sbyte, short, ushort, int, uint, " +
258                                               "long, or ulong expected (got: " +
259                                               TypeManager.MonoBASIC_Name (UnderlyingType) + ")");
260                                 return null;
261                         }
262
263                         if (IsTopLevel) {
264                                 ModuleBuilder builder = CodeGen.ModuleBuilder;
265
266                                 TypeBuilder = builder.DefineType (Name, attr, TypeManager.enum_type);
267                         } else {
268                                 TypeBuilder builder = Parent.TypeBuilder;
269
270                                 TypeBuilder = builder.DefineNestedType (
271                                         Basename, attr, TypeManager.enum_type);
272                         }
273
274                         TypeBuilder.DefineField ("value__", UnderlyingType,
275                                                  FieldAttributes.Public | FieldAttributes.SpecialName
276                                                  | FieldAttributes.RTSpecialName);
277
278                         TypeManager.AddEnumType (Name, TypeBuilder, this);
279
280                         return TypeBuilder;
281                 }
282
283                 public static bool IsValidEnumConstant (Expression e)
284                 {
285                         if (!(e is Constant))
286                                 return false;
287
288                         if (e is IntConstant || e is UIntConstant || e is LongConstant ||
289                             e is ByteConstant || e is SByteConstant || e is ShortConstant ||
290                             e is UShortConstant || e is ULongConstant || e is EnumConstant ||
291                             e is DoubleConstant)
292                                 return true;
293                         else
294                                 return false;
295                 }
296
297                 static public object GetNextDefaultValue (object default_value, Type UnderlyingType)
298                 {
299                         if (UnderlyingType == TypeManager.int32_type) {
300                                 int i = (int) default_value;
301                                 
302                                 if (i < System.Int32.MaxValue)
303                                         return ++i;
304                                 else
305                                         return null;
306                         } else if (UnderlyingType == TypeManager.uint32_type) {
307                                 uint i = (uint) default_value;
308
309                                 if (i < System.UInt32.MaxValue)
310                                         return ++i;
311                                 else
312                                         return null;
313                         } else if (UnderlyingType == TypeManager.int64_type) {
314                                 long i = (long) default_value;
315
316                                 if (i < System.Int64.MaxValue)
317                                         return ++i;
318                                 else
319                                         return null;
320                         } else if (UnderlyingType == TypeManager.uint64_type) {
321                                 ulong i = (ulong) default_value;
322
323                                 if (i < System.UInt64.MaxValue)
324                                         return ++i;
325                                 else
326                                         return null;
327                         } else if (UnderlyingType == TypeManager.short_type) {
328                                 short i = (short) default_value;
329
330                                 if (i < System.Int16.MaxValue)
331                                         return ++i;
332                                 else
333                                         return null;
334                         } else if (UnderlyingType == TypeManager.ushort_type) {
335                                 ushort i = (ushort) default_value;
336
337                                 if (i < System.UInt16.MaxValue)
338                                         return ++i;
339                                 else
340                                         return null;
341                         } else if (UnderlyingType == TypeManager.byte_type) {
342                                 byte i = (byte) default_value;
343
344                                 if (i < System.Byte.MaxValue)
345                                         return ++i;
346                                 else
347                                         return null;
348                         } else if (UnderlyingType == TypeManager.sbyte_type) {
349                                 sbyte i = (sbyte) default_value;
350
351                                 if (i < System.SByte.MaxValue)
352                                         return ++i;
353                                 else
354                                         return null;
355                         }
356
357                         return null;
358                 }
359
360                 public static void Error_ConstantValueCannotBeConverted (object val, Location loc, Type UnderlyingType)
361                 {
362                         if (val is Constant)
363                                 Report.Error (30439, loc, "Constant value '" + ((Constant) val).AsString () +
364                                               "' cannot be converted" +
365                                               " to a " + TypeManager.MonoBASIC_Name (UnderlyingType));
366                         else 
367                                 Report.Error (30439, loc, "Constant value '" + val +
368                                               "' cannot be converted" +
369                                               " to a " + TypeManager.MonoBASIC_Name (UnderlyingType));
370                         return;
371                 }
372
373                 /// <summary>
374                 ///  This is used to lookup the value of an enum member. If the member is undefined,
375                 ///  it attempts to define it and return its value
376                 /// </summary>
377                 public object LookupEnumValue (string name)
378                 {
379                         EnumMember em;
380
381                         // first check whether the requested name is there
382                         // in the member list of enum
383
384                         em = this [name];
385
386                         if (em == null)
387                                 return null;
388                         
389                         name = em.Name;
390                         FieldBuilder fb = em.DefineMember ();
391                         
392                         if (fb == null) {
393                                 return null;
394                         } 
395
396                         if (! field_builders.Contains (fb)) {
397                                 field_builders.Add (fb);
398                         }
399
400                         return em.GetValue ();
401                 }
402
403                 public override bool DefineMembers (TypeContainer parent)
404                 {
405                         return true;
406                 }
407                 
408                 public override bool Define (TypeContainer parent)
409                 {
410                         if (TypeBuilder == null)
411                                 return false;
412                         /*
413                         EmitContext ec = new EmitContext (parent, this, Location, null,
414                                                           UnderlyingType, ModFlags, false);
415                         */
416                         foreach (string name in ordered_enums) {
417                                 EnumMember em = this [name];
418                                 FieldBuilder fb = em.DefineMember ();
419
420                                 if (fb == null) {
421                                         return false;
422                                 } 
423
424                                 if (!field_builders.Contains (fb)) {
425                                         field_builders.Add (fb);
426                                 }
427                         }
428
429                         if (OptAttributes != null)
430                                 OptAttributes.Emit (EmitContext, this);
431                         
432                         return true;
433                 }
434                 
435                 //
436                 // IMemberFinder
437                 //
438                 public override MemberList FindMembers (MemberTypes mt, BindingFlags bf,
439                                                         MemberFilter filter, object criteria)
440                 {
441                         ArrayList members = new ArrayList ();
442
443                         if ((mt & MemberTypes.Field) != 0) {
444                                 
445                                 if (criteria is string){
446                                         LookupEnumValue ((string) criteria);
447                                 }
448                                 
449                                 foreach (FieldBuilder fb in field_builders)
450                                         if (filter (fb, criteria) == true)
451                                                 members.Add (fb);
452                         }
453
454                         return new MemberList (members);
455                 }
456
457                 public override MemberCache MemberCache {
458                         get {
459                                 return null;
460                         }
461                 }
462
463                 public ArrayList ValueNames {
464                         get {
465                                 return ordered_enums;
466                         }
467                 }
468
469                 // indexer
470                 public EnumMember this [string name] {
471                         get {
472                                 EnumMember em = (EnumMember) defined_names [name];
473
474                                 if (em != null)
475                                         return em;
476
477                                 name = name.ToLower();
478                                 foreach (string nm in ordered_enums) {
479                                         if (nm.ToLower() == name) {
480                                                 em = (EnumMember) defined_names [name];
481                                                 break;
482                                         }
483                                 }
484
485                                 return em;
486                         }
487                 }
488
489                 public EnumMember this[int mem_index] {
490                         get {
491                                 string mem_name = (string) ordered_enums [mem_index];;
492                                 return (EnumMember) defined_names[mem_name];
493                         }
494                 }
495
496                 public EmitContext EmitContext {
497                         get {
498                                 return emit_context;
499                         }
500                 }
501         }
502 }