2007-03-22 Chris Toshok <toshok@ximian.com>
[mcs.git] / mcs / const.cs
blob2588ab92aac252b9fc079d82917a265f45d29729
1 //
2 // const.cs: Constant declarations.
3 //
4 // Author:
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@seznam.cz)
7 //
8 // (C) 2001 Ximian, Inc.
9 //
12 namespace Mono.CSharp {
14 using System;
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Collections;
19 public interface IConstant
21 void CheckObsoleteness (Location loc);
22 bool ResolveValue ();
23 Constant CreateConstantReference (Location loc);
26 public class Const : FieldBase, IConstant {
27 protected Constant value;
28 bool in_transit;
29 bool resolved;
30 bool define_called;
32 public const int AllowedModifiers =
33 Modifiers.NEW |
34 Modifiers.PUBLIC |
35 Modifiers.PROTECTED |
36 Modifiers.INTERNAL |
37 Modifiers.PRIVATE;
39 public Const (DeclSpace parent, Expression constant_type, string name,
40 Expression expr, int mod_flags, Attributes attrs, Location loc)
41 : base (parent, constant_type, mod_flags, AllowedModifiers,
42 new MemberName (name, loc), attrs)
44 initializer = expr;
45 ModFlags |= Modifiers.STATIC;
48 protected override bool CheckBase ()
50 // Constant.Define can be called when the parent type hasn't yet been populated
51 // and it's base types need not have been populated. So, we defer this check
52 // to the second time Define () is called on this member.
53 if (Parent.PartialContainer.BaseCache == null)
54 return true;
55 return base.CheckBase ();
58 /// <summary>
59 /// Defines the constant in the @parent
60 /// </summary>
61 public override bool Define ()
63 // Because constant define can be called from other class
64 if (define_called) {
65 CheckBase ();
66 return FieldBuilder != null;
69 define_called = true;
71 if (!base.Define ())
72 return false;
74 Type ttype = MemberType;
75 if (!IsConstantTypeValid (ttype)) {
76 Error_InvalidConstantType (ttype, Location);
77 return false;
80 // If the constant is private then we don't need any field the
81 // value is already inlined and cannot be referenced
82 //if ((ModFlags & Modifiers.PRIVATE) != 0 && RootContext.Optimize)
83 // return true;
85 while (ttype.IsArray)
86 ttype = TypeManager.GetElementType (ttype);
88 FieldAttributes field_attr = FieldAttributes.Static | Modifiers.FieldAttr (ModFlags);
89 // Decimals cannot be emitted into the constant blob. So, convert to 'readonly'.
90 if (ttype == TypeManager.decimal_type) {
91 field_attr |= FieldAttributes.InitOnly;
92 } else {
93 field_attr |= FieldAttributes.Literal;
96 FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType, field_attr);
97 TypeManager.RegisterConstant (FieldBuilder, this);
99 if (ttype == TypeManager.decimal_type)
100 Parent.PartialContainer.RegisterFieldForInitialization (this,
101 new FieldInitializer (FieldBuilder, initializer, Parent));
103 return true;
106 public static bool IsConstantTypeValid (Type t)
108 if (TypeManager.IsBuiltinOrEnum (t))
109 return true;
111 if (t.IsPointer || t.IsValueType)
112 return false;
114 if (TypeManager.IsGenericParameter (t))
115 return false;
117 return true;
120 /// <summary>
121 /// Emits the field value by evaluating the expression
122 /// </summary>
123 public override void Emit ()
125 if (!ResolveValue ())
126 return;
128 if (FieldBuilder == null)
129 return;
131 if (value.Type == TypeManager.decimal_type) {
132 Decimal d = ((DecimalConstant)value).Value;
133 int[] bits = Decimal.GetBits (d);
134 object[] args = new object[] { (byte)(bits [3] >> 16), (byte)(bits [3] >> 31), (uint)bits [2], (uint)bits [1], (uint)bits [0] };
135 CustomAttributeBuilder cab = new CustomAttributeBuilder (TypeManager.decimal_constant_attribute_ctor, args);
136 FieldBuilder.SetCustomAttribute (cab);
138 else{
139 FieldBuilder.SetConstant (value.GetTypedValue ());
142 base.Emit ();
145 public static void Error_ExpressionMustBeConstant (Location loc, string e_name)
147 Report.Error (133, loc, "The expression being assigned to `{0}' must be constant", e_name);
150 public static void Error_CyclicDeclaration (MemberCore mc)
152 Report.Error (110, mc.Location, "The evaluation of the constant value for `{0}' involves a circular definition",
153 mc.GetSignatureForError ());
156 public static void Error_ConstantCanBeInitializedWithNullOnly (Location loc, string name)
158 Report.Error (134, loc, "`{0}': the constant of reference type other than string can only be initialized with null",
159 name);
162 public static void Error_InvalidConstantType (Type t, Location loc)
164 Report.Error (283, loc, "The type `{0}' cannot be declared const", TypeManager.CSharpName (t));
167 #region IConstant Members
169 public bool ResolveValue ()
171 if (resolved)
172 return value != null;
174 SetMemberIsUsed ();
175 if (in_transit) {
176 Error_CyclicDeclaration (this);
177 // Suppress cyclic errors
178 value = New.Constantify (MemberType);
179 resolved = true;
180 return false;
183 in_transit = true;
184 // TODO: IResolveContext here
185 EmitContext ec = new EmitContext (
186 this, Parent, Location, null, MemberType, ModFlags);
187 ec.InEnumContext = this is EnumMember;
188 value = DoResolveValue (ec);
189 in_transit = false;
190 resolved = true;
191 return value != null;
194 protected virtual Constant DoResolveValue (EmitContext ec)
196 Constant value = initializer.ResolveAsConstant (ec, this);
197 if (value == null)
198 return null;
200 Constant c = value.ConvertImplicitly (MemberType);
201 if (c == null) {
202 if (!MemberType.IsValueType && MemberType != TypeManager.string_type && !value.IsDefaultValue)
203 Error_ConstantCanBeInitializedWithNullOnly (Location, GetSignatureForError ());
204 else
205 value.Error_ValueCannotBeConverted (null, Location, MemberType, false);
208 return c;
211 public virtual Constant CreateConstantReference (Location loc)
213 if (value == null)
214 return null;
216 return Constant.CreateConstant (value.Type, value.GetValue(), loc);
219 #endregion
222 public class ExternalConstant : IConstant
224 FieldInfo fi;
225 object value;
227 public ExternalConstant (FieldInfo fi)
229 this.fi = fi;
232 private ExternalConstant (FieldInfo fi, object value):
233 this (fi)
235 this.value = value;
239 // Decimal constants cannot be encoded in the constant blob, and thus are marked
240 // as IsInitOnly ('readonly' in C# parlance). We get its value from the
241 // DecimalConstantAttribute metadata.
243 public static IConstant CreateDecimal (FieldInfo fi)
245 if (fi is FieldBuilder)
246 return null;
248 object[] attrs = fi.GetCustomAttributes (TypeManager.decimal_constant_attribute_type, false);
249 if (attrs.Length != 1)
250 return null;
252 IConstant ic = new ExternalConstant (fi,
253 ((System.Runtime.CompilerServices.DecimalConstantAttribute) attrs [0]).Value);
255 return ic;
258 #region IConstant Members
260 public void CheckObsoleteness (Location loc)
262 ObsoleteAttribute oa = AttributeTester.GetMemberObsoleteAttribute (fi);
263 if (oa == null) {
264 return;
267 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.GetFullNameSignature (fi), loc);
270 public bool ResolveValue ()
272 if (value != null)
273 return true;
275 value = fi.GetValue (fi);
276 return true;
279 public Constant CreateConstantReference (Location loc)
281 return Constant.CreateConstant (fi.FieldType, value, loc);
284 #endregion