// // enum.cs: Enum handling. // // Author: Miguel de Icaza (miguel@gnu.org) // Ravi Pratap (ravi@ximian.com) // Anirban Bhattacharjee (banirban@novell.com) // Jambunathan K (kjambunathan@novell.com) // // Licensed under the terms of the GNU GPL // // (C) 2001 Ximian, Inc (http://www.ximian.com) // using System; using System.Collections; using System.Reflection; using System.Reflection.Emit; namespace Mono.MonoBASIC { public class EnumMember : MemberCore { Enum parent_enum; Expression expr; int index; FieldBuilder builder; object enum_value; bool in_transit = false; FieldAttributes field_attrs = FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal; public EnumMember (Enum parent_enum, Expression expr, string name, Location loc, Attributes attrs, int index): base (name, attrs, loc) { this.parent_enum = parent_enum; this.expr = expr; this.index = index; } public override void ApplyAttributeBuilder(Attribute a, CustomAttributeBuilder cb) { builder.SetCustomAttribute (cb); } public override AttributeTargets AttributeTargets { get { return AttributeTargets.Field; } } public override bool Define (TypeContainer tc) { throw new NotImplementedException (); } public object GetValue () { if (builder == null) DefineMember (); return enum_value; } public FieldBuilder DefineMember () { if (builder != null) return builder; DoDefineMember (); if (builder == null) parent_enum.RemoveEnumMember (this.Name); return builder; } public FieldBuilder DoDefineMember () { object default_value = null; Constant c = null; Type UnderlyingType = parent_enum.UnderlyingType; EmitContext ec = parent_enum.EmitContext; default_value = enum_value; if (in_transit) { Report.Error (30500, Location, "The evaluation of the constant value for `" + parent_enum.Name + "." + Name + "' involves a circular definition."); return null; } // // So if the above doesn't happen, we have a member that is undefined // We now proceed to define it // Expression val = expr; int idx = index; if (val == null) { if (idx == 0) default_value = 0; else { int i = idx - 1; EnumMember em = parent_enum [i]; in_transit = true; default_value = em.GetValue (); in_transit = false; if (default_value == null) return null; default_value = Enum.GetNextDefaultValue (default_value, UnderlyingType); } } else { bool old = ec.InEnumContext; ec.InEnumContext = true; in_transit = true; val = val.Resolve (ec); in_transit = false; ec.InEnumContext = old; if (val == null) return null; if (! Enum.IsValidEnumConstant (val)) { Report.Error ( 30650, Location, "Type byte, sbyte, short, ushort, int, uint, long, or " + "ulong expected (have: " + val + ")"); return null; } c = (Constant) val; default_value = c.GetValue (); if (default_value == null) { Enum.Error_ConstantValueCannotBeConverted (c, Location, UnderlyingType); return null; } } builder = parent_enum.TypeBuilder.DefineField (Name, parent_enum.TypeBuilder, field_attrs); try { //FXME: ChangeType is not the right thing to do. default_value = TypeManager.ChangeType (default_value, UnderlyingType); } catch { Enum.Error_ConstantValueCannotBeConverted (c, Location, UnderlyingType); return null; } enum_value = default_value; builder.SetConstant (enum_value); if (!TypeManager.RegisterFieldValue (builder, enum_value)) return null; if (OptAttributes != null) OptAttributes.Emit (ec, this); return builder; } } /// /// Enumeration container /// public class Enum : DeclSpace { ArrayList ordered_enums; public Expression BaseType; public Type UnderlyingType; ArrayList field_builders; public const int AllowedModifiers = Modifiers.NEW | Modifiers.PUBLIC | Modifiers.PROTECTED | Modifiers.INTERNAL | Modifiers.PRIVATE; EmitContext emit_context; public Enum (TypeContainer parent, Expression type, int mod_flags, string name, Attributes attrs, Location l) : base (parent, name, attrs, l) { this.BaseType = type; ModFlags = Modifiers.Check (AllowedModifiers, mod_flags, IsTopLevel ? Modifiers.INTERNAL : Modifiers.PUBLIC, l); ordered_enums = new ArrayList (); field_builders = new ArrayList (); } public override AttributeTargets AttributeTargets { get { return AttributeTargets.Enum; } } /// /// Adds @name to the enumeration space, with @expr /// being its definition. /// public AdditionResult AddEnumMember (string name, Expression expr, Location loc, Attributes opt_attrs) { int index; EnumMember em; if (defined_names.Contains (name)) return AdditionResult.NameExists; index = ordered_enums.Add (name); em = new EnumMember (this, expr, name, loc, opt_attrs, index); DefineName (name, em); return AdditionResult.Success; } public void RemoveEnumMember (string name) { defined_names.Remove (name); } public override TypeBuilder DefineType () { if (TypeBuilder != null) return TypeBuilder; emit_context = new EmitContext (Parent, this, Location, null, UnderlyingType, ModFlags, false); TypeAttributes attr = Modifiers.TypeAttr (ModFlags, IsTopLevel); attr |= TypeAttributes.Class | TypeAttributes.Sealed; UnderlyingType = ResolveType (BaseType, false, Location); if (UnderlyingType != TypeManager.int32_type && UnderlyingType != TypeManager.uint32_type && UnderlyingType != TypeManager.int64_type && UnderlyingType != TypeManager.uint64_type && UnderlyingType != TypeManager.short_type && UnderlyingType != TypeManager.ushort_type && UnderlyingType != TypeManager.byte_type && UnderlyingType != TypeManager.sbyte_type) { Report.Error (30650, Location, "Type byte, sbyte, short, ushort, int, uint, " + "long, or ulong expected (got: " + TypeManager.MonoBASIC_Name (UnderlyingType) + ")"); return null; } if (IsTopLevel) { ModuleBuilder builder = CodeGen.ModuleBuilder; TypeBuilder = builder.DefineType (Name, attr, TypeManager.enum_type); } else { TypeBuilder builder = Parent.TypeBuilder; TypeBuilder = builder.DefineNestedType ( Basename, attr, TypeManager.enum_type); } TypeBuilder.DefineField ("value__", UnderlyingType, FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RTSpecialName); TypeManager.AddEnumType (Name, TypeBuilder, this); return TypeBuilder; } public static bool IsValidEnumConstant (Expression e) { if (!(e is Constant)) return false; if (e is IntConstant || e is UIntConstant || e is LongConstant || e is ByteConstant || e is SByteConstant || e is ShortConstant || e is UShortConstant || e is ULongConstant || e is EnumConstant || e is DoubleConstant) return true; else return false; } static public object GetNextDefaultValue (object default_value, Type UnderlyingType) { if (UnderlyingType == TypeManager.int32_type) { int i = (int) default_value; if (i < System.Int32.MaxValue) return ++i; else return null; } else if (UnderlyingType == TypeManager.uint32_type) { uint i = (uint) default_value; if (i < System.UInt32.MaxValue) return ++i; else return null; } else if (UnderlyingType == TypeManager.int64_type) { long i = (long) default_value; if (i < System.Int64.MaxValue) return ++i; else return null; } else if (UnderlyingType == TypeManager.uint64_type) { ulong i = (ulong) default_value; if (i < System.UInt64.MaxValue) return ++i; else return null; } else if (UnderlyingType == TypeManager.short_type) { short i = (short) default_value; if (i < System.Int16.MaxValue) return ++i; else return null; } else if (UnderlyingType == TypeManager.ushort_type) { ushort i = (ushort) default_value; if (i < System.UInt16.MaxValue) return ++i; else return null; } else if (UnderlyingType == TypeManager.byte_type) { byte i = (byte) default_value; if (i < System.Byte.MaxValue) return ++i; else return null; } else if (UnderlyingType == TypeManager.sbyte_type) { sbyte i = (sbyte) default_value; if (i < System.SByte.MaxValue) return ++i; else return null; } return null; } public static void Error_ConstantValueCannotBeConverted (object val, Location loc, Type UnderlyingType) { if (val is Constant) Report.Error (30439, loc, "Constant value '" + ((Constant) val).AsString () + "' cannot be converted" + " to a " + TypeManager.MonoBASIC_Name (UnderlyingType)); else Report.Error (30439, loc, "Constant value '" + val + "' cannot be converted" + " to a " + TypeManager.MonoBASIC_Name (UnderlyingType)); return; } /// /// This is used to lookup the value of an enum member. If the member is undefined, /// it attempts to define it and return its value /// public object LookupEnumValue (string name) { EnumMember em; // first check whether the requested name is there // in the member list of enum em = this [name]; if (em == null) return null; name = em.Name; FieldBuilder fb = em.DefineMember (); if (fb == null) { return null; } if (! field_builders.Contains (fb)) { field_builders.Add (fb); } return em.GetValue (); } public override bool DefineMembers (TypeContainer parent) { return true; } public override bool Define (TypeContainer parent) { if (TypeBuilder == null) return false; /* EmitContext ec = new EmitContext (parent, this, Location, null, UnderlyingType, ModFlags, false); */ foreach (string name in ordered_enums) { EnumMember em = this [name]; FieldBuilder fb = em.DefineMember (); if (fb == null) { return false; } if (!field_builders.Contains (fb)) { field_builders.Add (fb); } } if (OptAttributes != null) OptAttributes.Emit (EmitContext, this); return true; } // // IMemberFinder // public override MemberList FindMembers (MemberTypes mt, BindingFlags bf, MemberFilter filter, object criteria) { ArrayList members = new ArrayList (); if ((mt & MemberTypes.Field) != 0) { if (criteria is string){ LookupEnumValue ((string) criteria); } foreach (FieldBuilder fb in field_builders) if (filter (fb, criteria) == true) members.Add (fb); } return new MemberList (members); } public override MemberCache MemberCache { get { return null; } } public ArrayList ValueNames { get { return ordered_enums; } } // indexer public EnumMember this [string name] { get { EnumMember em = (EnumMember) defined_names [name]; if (em != null) return em; name = name.ToLower(); foreach (string nm in ordered_enums) { if (nm.ToLower() == name) { em = (EnumMember) defined_names [name]; break; } } return em; } } public EnumMember this[int mem_index] { get { string mem_name = (string) ordered_enums [mem_index];; return (EnumMember) defined_names[mem_name]; } } public EmitContext EmitContext { get { return emit_context; } } } }