2005-10-03 Marek Safar <marek.safar@seznam.cz>
[mono.git] / mcs / mcs / const.cs
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 //
10 //
11
12 namespace Mono.CSharp {
13
14         using System;
15         using System.Reflection;
16         using System.Reflection.Emit;
17         using System.Collections;
18
19         public interface IConstant
20         {
21                 void CheckObsoleteness (Location loc);
22                 bool ResolveValue ();
23                 Constant Value { get; }
24         }
25
26         public class Const : FieldMember, IConstant {
27                 Expression Expr;
28                 Constant value;
29                 bool in_transit;
30
31                 public const int AllowedModifiers =
32                         Modifiers.NEW |
33                         Modifiers.PUBLIC |
34                         Modifiers.PROTECTED |
35                         Modifiers.INTERNAL |
36                         Modifiers.PRIVATE;
37
38                 public Const (TypeContainer parent, Expression constant_type, string name,
39                               Expression expr, int mod_flags, Attributes attrs, Location loc)
40                         : base (parent, constant_type, mod_flags, AllowedModifiers,
41                                 new MemberName (name, loc), expr, attrs)
42                 {
43                         Expr = expr;
44                         ModFlags |= Modifiers.STATIC;
45                 }
46
47                 protected override bool CheckBase ()
48                 {
49                         // Constant.Define can be called when the parent type hasn't yet been populated
50                         // and it's base types need not have been populated.  So, we defer this check
51                         // to the second time Define () is called on this member.
52                         if (Parent.BaseCache == null)
53                                 return true;
54                         return base.CheckBase ();
55                 }
56
57                 /// <summary>
58                 ///   Defines the constant in the @parent
59                 /// </summary>
60                 public override bool Define ()
61                 {
62                         // Make Define () idempotent, but ensure that the error check happens.
63                         if (FieldBuilder != null)
64                                 return base.CheckBase ();
65
66                         if (!base.Define ())
67                                 return false;
68
69                         Type ttype = MemberType;
70                         while (ttype.IsArray)
71                             ttype = TypeManager.GetElementType (ttype);
72
73                         FieldAttributes field_attr = FieldAttributes.Static | Modifiers.FieldAttr (ModFlags);
74                         // Decimals cannot be emitted into the constant blob.  So, convert to 'readonly'.
75                         if (ttype == TypeManager.decimal_type) {
76                                 field_attr |= FieldAttributes.InitOnly;
77                                 Parent.RegisterFieldForInitialization (this);
78                         }
79                         else {
80                                 field_attr |= FieldAttributes.Literal;
81                         }
82
83                         FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType, field_attr);
84
85                         TypeManager.RegisterConstant (FieldBuilder, this);
86
87                         return true;
88                 }
89                 
90                 /// <summary>
91                 ///  Emits the field value by evaluating the expression
92                 /// </summary>
93                 public override void Emit ()
94                 {
95                         if (!ResolveValue ())
96                                 return;
97
98                         if (value.Type == TypeManager.decimal_type) {
99                                 Decimal d = ((DecimalConstant)value).Value;
100                                 int[] bits = Decimal.GetBits (d);
101                                 object[] args = new object[] { (byte)(bits [3] >> 16), (byte)(bits [3] >> 31), (uint)bits [2], (uint)bits [1], (uint)bits [0] };
102                                 CustomAttributeBuilder cab = new CustomAttributeBuilder (TypeManager.decimal_constant_attribute_ctor, args);
103                                 FieldBuilder.SetCustomAttribute (cab);
104                         }
105                         else{
106                                 FieldBuilder.SetConstant (value.GetTypedValue ());
107                         }
108
109                         base.Emit ();
110                 }
111
112                 public static void Error_ExpressionMustBeConstant (Location loc, string e_name)
113                 {
114                         Report.Error (133, loc, "The expression being assigned to `{0}' must be constant", e_name);
115                 }
116
117                 public static void Error_CyclicDeclaration (MemberCore mc)
118                 {
119                         Report.Error (110, mc.Location, "The evaluation of the constant value for `{0}' involves a circular definition",
120                                 mc.GetSignatureForError ());
121                 }
122
123                 #region IConstant Members
124
125                 public bool ResolveValue ()
126                 {
127                         if (value != null)
128                                 return true;
129
130                         SetMemberIsUsed ();
131                         if (in_transit) {
132                                 Error_CyclicDeclaration (this);
133                                 // Suppress cyclic errors
134                                 value = New.Constantify (MemberType);
135                                 return false;
136                         }
137
138                         in_transit = true;
139                         EmitContext ec = new EmitContext (Parent, Location, null, MemberType, ModFlags);
140                         value = Expr.ResolveAsConstant (ec, this);
141                         in_transit = false;
142
143                         if (value == null)
144                                 return false;
145
146                         value = value.ToType (MemberType, Location);
147                         if (value == null)
148                                 return false;
149
150                         if (!MemberType.IsValueType && MemberType != TypeManager.string_type && !value.IsDefaultValue) {
151                                 Report.Error (134, Location, "`{0}': A const of reference other than string can only be initialized with null",
152                                         GetSignatureForError ());
153                                 return false;
154                         }
155
156                         return true;
157                 }
158
159                 public Constant Value {
160                         get {
161                                 return value;
162                         }
163                 }
164
165                 #endregion
166         }
167
168         public class ExternalConstant: IConstant
169         {
170                 FieldInfo fi;
171                 Constant value;
172
173                 public ExternalConstant (FieldInfo fi)
174                 {
175                         this.fi = fi;
176                 }
177
178                 private ExternalConstant (FieldInfo fi, Constant value):
179                         this (fi)
180                 {
181                         this.value = value;
182                 }
183
184                 //
185                 // Decimal constants cannot be encoded in the constant blob, and thus are marked
186                 // as IsInitOnly ('readonly' in C# parlance).  We get its value from the 
187                 // DecimalConstantAttribute metadata.
188                 //
189                 public static IConstant CreateDecimal (FieldInfo fi)
190                 {
191                         if (fi is FieldBuilder)
192                                 return null;
193                         
194                         object[] attrs = fi.GetCustomAttributes (TypeManager.decimal_constant_attribute_type, false);
195                         if (attrs.Length != 1)
196                                 return null;
197
198                         IConstant ic = new ExternalConstant (fi,
199                                 new DecimalConstant (((System.Runtime.CompilerServices.DecimalConstantAttribute) attrs [0]).Value, Location.Null));
200
201                         return ic;
202                 }
203
204                 #region IConstant Members
205
206                 public void CheckObsoleteness (Location loc)
207                 {
208                         ObsoleteAttribute oa = AttributeTester.GetMemberObsoleteAttribute (fi);
209                         if (oa == null) {
210                                 return;
211                         }
212
213                         AttributeTester.Report_ObsoleteMessage (oa, TypeManager.GetFullNameSignature (fi), loc);
214                 }
215
216                 public bool ResolveValue ()
217                 {
218                         if (value != null)
219                                 return true;
220
221                         if (fi.DeclaringType.IsEnum) {
222                                 value = Expression.Constantify (fi.GetValue (fi), TypeManager.EnumToUnderlying (fi.FieldType));
223                                 value = new EnumConstant (value, fi.DeclaringType);
224                                 return true;
225                         }
226
227                         value = Expression.Constantify (fi.GetValue (fi), fi.FieldType);
228                         return true;
229                 }
230
231                 public Constant Value {
232                         get {
233                                 return value;
234                         }
235                 }
236
237                 #endregion
238         }
239
240 }