d3b1ad64b2df886ee5e92c85a63ab73d9dd1fbc0
[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                 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 = RootContext.LookupType (ec.TypeContainer, name, false, Location);\r
73 \r
74                         if (Type == null) {\r
75                                 Report.Error (\r
76                                         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                                 Argument a = (Argument) pos_args [i];\r
104                                 Expression e;\r
105 \r
106                                 if (!a.Resolve (ec, Location))\r
107                                         return null;\r
108 \r
109                                 e = a.Expr;\r
110                                 if (e is Constant) {\r
111                                         pos_values [i] = ((Constant) 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                                 DictionaryEntry de = (DictionaryEntry) named_args [i];\r
132                                 string member_name = (string) de.Key;\r
133                                 Argument a  = (Argument) de.Value;\r
134                                 Expression e;\r
135                                 \r
136                                 if (!a.Resolve (ec, Location))\r
137                                         return null;\r
138 \r
139                                 Expression member = Expression.MemberLookup (\r
140                                         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                                 e = a.Expr;\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                                         if (e is Constant) {\r
161                                                 object o = ((Constant) e).GetValue ();\r
162                                                 prop_values.Add (o);\r
163                                                 \r
164                                                 if (UsageAttr) {\r
165                                                         if (member_name == "AllowMultiple")\r
166                                                                 this.AllowMultiple = (bool) o;\r
167                                                         if (member_name == "Inherited")\r
168                                                                 this.Inherited = (bool) o;\r
169                                                 }\r
170                                                 \r
171                                         } else { \r
172                                                 error182 ();\r
173                                                 return null;\r
174                                         }\r
175                                         \r
176                                         prop_infos.Add (pi);\r
177                                         \r
178                                 } else if (member is FieldExpr) {\r
179                                         FieldExpr fe = (FieldExpr) member;\r
180                                         FieldInfo fi = fe.FieldInfo;\r
181 \r
182                                         if (fi.IsInitOnly) {\r
183                                                 error617 (member_name);\r
184                                                 return null;\r
185                                         }\r
186 \r
187                                         if (e is Constant)\r
188                                                 field_values.Add (((Constant) e).GetValue ());\r
189                                         else { \r
190                                                 error182 ();\r
191                                                 return null;\r
192                                         }\r
193                                         \r
194                                         field_infos.Add (fi);\r
195                                 }\r
196                         }\r
197                         \r
198                         Expression mg = Expression.MemberLookup (\r
199                                 ec, Type, ".ctor", false, MemberTypes.Constructor,\r
200                                 BindingFlags.Public | BindingFlags.Instance, Location);\r
201 \r
202                         if (mg == null) {\r
203                                 Report.Error (\r
204                                         -6, Location,\r
205                                         "Could not find a constructor for this argument list.");\r
206                                 return null;\r
207                         }\r
208 \r
209                         MethodBase constructor = Invocation.OverloadResolve (\r
210                                 ec, (MethodGroupExpr) mg, pos_args, Location);\r
211 \r
212                         if (constructor == null) {\r
213                                 Report.Error (\r
214                                         -6, Location,\r
215                                         "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 (\r
231                                 (ConstructorInfo) constructor, pos_values,\r
232                                 prop_info_arr, prop_values_arr,\r
233                                 field_info_arr, field_values_arr); \r
234                         \r
235                         return cb;\r
236                 }\r
237 \r
238                 static string GetValidPlaces (Attribute attr)\r
239                 {\r
240                         StringBuilder sb = new StringBuilder ();\r
241                         AttributeTargets targets = 0;\r
242                         \r
243                         TypeContainer a = TypeManager.LookupAttr (attr.Type);\r
244 \r
245                         if (a == null) {\r
246                                 System.Attribute [] attrs = System.Attribute.GetCustomAttributes (attr.Type);\r
247                                 \r
248                                 foreach (System.Attribute tmp in attrs)\r
249                                         if (tmp is AttributeUsageAttribute) \r
250                                                 targets = ((AttributeUsageAttribute) tmp).ValidOn;\r
251                         } else\r
252                                 targets = a.Targets;\r
253 \r
254                         \r
255                         if ((targets & AttributeTargets.Assembly) != 0)\r
256                                 sb.Append ("'assembly' ");\r
257 \r
258                         if ((targets & AttributeTargets.Class) != 0)\r
259                                 sb.Append ("'class' ");\r
260 \r
261                         if ((targets & AttributeTargets.Constructor) != 0)\r
262                                 sb.Append ("'constructor' ");\r
263 \r
264                         if ((targets & AttributeTargets.Delegate) != 0)\r
265                                 sb.Append ("'delegate' ");\r
266 \r
267                         if ((targets & AttributeTargets.Enum) != 0)\r
268                                 sb.Append ("'enum' ");\r
269 \r
270                         if ((targets & AttributeTargets.Event) != 0)\r
271                                 sb.Append ("'event' ");\r
272 \r
273                         if ((targets & AttributeTargets.Field) != 0)\r
274                                 sb.Append ("'field' ");\r
275 \r
276                         if ((targets & AttributeTargets.Interface) != 0)\r
277                                 sb.Append ("'interface' ");\r
278 \r
279                         if ((targets & AttributeTargets.Method) != 0)\r
280                                 sb.Append ("'method' ");\r
281 \r
282                         if ((targets & AttributeTargets.Module) != 0)\r
283                                 sb.Append ("'module' ");\r
284 \r
285                         if ((targets & AttributeTargets.Parameter) != 0)\r
286                                 sb.Append ("'parameter' ");\r
287 \r
288                         if ((targets & AttributeTargets.Property) != 0)\r
289                                 sb.Append ("'property' ");\r
290 \r
291                         if ((targets & AttributeTargets.ReturnValue) != 0)\r
292                                 sb.Append ("'return value' ");\r
293 \r
294                         if ((targets & AttributeTargets.Struct) != 0)\r
295                                 sb.Append ("'struct' ");\r
296 \r
297                         return sb.ToString ();\r
298 \r
299                 }\r
300 \r
301                 public static void error592 (Attribute a, Location loc)\r
302                 {\r
303                         Report.Error (592, loc, "Attribute '" + a.Name + "' is not valid on this declaration type. " +\r
304                                       "It is valid on " + GetValidPlaces (a) + "declarations only.");\r
305                 }\r
306 \r
307                 public static bool CheckAttribute (Attribute a, object element)\r
308                 {\r
309                         TypeContainer attr = TypeManager.LookupAttr (a.Type);\r
310                         AttributeTargets targets = 0;\r
311                         \r
312                         if (attr == null) {\r
313                                 System.Attribute [] attrs = System.Attribute.GetCustomAttributes (a.Type);\r
314 \r
315                                 foreach (System.Attribute tmp in attrs)\r
316                                         if (tmp is AttributeUsageAttribute) \r
317                                                 targets = ((AttributeUsageAttribute) tmp).ValidOn;\r
318                         } else\r
319                                 targets = attr.Targets;\r
320 \r
321                         if (element is Class) {\r
322                                 if ((targets & AttributeTargets.Class) != 0)\r
323                                         return true;\r
324                                 else\r
325                                         return false;\r
326                                 \r
327                         } else if (element is Struct) {\r
328                                 if ((targets & AttributeTargets.Struct) != 0)\r
329                                         return true;\r
330                                 else\r
331                                         return false;\r
332                         } else if (element is Constructor) {\r
333                                 if ((targets & AttributeTargets.Constructor) != 0)\r
334                                         return true;\r
335                                 else\r
336                                         return false;\r
337                         } else if (element is Delegate) {\r
338                                 if ((targets & AttributeTargets.Delegate) != 0)\r
339                                         return true;\r
340                                 else\r
341                                         return false;\r
342                         } else if (element is Enum) {\r
343                                 if ((targets & AttributeTargets.Enum) != 0)\r
344                                         return true;\r
345                                 else\r
346                                         return false;\r
347                         } else if (element is Event) {\r
348                                 if ((targets & AttributeTargets.Event) != 0)\r
349                                         return true;\r
350                                 else\r
351                                         return false;\r
352                         } else if (element is Field) {\r
353                                 if ((targets & AttributeTargets.Field) != 0)\r
354                                         return true;\r
355                                 else\r
356                                         return false;\r
357                         } else if (element is Interface) {\r
358                                 if ((targets & AttributeTargets.Interface) != 0)\r
359                                         return true;\r
360                                 else\r
361                                         return false;\r
362                         } else if (element is Method) {\r
363                                 if ((targets & AttributeTargets.Method) != 0)\r
364                                         return true;\r
365                                 else\r
366                                         return false;\r
367                         } else if (element is Parameter) {\r
368                                 if ((targets & AttributeTargets.Parameter) != 0)\r
369                                         return true;\r
370                                 else\r
371                                         return false;\r
372                         } else if (element is Property) {\r
373                                 if ((targets & AttributeTargets.Property) != 0)\r
374                                         return true;\r
375                                 else\r
376                                         return false;\r
377                         }\r
378 \r
379                         return false;\r
380                 }\r
381 \r
382                 public static void ApplyAttributes (EmitContext ec, object builder, object kind,\r
383                                                     Attributes opt_attrs, Location loc)\r
384                 {\r
385                         if (opt_attrs == null)\r
386                                 return;\r
387 \r
388                         if (opt_attrs.AttributeSections == null)\r
389                                 return;\r
390 \r
391                         foreach (AttributeSection asec in opt_attrs.AttributeSections) {\r
392 \r
393                                 if (asec.Attributes == null)\r
394                                         continue;\r
395 \r
396                                 foreach (Attribute a in asec.Attributes) {\r
397                                         CustomAttributeBuilder cb = a.Resolve (ec);\r
398                                         if (cb == null)\r
399                                                 continue;\r
400 \r
401                                         if (!(kind is TypeContainer))\r
402                                                 if (!CheckAttribute (a, kind)) {\r
403                                                         error592 (a, loc);\r
404                                                         return;\r
405                                                 }\r
406 \r
407                                         \r
408                                         if (kind is Method) {\r
409                                                 if (a.Type != TypeManager.dllimport_type)\r
410                                                         ((MethodBuilder) builder).SetCustomAttribute (cb);\r
411 \r
412                                         } else if (kind is Constructor) {\r
413                                                 ((ConstructorBuilder) builder).SetCustomAttribute (cb);\r
414 \r
415                                         } else if (kind is Field) {\r
416                                                 ((FieldBuilder) builder).SetCustomAttribute (cb);\r
417 \r
418                                         } else if (kind is Property || kind is Indexer) {\r
419                                                 ((PropertyBuilder) builder).SetCustomAttribute (cb);\r
420 \r
421                                         } else if (kind is Event) {\r
422                                                 ((EventBuilder) builder).SetCustomAttribute (cb);\r
423 \r
424                                         } else if (kind is Operator) {\r
425                                                 ((MethodBuilder) builder).SetCustomAttribute (cb);\r
426 \r
427                                         } else if (kind is Enum) {\r
428                                                 ((TypeBuilder) builder).SetCustomAttribute (cb); \r
429 \r
430                                         } else if (kind is TypeContainer) {\r
431 \r
432                                                 TypeContainer tc = (TypeContainer) kind;\r
433                                                 \r
434                                                 if (a.UsageAttr) {\r
435                                                         tc.Targets = a.Targets;\r
436                                                         tc.AllowMultiple = a.AllowMultiple;\r
437                                                         tc.Inherited = a.Inherited;\r
438                                                         \r
439                                                         RootContext.TypeManager.RegisterAttrType (\r
440                                                                                  (TypeBuilder) builder, tc);\r
441 \r
442                                                 } else {\r
443                                                         if (!CheckAttribute (a, kind)) {\r
444                                                                 error592 (a, loc);\r
445                                                                 return;\r
446                                                         }\r
447                                                 }\r
448                                                 \r
449                                                 ((TypeBuilder) builder).SetCustomAttribute (cb);\r
450                                                 \r
451                                         }\r
452                                 }\r
453                         }\r
454                 }\r
455 \r
456                 public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name,\r
457                                                           MethodAttributes flags, Type ret_type, Type [] param_types)\r
458                 {\r
459                         //\r
460                         // We extract from the attribute the information we need \r
461                         //\r
462 \r
463                         if (Arguments == null) {\r
464                                 Console.WriteLine ("Internal error : this is not supposed to happen !");\r
465                                 return null;\r
466                         }\r
467 \r
468                         string attr_name = Name;\r
469 \r
470                         if (Name.IndexOf ("Attribute") == -1)\r
471                                 attr_name = Name + "Attribute";\r
472                         else if (Name.LastIndexOf ("Attribute") == 0)\r
473                                 attr_name = Name + "Attribute";\r
474                         \r
475                         Type = RootContext.LookupType (ec.TypeContainer, attr_name, false, Location);\r
476 \r
477                         if (Type == null) {\r
478                                 Report.Error (246, Location, "Could not find attribute '" + Name + "' (are you" +\r
479                                               " missing a using directive or an assembly reference ?)");\r
480                                 return null;\r
481                         }\r
482                         \r
483                         ArrayList named_args = new ArrayList ();\r
484                         \r
485                         ArrayList pos_args = (ArrayList) Arguments [0];\r
486                         if (Arguments.Count > 1)\r
487                                 named_args = (ArrayList) Arguments [1];\r
488                         \r
489 \r
490                         string dll_name = null;\r
491                         \r
492                         Argument tmp = (Argument) pos_args [0];\r
493 \r
494                         if (!tmp.Resolve (ec, Location))\r
495                                 return null;\r
496                         \r
497                         if (tmp.Expr is Constant)\r
498                                 dll_name = (string) ((Constant) tmp.Expr).GetValue ();\r
499                         else { \r
500                                 error182 ();\r
501                                 return null;\r
502                         }\r
503 \r
504                         // Now we process the named arguments\r
505                         CallingConvention cc = 0;\r
506                         CharSet charset = 0;\r
507                         bool preserve_sig = false;\r
508                         bool exact_spelling = false;\r
509                         bool set_last_err = false;\r
510                         string entry_point = null;\r
511                         \r
512                         for (int i = 0; i < named_args.Count; i++) {\r
513 \r
514                                 DictionaryEntry de = (DictionaryEntry) named_args [i];\r
515 \r
516                                 string member_name = (string) de.Key;\r
517                                 Argument a  = (Argument) de.Value;\r
518 \r
519                                 if (!a.Resolve (ec, Location))\r
520                                         return null;\r
521 \r
522                                 Expression member = Expression.MemberLookup (ec, Type, member_name, false,\r
523                                                                              MemberTypes.Field | MemberTypes.Property,\r
524                                                                              BindingFlags.Public | BindingFlags.Instance,\r
525                                                                              Location);\r
526 \r
527                                 if (member == null || !(member is FieldExpr)) {\r
528                                         error617 (member_name);\r
529                                         return null;\r
530                                 }\r
531 \r
532                                 if (member is FieldExpr) {\r
533                                         FieldExpr fe = (FieldExpr) member;\r
534                                         FieldInfo fi = fe.FieldInfo;\r
535 \r
536                                         if (fi.IsInitOnly) {\r
537                                                 error617 (member_name);\r
538                                                 return null;\r
539                                         }\r
540 \r
541                                         if (a.Expr is Constant) {\r
542                                                 Constant c = (Constant) a.Expr;\r
543                                                 \r
544                                                 if (member_name == "CallingConvention")\r
545                                                         cc = (CallingConvention) c.GetValue ();\r
546                                                 else if (member_name == "CharSet")\r
547                                                         charset = (CharSet) c.GetValue ();\r
548                                                 else if (member_name == "EntryPoint")\r
549                                                         entry_point = (string) c.GetValue ();\r
550                                                 else if (member_name == "SetLastError")\r
551                                                         set_last_err = (bool) c.GetValue ();\r
552                                                 else if (member_name == "ExactSpelling")\r
553                                                         exact_spelling = (bool) c.GetValue ();\r
554                                                 else if (member_name == "PreserveSig")\r
555                                                         preserve_sig = (bool) c.GetValue ();\r
556                                         } else { \r
557                                                 error182 ();\r
558                                                 return null;\r
559                                         }\r
560                                         \r
561                                 }\r
562                         }\r
563 \r
564                         MethodBuilder mb = builder.DefinePInvokeMethod (\r
565                                 name, dll_name, flags,\r
566                                 CallingConventions.Standard,\r
567                                 ret_type,\r
568                                 param_types,\r
569                                 cc,\r
570                                 charset); \r
571                         \r
572                         return mb;\r
573                 }\r
574                 \r
575         }\r
576         \r
577         public class AttributeSection {\r
578                 \r
579                 public readonly string    Target;\r
580                 public readonly ArrayList Attributes;\r
581                 \r
582                 public AttributeSection (string target, ArrayList attrs)\r
583                 {\r
584                         Target = target;\r
585                         Attributes = attrs;\r
586                 }\r
587                 \r
588         }\r
589 \r
590         public class Attributes {\r
591 \r
592                 public ArrayList AttributeSections;\r
593 \r
594                 public Attributes (AttributeSection a)\r
595                 {\r
596                         AttributeSections = new ArrayList ();\r
597                         AttributeSections.Add (a);\r
598 \r
599                 }\r
600 \r
601                 public void AddAttribute (AttributeSection a)\r
602                 {\r
603                         if (a != null)\r
604                                 AttributeSections.Add (a);\r
605                 }\r
606         }\r
607 }\r