2001-11-22 Miguel de Icaza <miguel@ximian.com>
[mono.git] / mcs / mcs / attribute.cs
1 //\r
2 // attribute.cs: Attribute Handler\r
3 //\r
4 // Author: Ravi Pratap (ravi@ximian.com)\r
5 //\r
6 // Licensed under the terms of the GNU GPL\r
7 //\r
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)\r
9 //\r
10 //\r
11 \r
12 using System;\r
13 using System.Collections;\r
14 using System.Reflection;\r
15 using System.Reflection.Emit;\r
16 using System.Text;\r
17 \r
18 namespace Mono.CSharp {\r
19 \r
20         public class Attribute {\r
21                 \r
22                 public readonly string    Name;\r
23                 public readonly ArrayList Arguments;\r
24 \r
25                 Location Location;\r
26 \r
27                 public Type Type;\r
28                 \r
29                 //\r
30                 // The following are only meaningful when the attribute\r
31                 // being emitted is one of the builtin ones\r
32                 //\r
33                 public AttributeTargets Targets;\r
34                 public bool AllowMultiple;\r
35                 public bool Inherited;\r
36 \r
37                 public bool UsageAttr = false;\r
38                 \r
39                 public Attribute (string name, ArrayList args, Location loc)\r
40                 {\r
41                         Name = name;\r
42                         Arguments = args;\r
43                         Location = loc;\r
44                 }\r
45 \r
46                 void error617 (string name)\r
47                 {\r
48                         Report.Error (617, Location, "'" + name + "' is not a valid named attribute " +\r
49                                       "argument. Named attribute arguments must be fields which are not " +\r
50                                       "readonly, static or const, or properties with a set accessor which "+\r
51                                       "are not static.");\r
52                 }\r
53 \r
54                 void error182 ()\r
55                 {\r
56                         Report.Error (182, Location,\r
57                                       "An attribute argument must be a constant expression, typeof " +\r
58                                       "expression or array creation expression");\r
59                 }\r
60 \r
61                 public CustomAttributeBuilder Resolve (EmitContext ec)\r
62                 {\r
63                         string name = Name;\r
64 \r
65                         UsageAttr = false;\r
66 \r
67                         if (Name.IndexOf ("Attribute") == -1)\r
68                                 name = Name + "Attribute";\r
69                         else if (Name.LastIndexOf ("Attribute") == 0)\r
70                                 name = Name + "Attribute";\r
71                         \r
72                         Type = ec.TypeContainer.LookupType (name, false);\r
73 \r
74                         if (Type == null) {\r
75                                 Report.Error (246, Location, "Could not find attribute '" + Name + "' (are you" +\r
76                                               " missing a using directive or an assembly reference ?)");\r
77                                 return null;\r
78                         }\r
79 \r
80                         if (Type == TypeManager.attribute_usage_type)\r
81                                 UsageAttr = true;\r
82                         \r
83                         // Now we extract the positional and named arguments\r
84                         \r
85                         ArrayList pos_args = new ArrayList ();\r
86                         ArrayList named_args = new ArrayList ();\r
87                         \r
88                         if (Arguments != null) {\r
89                                 pos_args = (ArrayList) Arguments [0];\r
90                                 if (Arguments.Count > 1)\r
91                                         named_args = (ArrayList) Arguments [1];\r
92                         }\r
93                                 \r
94                         object [] pos_values = new object [pos_args.Count];\r
95 \r
96                         //\r
97                         // First process positional arguments \r
98                         //\r
99                         \r
100                         int i;\r
101                         for (i = 0; i < pos_args.Count; i++) {\r
102 \r
103                                 Argument a = (Argument) pos_args [i];\r
104 \r
105                                 if (!a.Resolve (ec, Location))\r
106                                         return null;\r
107 \r
108                                 Expression e = Expression.Reduce (ec, a.Expr);\r
109 \r
110                                 if (e is Literal) {\r
111                                         pos_values [i] = ((Literal) e).GetValue ();\r
112 \r
113                                         if (UsageAttr)\r
114                                                 this.Targets = (AttributeTargets) pos_values [0];\r
115                                 } else { \r
116                                         error182 ();\r
117                                         return null;\r
118                                 }\r
119                         }\r
120 \r
121                         //\r
122                         // Now process named arguments\r
123                         //\r
124 \r
125                         ArrayList field_infos = new ArrayList ();\r
126                         ArrayList prop_infos  = new ArrayList ();\r
127                         ArrayList field_values = new ArrayList ();\r
128                         ArrayList prop_values = new ArrayList ();\r
129 \r
130                         for (i = 0; i < named_args.Count; i++) {\r
131 \r
132                                 DictionaryEntry de = (DictionaryEntry) named_args [i];\r
133 \r
134                                 string member_name = (string) de.Key;\r
135                                 Argument a  = (Argument) de.Value;\r
136 \r
137                                 if (!a.Resolve (ec, Location))\r
138                                         return null;\r
139 \r
140                                 Expression member = Expression.MemberLookup (ec, Type, member_name, false,\r
141                                                                              MemberTypes.Field | MemberTypes.Property,\r
142                                                                              BindingFlags.Public | BindingFlags.Instance,\r
143                                                                              Location);\r
144 \r
145                                 if (member == null || !(member is PropertyExpr || member is FieldExpr)) {\r
146                                         error617 (member_name);\r
147                                         return null;\r
148                                 }\r
149 \r
150                                 if (member is PropertyExpr) {\r
151                                         PropertyExpr pe = (PropertyExpr) member;\r
152                                         PropertyInfo pi = pe.PropertyInfo;\r
153 \r
154                                         if (!pi.CanWrite) {\r
155                                                 error617 (member_name);\r
156                                                 return null;\r
157                                         }\r
158 \r
159                                         Expression e = Expression.Reduce (ec, a.Expr);\r
160                                         \r
161                                         if (e is Literal) {\r
162                                                 object o = ((Literal) e).GetValue ();\r
163                                                 prop_values.Add (o);\r
164                                                 \r
165                                                 if (UsageAttr) {\r
166                                                         if (member_name == "AllowMultiple")\r
167                                                                 this.AllowMultiple = (bool) o;\r
168                                                         if (member_name == "Inherited")\r
169                                                                 this.Inherited = (bool) o;\r
170                                                 }\r
171                                                 \r
172                                         } else { \r
173                                                 error182 ();\r
174                                                 return null;\r
175                                         }\r
176                                         \r
177                                         prop_infos.Add (pi);\r
178                                         \r
179                                 } else if (member is FieldExpr) {\r
180                                         FieldExpr fe = (FieldExpr) member;\r
181                                         FieldInfo fi = fe.FieldInfo;\r
182 \r
183                                         if (fi.IsInitOnly) {\r
184                                                 error617 (member_name);\r
185                                                 return null;\r
186                                         }\r
187 \r
188                                         Expression e = Expression.Reduce (ec, a.Expr);\r
189                                         \r
190                                         if (e is Literal)\r
191                                                 field_values.Add (((Literal) e).GetValue ());\r
192                                         else { \r
193                                                 error182 ();\r
194                                                 return null;\r
195                                         }\r
196                                         \r
197                                         field_infos.Add (fi);\r
198                                 }\r
199                         }\r
200                         \r
201                         Expression mg = Expression.MemberLookup (ec, Type, ".ctor", false,\r
202                                                                  MemberTypes.Constructor,\r
203                                                                  BindingFlags.Public | BindingFlags.Instance,\r
204                                                                  Location);\r
205 \r
206                         if (mg == null) {\r
207                                 Report.Error (-6, Location, "Could not find a constructor for this argument list.");\r
208                                 return null;\r
209                         }\r
210 \r
211                         MethodBase constructor = Invocation.OverloadResolve (ec, (MethodGroupExpr) mg, pos_args, Location);\r
212 \r
213                         if (constructor == null) {\r
214                                 Report.Error (-6, Location, "Could not find a constructor for this argument list.");\r
215                                 return null;\r
216                         }\r
217                         \r
218                         PropertyInfo [] prop_info_arr = new PropertyInfo [prop_infos.Count];\r
219                         FieldInfo [] field_info_arr = new FieldInfo [field_infos.Count];\r
220                         object [] field_values_arr = new object [field_values.Count];\r
221                         object [] prop_values_arr = new object [prop_values.Count];\r
222 \r
223                         field_infos.CopyTo  (field_info_arr, 0);\r
224                         field_values.CopyTo (field_values_arr, 0);\r
225 \r
226                         prop_values.CopyTo  (prop_values_arr, 0);\r
227                         prop_infos.CopyTo   (prop_info_arr, 0);\r
228                         \r
229                         CustomAttributeBuilder cb = new CustomAttributeBuilder ((ConstructorInfo) constructor, pos_values,\r
230                                                                                 prop_info_arr, prop_values_arr,\r
231                                                                                 field_info_arr, field_values_arr); \r
232                         \r
233                         return cb;\r
234                 }\r
235 \r
236                 static string GetValidPlaces (Attribute attr)\r
237                 {\r
238                         StringBuilder sb = new StringBuilder ();\r
239 \r
240                         TypeContainer a = TypeManager.LookupAttr (attr.Type);\r
241 \r
242                         if (a == null)\r
243                                 return "(none) ";\r
244 \r
245                         if ((a.Targets & AttributeTargets.Assembly) != 0)\r
246                                 sb.Append ("'assembly' ");\r
247 \r
248                         if ((a.Targets & AttributeTargets.Class) != 0)\r
249                                 sb.Append ("'class' ");\r
250 \r
251                         if ((a.Targets & AttributeTargets.Constructor) != 0)\r
252                                 sb.Append ("'constructor' ");\r
253 \r
254                         if ((a.Targets & AttributeTargets.Delegate) != 0)\r
255                                 sb.Append ("'delegate' ");\r
256 \r
257                         if ((a.Targets & AttributeTargets.Enum) != 0)\r
258                                 sb.Append ("'enum' ");\r
259 \r
260                         if ((a.Targets & AttributeTargets.Event) != 0)\r
261                                 sb.Append ("'event' ");\r
262 \r
263                         if ((a.Targets & AttributeTargets.Field) != 0)\r
264                                 sb.Append ("'field' ");\r
265 \r
266                         if ((a.Targets & AttributeTargets.Interface) != 0)\r
267                                 sb.Append ("'interface' ");\r
268 \r
269                         if ((a.Targets & AttributeTargets.Method) != 0)\r
270                                 sb.Append ("'method' ");\r
271 \r
272                         if ((a.Targets & AttributeTargets.Module) != 0)\r
273                                 sb.Append ("'module' ");\r
274 \r
275                         if ((a.Targets & AttributeTargets.Parameter) != 0)\r
276                                 sb.Append ("'parameter' ");\r
277 \r
278                         if ((a.Targets & AttributeTargets.Property) != 0)\r
279                                 sb.Append ("'property' ");\r
280 \r
281                         if ((a.Targets & AttributeTargets.ReturnValue) != 0)\r
282                                 sb.Append ("'return value' ");\r
283 \r
284                         if ((a.Targets & AttributeTargets.Struct) != 0)\r
285                                 sb.Append ("'struct' ");\r
286 \r
287                         return sb.ToString ();\r
288 \r
289                 }\r
290 \r
291                 public static void Error592 (Attribute a, Location loc)\r
292                 {\r
293                         Report.Error (592, loc, "Attribute '" + a.Name + "' is not valid on this declaration type. " +\r
294                                       "It is valid on " + GetValidPlaces (a) + "declarations only.");\r
295                 }\r
296 \r
297                 public static bool CheckAttribute (Attribute a, object element)\r
298                 {\r
299                         TypeContainer attr = TypeManager.LookupAttr (a.Type);\r
300 \r
301                         if (attr == null)\r
302                                 return false;\r
303 \r
304                         if (element is Class) {\r
305                                 if ((attr.Targets & AttributeTargets.Class) != 0)\r
306                                         return true;\r
307                                 else\r
308                                         return false;\r
309                                 \r
310                         } else if (element is Struct) {\r
311                                 if ((attr.Targets & AttributeTargets.Struct) != 0)\r
312                                         return true;\r
313                                 else\r
314                                         return false;\r
315                         } else if (element is Constructor) {\r
316                                 if ((attr.Targets & AttributeTargets.Constructor) != 0)\r
317                                         return true;\r
318                                 else\r
319                                         return false;\r
320                         } else if (element is Delegate) {\r
321                                 if ((attr.Targets & AttributeTargets.Delegate) != 0)\r
322                                         return true;\r
323                                 else\r
324                                         return false;\r
325                         } else if (element is Enum) {\r
326                                 if ((attr.Targets & AttributeTargets.Enum) != 0)\r
327                                         return true;\r
328                                 else\r
329                                         return false;\r
330                         } else if (element is Event) {\r
331                                 if ((attr.Targets & AttributeTargets.Event) != 0)\r
332                                         return true;\r
333                                 else\r
334                                         return false;\r
335                         } else if (element is Field) {\r
336                                 if ((attr.Targets & AttributeTargets.Field) != 0)\r
337                                         return true;\r
338                                 else\r
339                                         return false;\r
340                         } else if (element is Interface) {\r
341                                 if ((attr.Targets & AttributeTargets.Interface) != 0)\r
342                                         return true;\r
343                                 else\r
344                                         return false;\r
345                         } else if (element is Method) {\r
346                                 if ((attr.Targets & AttributeTargets.Method) != 0)\r
347                                         return true;\r
348                                 else\r
349                                         return false;\r
350                         } else if (element is Parameter) {\r
351                                 if ((attr.Targets & AttributeTargets.Parameter) != 0)\r
352                                         return true;\r
353                                 else\r
354                                         return false;\r
355                         } else if (element is Property) {\r
356                                 if ((attr.Targets & AttributeTargets.Property) != 0)\r
357                                         return true;\r
358                                 else\r
359                                         return false;\r
360                         }\r
361                         \r
362                         return false;\r
363                 }\r
364         }\r
365         \r
366         public class AttributeSection {\r
367                 \r
368                 public readonly string    Target;\r
369                 public readonly ArrayList Attributes;\r
370                 \r
371                 public AttributeSection (string target, ArrayList attrs)\r
372                 {\r
373                         Target = target;\r
374                         Attributes = attrs;\r
375                 }\r
376                 \r
377         }\r
378 \r
379         public class Attributes {\r
380 \r
381                 public ArrayList AttributeSections;\r
382 \r
383                 public Attributes (AttributeSection a)\r
384                 {\r
385                         AttributeSections = new ArrayList ();\r
386                         AttributeSections.Add (a);\r
387 \r
388                 }\r
389 \r
390                 public void AddAttribute (AttributeSection a)\r
391                 {\r
392                         if (a != null)\r
393                                 AttributeSections.Add (a);\r
394                 }\r
395         }\r
396 }\r