7d05485754
Former-commit-id: df344e34b07851d296efb3e6604c8db42b6f7aa3
374 lines
8.9 KiB
C#
374 lines
8.9 KiB
C#
//
|
|
// enum.cs: Enum handling.
|
|
//
|
|
// Author: Miguel de Icaza (miguel@gnu.org)
|
|
// Ravi Pratap (ravi@ximian.com)
|
|
// Marek Safar (marek.safar@seznam.cz)
|
|
//
|
|
// Dual licensed under the terms of the MIT X11 or GNU GPL
|
|
//
|
|
// Copyright 2001 Ximian, Inc (http://www.ximian.com)
|
|
// Copyright 2003-2003 Novell, Inc (http://www.novell.com)
|
|
// Copyright 2011 Xamarin Inc
|
|
//
|
|
|
|
using System;
|
|
|
|
#if STATIC
|
|
using MetaType = IKVM.Reflection.Type;
|
|
using IKVM.Reflection;
|
|
#else
|
|
using MetaType = System.Type;
|
|
using System.Reflection;
|
|
#endif
|
|
|
|
namespace Mono.CSharp
|
|
{
|
|
|
|
public class EnumMember : Const
|
|
{
|
|
#if !STATIC
|
|
class MemberTypeDelegator : TypeDelegator
|
|
{
|
|
Type underlyingType;
|
|
|
|
public MemberTypeDelegator (Type delegatingType, Type underlyingType)
|
|
: base (delegatingType)
|
|
{
|
|
this.underlyingType = underlyingType;
|
|
}
|
|
|
|
public override Type GetEnumUnderlyingType ()
|
|
{
|
|
return underlyingType;
|
|
}
|
|
|
|
public override Type UnderlyingSystemType {
|
|
get {
|
|
return underlyingType;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
class EnumTypeExpr : TypeExpr
|
|
{
|
|
public override TypeSpec ResolveAsType (IMemberContext ec, bool allowUnboundTypeArguments)
|
|
{
|
|
type = ec.CurrentType;
|
|
eclass = ExprClass.Type;
|
|
return type;
|
|
}
|
|
}
|
|
|
|
public EnumMember (Enum parent, MemberName name, Attributes attrs)
|
|
: base (parent, new EnumTypeExpr (), Modifiers.PUBLIC, name, attrs)
|
|
{
|
|
}
|
|
|
|
static bool IsValidEnumType (TypeSpec t)
|
|
{
|
|
switch (t.BuiltinType) {
|
|
case BuiltinTypeSpec.Type.Int:
|
|
case BuiltinTypeSpec.Type.UInt:
|
|
case BuiltinTypeSpec.Type.Long:
|
|
case BuiltinTypeSpec.Type.Byte:
|
|
case BuiltinTypeSpec.Type.SByte:
|
|
case BuiltinTypeSpec.Type.Short:
|
|
case BuiltinTypeSpec.Type.UShort:
|
|
case BuiltinTypeSpec.Type.ULong:
|
|
case BuiltinTypeSpec.Type.Char:
|
|
return true;
|
|
default:
|
|
return t.IsEnum;
|
|
}
|
|
}
|
|
|
|
public override Constant ConvertInitializer (ResolveContext rc, Constant expr)
|
|
{
|
|
if (expr is EnumConstant)
|
|
expr = ((EnumConstant) expr).Child;
|
|
|
|
var en = (Enum)Parent;
|
|
var underlying = en.UnderlyingType;
|
|
if (expr != null) {
|
|
expr = expr.ImplicitConversionRequired (rc, underlying);
|
|
if (expr != null && !IsValidEnumType (expr.Type)) {
|
|
en.Error_UnderlyingType (Location);
|
|
expr = null;
|
|
}
|
|
}
|
|
|
|
if (expr == null)
|
|
expr = New.Constantify (underlying, Location);
|
|
|
|
return new EnumConstant (expr, MemberType);
|
|
}
|
|
|
|
public override bool Define ()
|
|
{
|
|
if (!ResolveMemberType ())
|
|
return false;
|
|
|
|
MetaType ftype = MemberType.GetMetaInfo ();
|
|
#if !STATIC
|
|
//
|
|
// Workaround for .net SRE limitation which cannot define field of unbaked enum type
|
|
// which is how all enums are declared
|
|
//
|
|
ftype = new MemberTypeDelegator (ftype, ((Enum)Parent).UnderlyingType.GetMetaInfo ());
|
|
#endif
|
|
|
|
const FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal;
|
|
FieldBuilder = Parent.TypeBuilder.DefineField (Name, ftype, attr);
|
|
spec = new ConstSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags, initializer);
|
|
|
|
Parent.MemberCache.AddMember (spec);
|
|
return true;
|
|
}
|
|
|
|
public override void Accept (StructuralVisitor visitor)
|
|
{
|
|
visitor.Visit (this);
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enumeration container
|
|
/// </summary>
|
|
public class Enum : TypeDefinition
|
|
{
|
|
//
|
|
// Implicit enum member initializer, used when no constant value is provided
|
|
//
|
|
sealed class ImplicitInitializer : Expression
|
|
{
|
|
readonly EnumMember prev;
|
|
readonly EnumMember current;
|
|
|
|
public ImplicitInitializer (EnumMember current, EnumMember prev)
|
|
{
|
|
this.current = current;
|
|
this.prev = prev;
|
|
}
|
|
|
|
public override bool ContainsEmitWithAwait ()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override Expression CreateExpressionTree (ResolveContext ec)
|
|
{
|
|
throw new NotSupportedException ("Missing Resolve call");
|
|
}
|
|
|
|
protected override Expression DoResolve (ResolveContext rc)
|
|
{
|
|
// We are the first member
|
|
if (prev == null) {
|
|
return New.Constantify (current.Parent.Definition, Location);
|
|
}
|
|
|
|
var c = ((ConstSpec) prev.Spec).GetConstant (rc) as EnumConstant;
|
|
try {
|
|
return c.Increment ();
|
|
} catch (OverflowException) {
|
|
rc.Report.Error (543, current.Location,
|
|
"The enumerator value `{0}' is outside the range of enumerator underlying type `{1}'",
|
|
current.GetSignatureForError (), ((Enum) current.Parent).UnderlyingType.GetSignatureForError ());
|
|
|
|
return New.Constantify (current.Parent.Definition, current.Location);
|
|
}
|
|
}
|
|
|
|
public override void Emit (EmitContext ec)
|
|
{
|
|
throw new NotSupportedException ("Missing Resolve call");
|
|
}
|
|
}
|
|
|
|
public static readonly string UnderlyingValueField = "value__";
|
|
|
|
const Modifiers AllowedModifiers =
|
|
Modifiers.NEW |
|
|
Modifiers.PUBLIC |
|
|
Modifiers.PROTECTED |
|
|
Modifiers.INTERNAL |
|
|
Modifiers.PRIVATE;
|
|
|
|
readonly FullNamedExpression underlying_type_expr;
|
|
|
|
public Enum (TypeContainer parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs)
|
|
: base (parent, name, attrs, MemberKind.Enum)
|
|
{
|
|
underlying_type_expr = type;
|
|
var accmods = IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE;
|
|
ModFlags = ModifiersExtensions.Check (AllowedModifiers, mod_flags, accmods, Location, Report);
|
|
spec = new EnumSpec (null, this, null, null, ModFlags);
|
|
}
|
|
|
|
#region Properties
|
|
|
|
public override AttributeTargets AttributeTargets {
|
|
get {
|
|
return AttributeTargets.Enum;
|
|
}
|
|
}
|
|
|
|
public FullNamedExpression BaseTypeExpression {
|
|
get {
|
|
return underlying_type_expr;
|
|
}
|
|
}
|
|
|
|
protected override TypeAttributes TypeAttr {
|
|
get {
|
|
return base.TypeAttr | TypeAttributes.Class | TypeAttributes.Sealed;
|
|
}
|
|
}
|
|
|
|
public TypeSpec UnderlyingType {
|
|
get {
|
|
return ((EnumSpec) spec).UnderlyingType;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
public override void Accept (StructuralVisitor visitor)
|
|
{
|
|
visitor.Visit (this);
|
|
}
|
|
|
|
public void AddEnumMember (EnumMember em)
|
|
{
|
|
if (em.Name == UnderlyingValueField) {
|
|
Report.Error (76, em.Location, "An item in an enumeration cannot have an identifier `{0}'",
|
|
UnderlyingValueField);
|
|
return;
|
|
}
|
|
|
|
AddMember (em);
|
|
}
|
|
|
|
public void Error_UnderlyingType (Location loc)
|
|
{
|
|
Report.Error (1008, loc,
|
|
"Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
|
|
}
|
|
|
|
protected override void DoDefineContainer ()
|
|
{
|
|
TypeSpec ut;
|
|
if (underlying_type_expr != null) {
|
|
ut = underlying_type_expr.ResolveAsType (this);
|
|
if (!EnumSpec.IsValidUnderlyingType (ut)) {
|
|
Error_UnderlyingType (underlying_type_expr.Location);
|
|
ut = null;
|
|
}
|
|
} else {
|
|
ut = null;
|
|
}
|
|
|
|
if (ut == null)
|
|
ut = Compiler.BuiltinTypes.Int;
|
|
|
|
((EnumSpec) spec).UnderlyingType = ut;
|
|
|
|
TypeBuilder.DefineField (UnderlyingValueField, UnderlyingType.GetMetaInfo (),
|
|
FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RTSpecialName);
|
|
|
|
DefineBaseTypes ();
|
|
}
|
|
|
|
protected override bool DoDefineMembers ()
|
|
{
|
|
for (int i = 0; i < Members.Count; ++i) {
|
|
EnumMember em = (EnumMember) Members[i];
|
|
if (em.Initializer == null) {
|
|
em.Initializer = new ImplicitInitializer (em, i == 0 ? null : (EnumMember) Members[i - 1]);
|
|
}
|
|
|
|
em.Define ();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public override bool IsUnmanagedType ()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class)
|
|
{
|
|
base_type = Compiler.BuiltinTypes.Enum;
|
|
base_class = null;
|
|
return null;
|
|
}
|
|
|
|
protected override bool VerifyClsCompliance ()
|
|
{
|
|
if (!base.VerifyClsCompliance ())
|
|
return false;
|
|
|
|
switch (UnderlyingType.BuiltinType) {
|
|
case BuiltinTypeSpec.Type.UInt:
|
|
case BuiltinTypeSpec.Type.ULong:
|
|
case BuiltinTypeSpec.Type.UShort:
|
|
Report.Warning (3009, 1, Location, "`{0}': base type `{1}' is not CLS-compliant",
|
|
GetSignatureForError (), UnderlyingType.GetSignatureForError ());
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class EnumSpec : TypeSpec
|
|
{
|
|
TypeSpec underlying;
|
|
|
|
public EnumSpec (TypeSpec declaringType, ITypeDefinition definition, TypeSpec underlyingType, MetaType info, Modifiers modifiers)
|
|
: base (MemberKind.Enum, declaringType, definition, info, modifiers | Modifiers.SEALED)
|
|
{
|
|
this.underlying = underlyingType;
|
|
}
|
|
|
|
public TypeSpec UnderlyingType {
|
|
get {
|
|
return underlying;
|
|
}
|
|
set {
|
|
if (underlying != null)
|
|
throw new InternalErrorException ("UnderlyingType reset");
|
|
|
|
underlying = value;
|
|
}
|
|
}
|
|
|
|
public static TypeSpec GetUnderlyingType (TypeSpec t)
|
|
{
|
|
return ((EnumSpec) t.GetDefinition ()).UnderlyingType;
|
|
}
|
|
|
|
public static bool IsValidUnderlyingType (TypeSpec type)
|
|
{
|
|
switch (type.BuiltinType) {
|
|
case BuiltinTypeSpec.Type.Int:
|
|
case BuiltinTypeSpec.Type.UInt:
|
|
case BuiltinTypeSpec.Type.Long:
|
|
case BuiltinTypeSpec.Type.Byte:
|
|
case BuiltinTypeSpec.Type.SByte:
|
|
case BuiltinTypeSpec.Type.Short:
|
|
case BuiltinTypeSpec.Type.UShort:
|
|
case BuiltinTypeSpec.Type.ULong:
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|