//
// Author:
// Miguel de Icaza (miguel@ximian.com)
+// Marek Safar (marek.safar@seznam.cz)
//
// (C) 2001 Ximian, Inc.
//
//
-//
-// This is needed because the following situation arises:
-//
-// The FieldBuilder is declared with the real type for an enumeration
-//
-// When we attempt to set the value for the constant, the FieldBuilder.SetConstant
-// function aborts because it requires its argument to be of the same type
-//
-
namespace Mono.CSharp {
using System;
using System.Reflection.Emit;
using System.Collections;
- public class Const : MemberCore {
- public readonly string ConstantType;
- public Expression Expr;
- public Attributes OptAttributes;
- public FieldBuilder FieldBuilder;
+ public interface IConstant
+ {
+ void CheckObsoleteness (Location loc);
+ bool ResolveValue ();
+ Constant Value { get; }
+ }
- object ConstantValue = null;
- Type type;
+ public class Const : FieldMember, IConstant {
+ Constant value;
+ bool in_transit;
public const int AllowedModifiers =
Modifiers.NEW |
Modifiers.INTERNAL |
Modifiers.PRIVATE;
- public Const (string constant_type, string name, Expression expr, int mod_flags,
- Attributes attrs, Location loc)
- : base (name, loc)
+ public Const (DeclSpace parent, Expression constant_type, string name,
+ Expression expr, int mod_flags, Attributes attrs, Location loc)
+ : base (parent, constant_type, mod_flags, AllowedModifiers,
+ new MemberName (name, loc), attrs)
{
- ConstantType = constant_type;
- Name = name;
- Expr = expr;
- ModFlags = Modifiers.Check (AllowedModifiers, mod_flags, Modifiers.PRIVATE, loc);
- OptAttributes = attrs;
+ initializer = expr;
+ ModFlags |= Modifiers.STATIC;
}
- public FieldAttributes FieldAttr {
- get {
- return FieldAttributes.Literal | FieldAttributes.Static |
- Modifiers.FieldAttr (ModFlags) ;
- }
- }
-
-#if DEBUG
- void dump_tree (Type t)
+ protected override bool CheckBase ()
{
- Console.WriteLine ("Dumping hierarchy");
- while (t != null){
- Console.WriteLine (" " + t.FullName + " " +
- (t.GetType ().IsEnum ? "yes" : "no"));
- t = t.BaseType;
- }
+ // Constant.Define can be called when the parent type hasn't yet been populated
+ // and it's base types need not have been populated. So, we defer this check
+ // to the second time Define () is called on this member.
+ if (ParentContainer.BaseCache == null)
+ return true;
+ return base.CheckBase ();
}
-#endif
/// <summary>
/// Defines the constant in the @parent
/// </summary>
- public override bool Define (TypeContainer parent)
+ public override bool Define ()
{
- type = RootContext.LookupType (parent, ConstantType, true, Location);
+ // Make Define () idempotent, but ensure that the error check happens.
+ if (FieldBuilder != null)
+ return base.CheckBase ();
- if (type == null)
+ if (!base.Define ())
return false;
- if (!TypeManager.IsBuiltinType (type) &&
- (!type.IsSubclassOf (TypeManager.enum_type))) {
- Report.Error (
- -3, Location,
- "Constant type is not valid (only system types are allowed)");
- return false;
- }
-
- Type ptype = parent.TypeBuilder.BaseType;
+ Type ttype = MemberType;
+ while (ttype.IsArray)
+ ttype = TypeManager.GetElementType (ttype);
- if (ptype != null) {
- MemberInfo [] mi = TypeContainer.FindMembers (
- ptype, MemberTypes.Field, BindingFlags.Public,
- Type.FilterName, Name);
-
- if (mi == null || mi.Length == 0)
- if ((ModFlags & Modifiers.NEW) != 0)
- WarningNotHiding (parent);
-
- } else if ((ModFlags & Modifiers.NEW) != 0)
- WarningNotHiding (parent);
+ FieldAttributes field_attr = FieldAttributes.Static | Modifiers.FieldAttr (ModFlags);
+ // Decimals cannot be emitted into the constant blob. So, convert to 'readonly'.
+ if (ttype == TypeManager.decimal_type) {
+ field_attr |= FieldAttributes.InitOnly;
+ ParentContainer.RegisterFieldForInitialization (this);
+ }
+ else {
+ field_attr |= FieldAttributes.Literal;
+ }
- FieldBuilder = parent.TypeBuilder.DefineField (Name, type, FieldAttr);
+ FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType, field_attr);
TypeManager.RegisterConstant (FieldBuilder, this);
return true;
}
-
+
/// <summary>
- /// Looks up the value of a constant field. Defines it if it hasn't
- /// already been. Similar to LookupEnumValue in spirit.
+ /// Emits the field value by evaluating the expression
/// </summary>
- public object LookupConstantValue (EmitContext ec)
+ public override void Emit ()
{
- if (ConstantValue != null)
- return ConstantValue;
-
- Expr = Expr.Resolve (ec);
+ if (!ResolveValue ())
+ return;
+
+ if (value.Type == TypeManager.decimal_type) {
+ Decimal d = ((DecimalConstant)value).Value;
+ int[] bits = Decimal.GetBits (d);
+ object[] args = new object[] { (byte)(bits [3] >> 16), (byte)(bits [3] >> 31), (uint)bits [2], (uint)bits [1], (uint)bits [0] };
+ CustomAttributeBuilder cab = new CustomAttributeBuilder (TypeManager.decimal_constant_attribute_ctor, args);
+ FieldBuilder.SetCustomAttribute (cab);
+ }
+ else{
+ FieldBuilder.SetConstant (value.GetTypedValue ());
+ }
- if (Expr == null) {
- Report.Error (150, Location, "A constant value is expected");
- return null;
+ base.Emit ();
+ }
+
+ public static void Error_ExpressionMustBeConstant (Location loc, string e_name)
+ {
+ Report.Error (133, loc, "The expression being assigned to `{0}' must be constant", e_name);
+ }
+
+ public static void Error_CyclicDeclaration (MemberCore mc)
+ {
+ Report.Error (110, mc.Location, "The evaluation of the constant value for `{0}' involves a circular definition",
+ mc.GetSignatureForError ());
+ }
+
+ public static void Error_ConstantCanBeInitializedWithNullOnly (Location loc, string name)
+ {
+ Report.Error (134, loc, "`{0}': the constant of reference type other than string can only be initialized with null",
+ name);
+ }
+
+ #region IConstant Members
+
+ public bool ResolveValue ()
+ {
+ if (value != null)
+ return true;
+
+ SetMemberIsUsed ();
+ if (in_transit) {
+ Error_CyclicDeclaration (this);
+ // Suppress cyclic errors
+ value = New.Constantify (MemberType);
+ return false;
}
- if (!(Expr is Constant)) {
- Report.Error (150, Location, "A constant value is expected");
- return null;
+ in_transit = true;
+ // TODO: IResolveContext here
+ EmitContext ec = new EmitContext (this, Parent, Location, null, MemberType, ModFlags);
+ value = initializer.ResolveAsConstant (ec, this);
+ in_transit = false;
+
+ if (value == null)
+ return false;
+
+ value = value.ToType (MemberType, Location);
+ if (value == null)
+ return false;
+
+ if (!MemberType.IsValueType && MemberType != TypeManager.string_type && !value.IsDefaultValue) {
+ Error_ConstantCanBeInitializedWithNullOnly (Location, GetSignatureForError ());
+ return false;
}
- ConstantValue = ((Constant) Expr).GetValue ();
+ return true;
+ }
- if (type.IsEnum){
- //
- // This sadly does not work for our user-defined enumerations types ;-(
- //
- ConstantValue = System.Enum.ToObject (
- type, ConstantValue);
+ public Constant Value {
+ get {
+ return value;
}
+ }
- FieldBuilder.SetConstant (ConstantValue);
+ #endregion
+ }
- if (!TypeManager.RegisterFieldValue (FieldBuilder, ConstantValue))
- return null;
+ public class ExternalConstant : IConstant
+ {
+ FieldInfo fi;
+ Constant value;
- return ConstantValue;
+ public ExternalConstant (FieldInfo fi)
+ {
+ this.fi = fi;
}
-
-
- /// <summary>
- /// Emits the field value by evaluating the expression
- /// </summary>
- public void EmitConstant (TypeContainer parent)
+
+ private ExternalConstant (FieldInfo fi, Constant value):
+ this (fi)
{
- EmitContext ec = new EmitContext (parent, Location, null, type, ModFlags);
- LookupConstantValue (ec);
+ this.value = value;
+ }
+
+ //
+ // Decimal constants cannot be encoded in the constant blob, and thus are marked
+ // as IsInitOnly ('readonly' in C# parlance). We get its value from the
+ // DecimalConstantAttribute metadata.
+ //
+ public static IConstant CreateDecimal (FieldInfo fi)
+ {
+ if (fi is FieldBuilder)
+ return null;
- return;
+ object[] attrs = fi.GetCustomAttributes (TypeManager.decimal_constant_attribute_type, false);
+ if (attrs.Length != 1)
+ return null;
+
+ IConstant ic = new ExternalConstant (fi,
+ new DecimalConstant (((System.Runtime.CompilerServices.DecimalConstantAttribute) attrs [0]).Value, Location.Null));
+
+ return ic;
}
- }
-}
+ #region IConstant Members
+
+ public void CheckObsoleteness (Location loc)
+ {
+ ObsoleteAttribute oa = AttributeTester.GetMemberObsoleteAttribute (fi);
+ if (oa == null) {
+ return;
+ }
+
+ AttributeTester.Report_ObsoleteMessage (oa, TypeManager.GetFullNameSignature (fi), loc);
+ }
+
+ public bool ResolveValue ()
+ {
+ if (value != null)
+ return true;
+
+ if (fi.DeclaringType.IsEnum) {
+ value = Expression.Constantify (fi.GetValue (fi), TypeManager.EnumToUnderlying (fi.FieldType));
+ value = new EnumConstant (value, fi.DeclaringType);
+ return true;
+ }
+ value = Expression.Constantify (fi.GetValue (fi), fi.FieldType);
+ return true;
+ }
+
+ public Constant Value {
+ get {
+ return value;
+ }
+ }
+
+ #endregion
+ }
+
+}