55bec4fe9b38c2ce17c34f9cc33ebfc6bed2032d
[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.Runtime.InteropServices;\r
17 using System.Text;\r
18 \r
19 namespace Mono.CSharp {\r
20 \r
21         public class Attribute {\r
22                 \r
23                 public readonly string    Name;\r
24                 public readonly ArrayList Arguments;\r
25 \r
26                 Location Location;\r
27 \r
28                 public Type Type;\r
29                 \r
30                 //\r
31                 // The following are only meaningful when the attribute\r
32                 // being emitted is one of the builtin ones\r
33                 //\r
34                 public AttributeTargets Targets;\r
35                 public bool AllowMultiple;\r
36                 public bool Inherited;\r
37 \r
38                 public bool UsageAttr = false;\r
39                 \r
40                 public Attribute (string name, ArrayList args, Location loc)\r
41                 {\r
42                         Name = name;\r
43                         Arguments = args;\r
44                         Location = loc;\r
45                 }\r
46 \r
47                 void error617 (string name)\r
48                 {\r
49                         Report.Error (617, Location, "'" + name + "' is not a valid named attribute " +\r
50                                       "argument. Named attribute arguments must be fields which are not " +\r
51                                       "readonly, static or const, or properties with a set accessor which "+\r
52                                       "are not static.");\r
53                 }\r
54 \r
55                 void error182 ()\r
56                 {\r
57                         Report.Error (182, Location,\r
58                                       "An attribute argument must be a constant expression, typeof " +\r
59                                       "expression or array creation expression");\r
60                 }\r
61 \r
62                 public CustomAttributeBuilder Resolve (EmitContext ec)\r
63                 {\r
64                         string name = Name;\r
65 \r
66                         UsageAttr = false;\r
67 \r
68                         if (Name.IndexOf ("Attribute") == -1)\r
69                                 name = Name + "Attribute";\r
70                         else if (Name.LastIndexOf ("Attribute") == 0)\r
71                                 name = Name + "Attribute";\r
72                         \r
73                         Type = ec.TypeContainer.LookupType (name, false);\r
74 \r
75                         if (Type == null) {\r
76                                 Report.Error (246, Location, "Could not find attribute '" + Name + "' (are you" +\r
77                                               " missing a using directive or an assembly reference ?)");\r
78                                 return null;\r
79                         }\r
80 \r
81                         if (Type == TypeManager.attribute_usage_type)\r
82                                 UsageAttr = true;\r
83                         \r
84                         // Now we extract the positional and named arguments\r
85                         \r
86                         ArrayList pos_args = new ArrayList ();\r
87                         ArrayList named_args = new ArrayList ();\r
88                         \r
89                         if (Arguments != null) {\r
90                                 pos_args = (ArrayList) Arguments [0];\r
91                                 if (Arguments.Count > 1)\r
92                                         named_args = (ArrayList) Arguments [1];\r
93                         }\r
94                                 \r
95                         object [] pos_values = new object [pos_args.Count];\r
96 \r
97                         //\r
98                         // First process positional arguments \r
99                         //\r
100                         \r
101                         int i;\r
102                         for (i = 0; i < pos_args.Count; i++) {\r
103 \r
104                                 Argument a = (Argument) pos_args [i];\r
105 \r
106                                 if (!a.Resolve (ec, Location))\r
107                                         return null;\r
108 \r
109                                 Expression e = Expression.Reduce (ec, a.Expr);\r
110 \r
111                                 if (e is Literal) {\r
112                                         pos_values [i] = ((Literal) e).GetValue ();\r
113 \r
114                                         if (UsageAttr)\r
115                                                 this.Targets = (AttributeTargets) pos_values [0];\r
116                                 } else { \r
117                                         error182 ();\r
118                                         return null;\r
119                                 }\r
120                         }\r
121 \r
122                         //\r
123                         // Now process named arguments\r
124                         //\r
125 \r
126                         ArrayList field_infos = new ArrayList ();\r
127                         ArrayList prop_infos  = new ArrayList ();\r
128                         ArrayList field_values = new ArrayList ();\r
129                         ArrayList prop_values = new ArrayList ();\r
130 \r
131                         for (i = 0; i < named_args.Count; i++) {\r
132 \r
133                                 DictionaryEntry de = (DictionaryEntry) named_args [i];\r
134 \r
135                                 string member_name = (string) de.Key;\r
136                                 Argument a  = (Argument) de.Value;\r
137 \r
138                                 if (!a.Resolve (ec, Location))\r
139                                         return null;\r
140 \r
141                                 Expression member = Expression.MemberLookup (ec, Type, member_name, false,\r
142                                                                              MemberTypes.Field | MemberTypes.Property,\r
143                                                                              BindingFlags.Public | BindingFlags.Instance,\r
144                                                                              Location);\r
145 \r
146                                 if (member == null || !(member is PropertyExpr || member is FieldExpr)) {\r
147                                         error617 (member_name);\r
148                                         return null;\r
149                                 }\r
150 \r
151                                 if (member is PropertyExpr) {\r
152                                         PropertyExpr pe = (PropertyExpr) member;\r
153                                         PropertyInfo pi = pe.PropertyInfo;\r
154 \r
155                                         if (!pi.CanWrite) {\r
156                                                 error617 (member_name);\r
157                                                 return null;\r
158                                         }\r
159 \r
160                                         Expression e = Expression.Reduce (ec, a.Expr);\r
161                                         \r
162                                         if (e is Literal) {\r
163                                                 object o = ((Literal) e).GetValue ();\r
164                                                 prop_values.Add (o);\r
165                                                 \r
166                                                 if (UsageAttr) {\r
167                                                         if (member_name == "AllowMultiple")\r
168                                                                 this.AllowMultiple = (bool) o;\r
169                                                         if (member_name == "Inherited")\r
170                                                                 this.Inherited = (bool) o;\r
171                                                 }\r
172                                                 \r
173                                         } else { \r
174                                                 error182 ();\r
175                                                 return null;\r
176                                         }\r
177                                         \r
178                                         prop_infos.Add (pi);\r
179                                         \r
180                                 } else if (member is FieldExpr) {\r
181                                         FieldExpr fe = (FieldExpr) member;\r
182                                         FieldInfo fi = fe.FieldInfo;\r
183 \r
184                                         if (fi.IsInitOnly) {\r
185                                                 error617 (member_name);\r
186                                                 return null;\r
187                                         }\r
188 \r
189                                         Expression e = Expression.Reduce (ec, a.Expr);\r
190                                         \r
191                                         if (e is Literal)\r
192                                                 field_values.Add (((Literal) e).GetValue ());\r
193                                         else { \r
194                                                 error182 ();\r
195                                                 return null;\r
196                                         }\r
197                                         \r
198                                         field_infos.Add (fi);\r
199                                 }\r
200                         }\r
201                         \r
202                         Expression mg = Expression.MemberLookup (ec, Type, ".ctor", false,\r
203                                                                  MemberTypes.Constructor,\r
204                                                                  BindingFlags.Public | BindingFlags.Instance,\r
205                                                                  Location);\r
206 \r
207                         if (mg == null) {\r
208                                 Report.Error (-6, Location, "Could not find a constructor for this argument list.");\r
209                                 return null;\r
210                         }\r
211 \r
212                         MethodBase constructor = Invocation.OverloadResolve (ec, (MethodGroupExpr) mg, pos_args, Location);\r
213 \r
214                         if (constructor == null) {\r
215                                 Report.Error (-6, Location, "Could not find a constructor for this argument list.");\r
216                                 return null;\r
217                         }\r
218                         \r
219                         PropertyInfo [] prop_info_arr = new PropertyInfo [prop_infos.Count];\r
220                         FieldInfo [] field_info_arr = new FieldInfo [field_infos.Count];\r
221                         object [] field_values_arr = new object [field_values.Count];\r
222                         object [] prop_values_arr = new object [prop_values.Count];\r
223 \r
224                         field_infos.CopyTo  (field_info_arr, 0);\r
225                         field_values.CopyTo (field_values_arr, 0);\r
226 \r
227                         prop_values.CopyTo  (prop_values_arr, 0);\r
228                         prop_infos.CopyTo   (prop_info_arr, 0);\r
229                         \r
230                         CustomAttributeBuilder cb = new CustomAttributeBuilder ((ConstructorInfo) constructor, pos_values,\r
231                                                                                 prop_info_arr, prop_values_arr,\r
232                                                                                 field_info_arr, field_values_arr); \r
233                         \r
234                         return cb;\r
235                 }\r
236 \r
237                 static string GetValidPlaces (Attribute attr)\r
238                 {\r
239                         StringBuilder sb = new StringBuilder ();\r
240                         AttributeTargets targets = 0;\r
241                         \r
242                         TypeContainer a = TypeManager.LookupAttr (attr.Type);\r
243 \r
244                         if (a == null) {\r
245                                 System.Attribute [] attrs = System.Attribute.GetCustomAttributes (attr.Type);\r
246                                 \r
247                                 foreach (System.Attribute tmp in attrs)\r
248                                         if (tmp is AttributeUsageAttribute) \r
249                                                 targets = ((AttributeUsageAttribute) tmp).ValidOn;\r
250                         } else\r
251                                 targets = a.Targets;\r
252 \r
253                         \r
254                         if ((targets & AttributeTargets.Assembly) != 0)\r
255                                 sb.Append ("'assembly' ");\r
256 \r
257                         if ((targets & AttributeTargets.Class) != 0)\r
258                                 sb.Append ("'class' ");\r
259 \r
260                         if ((targets & AttributeTargets.Constructor) != 0)\r
261                                 sb.Append ("'constructor' ");\r
262 \r
263                         if ((targets & AttributeTargets.Delegate) != 0)\r
264                                 sb.Append ("'delegate' ");\r
265 \r
266                         if ((targets & AttributeTargets.Enum) != 0)\r
267                                 sb.Append ("'enum' ");\r
268 \r
269                         if ((targets & AttributeTargets.Event) != 0)\r
270                                 sb.Append ("'event' ");\r
271 \r
272                         if ((targets & AttributeTargets.Field) != 0)\r
273                                 sb.Append ("'field' ");\r
274 \r
275                         if ((targets & AttributeTargets.Interface) != 0)\r
276                                 sb.Append ("'interface' ");\r
277 \r
278                         if ((targets & AttributeTargets.Method) != 0)\r
279                                 sb.Append ("'method' ");\r
280 \r
281                         if ((targets & AttributeTargets.Module) != 0)\r
282                                 sb.Append ("'module' ");\r
283 \r
284                         if ((targets & AttributeTargets.Parameter) != 0)\r
285                                 sb.Append ("'parameter' ");\r
286 \r
287                         if ((targets & AttributeTargets.Property) != 0)\r
288                                 sb.Append ("'property' ");\r
289 \r
290                         if ((targets & AttributeTargets.ReturnValue) != 0)\r
291                                 sb.Append ("'return value' ");\r
292 \r
293                         if ((targets & AttributeTargets.Struct) != 0)\r
294                                 sb.Append ("'struct' ");\r
295 \r
296                         return sb.ToString ();\r
297 \r
298                 }\r
299 \r
300                 public static void Error592 (Attribute a, Location loc)\r
301                 {\r
302                         Report.Error (592, loc, "Attribute '" + a.Name + "' is not valid on this declaration type. " +\r
303                                       "It is valid on " + GetValidPlaces (a) + "declarations only.");\r
304                 }\r
305 \r
306                 public static bool CheckAttribute (Attribute a, object element)\r
307                 {\r
308                         TypeContainer attr = TypeManager.LookupAttr (a.Type);\r
309                         AttributeTargets targets = 0;\r
310                         \r
311                         if (attr == null) {\r
312                                 System.Attribute [] attrs = System.Attribute.GetCustomAttributes (a.Type);\r
313 \r
314                                 foreach (System.Attribute tmp in attrs)\r
315                                         if (tmp is AttributeUsageAttribute) \r
316                                                 targets = ((AttributeUsageAttribute) tmp).ValidOn;\r
317                         } else\r
318                                 targets = attr.Targets;\r
319 \r
320                         if (element is Class) {\r
321                                 if ((targets & AttributeTargets.Class) != 0)\r
322                                         return true;\r
323                                 else\r
324                                         return false;\r
325                                 \r
326                         } else if (element is Struct) {\r
327                                 if ((targets & AttributeTargets.Struct) != 0)\r
328                                         return true;\r
329                                 else\r
330                                         return false;\r
331                         } else if (element is Constructor) {\r
332                                 if ((targets & AttributeTargets.Constructor) != 0)\r
333                                         return true;\r
334                                 else\r
335                                         return false;\r
336                         } else if (element is Delegate) {\r
337                                 if ((targets & AttributeTargets.Delegate) != 0)\r
338                                         return true;\r
339                                 else\r
340                                         return false;\r
341                         } else if (element is Enum) {\r
342                                 if ((targets & AttributeTargets.Enum) != 0)\r
343                                         return true;\r
344                                 else\r
345                                         return false;\r
346                         } else if (element is Event) {\r
347                                 if ((targets & AttributeTargets.Event) != 0)\r
348                                         return true;\r
349                                 else\r
350                                         return false;\r
351                         } else if (element is Field) {\r
352                                 if ((targets & AttributeTargets.Field) != 0)\r
353                                         return true;\r
354                                 else\r
355                                         return false;\r
356                         } else if (element is Interface) {\r
357                                 if ((targets & AttributeTargets.Interface) != 0)\r
358                                         return true;\r
359                                 else\r
360                                         return false;\r
361                         } else if (element is Method) {\r
362                                 if ((targets & AttributeTargets.Method) != 0)\r
363                                         return true;\r
364                                 else\r
365                                         return false;\r
366                         } else if (element is Parameter) {\r
367                                 if ((targets & AttributeTargets.Parameter) != 0)\r
368                                         return true;\r
369                                 else\r
370                                         return false;\r
371                         } else if (element is Property) {\r
372                                 if ((targets & AttributeTargets.Property) != 0)\r
373                                         return true;\r
374                                 else\r
375                                         return false;\r
376                         }\r
377 \r
378                         return false;\r
379                 }\r
380 \r
381                 public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name,\r
382                                                           MethodAttributes flags, Type ret_type, Type [] param_types)\r
383                 {\r
384                         //\r
385                         // We extract from the attribute the information we need from the attribute\r
386                         //\r
387 \r
388                         if (Arguments == null) {\r
389                                 Console.WriteLine ("Internal error : this is not supposed to happen !");\r
390                                 return null;\r
391                         }\r
392                         \r
393                         ArrayList named_args = new ArrayList ();\r
394                         \r
395                         ArrayList pos_args = (ArrayList) Arguments [0];\r
396                         if (Arguments.Count > 1)\r
397                                 named_args = (ArrayList) Arguments [1];\r
398                         \r
399 \r
400                         string dll_name = null;\r
401                         \r
402                         Argument tmp = (Argument) pos_args [0];\r
403 \r
404                         if (!tmp.Resolve (ec, Location))\r
405                                 return null;\r
406                         \r
407                         Expression exp = Expression.Reduce (ec, tmp.Expr);\r
408                         \r
409                         if (exp is Literal)\r
410                                 dll_name = (string) ((Literal) exp).GetValue ();\r
411                         else { \r
412                                 error182 ();\r
413                                 return null;\r
414                         }\r
415 \r
416                         // FIXME : We need to process CallingConvention and other named\r
417                         // args but for now, we ignore them.\r
418                         \r
419                         MethodBuilder mb = builder.DefinePInvokeMethod (\r
420                                                                    name, dll_name, flags,\r
421                                                                    CallingConventions.Standard,\r
422                                                                    ret_type,\r
423                                                                    param_types,\r
424                                                                    CallingConvention.StdCall,\r
425                                                                    CharSet.Auto); \r
426                         \r
427                         return mb;\r
428                 }\r
429                 \r
430         }\r
431         \r
432         public class AttributeSection {\r
433                 \r
434                 public readonly string    Target;\r
435                 public readonly ArrayList Attributes;\r
436                 \r
437                 public AttributeSection (string target, ArrayList attrs)\r
438                 {\r
439                         Target = target;\r
440                         Attributes = attrs;\r
441                 }\r
442                 \r
443         }\r
444 \r
445         public class Attributes {\r
446 \r
447                 public ArrayList AttributeSections;\r
448 \r
449                 public Attributes (AttributeSection a)\r
450                 {\r
451                         AttributeSections = new ArrayList ();\r
452                         AttributeSections.Add (a);\r
453 \r
454                 }\r
455 \r
456                 public void AddAttribute (AttributeSection a)\r
457                 {\r
458                         if (a != null)\r
459                                 AttributeSections.Add (a);\r
460                 }\r
461         }\r
462 }\r