2002-03-01 Ravi Pratap <ravi@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.Runtime.InteropServices;\r
17 using System.Runtime.CompilerServices;\r
18 using System.Text;\r
19 \r
20 namespace Mono.CSharp {\r
21 \r
22         public class Attribute {\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 MethodImplOptions ImplOptions;\r
41                 \r
42                 public Attribute (string name, ArrayList args, Location loc)\r
43                 {\r
44                         Name = name;\r
45                         Arguments = args;\r
46                         Location = loc;\r
47                 }\r
48 \r
49                 void error617 (string name)\r
50                 {\r
51                         Report.Error (617, Location, "'" + name + "' is not a valid named attribute " +\r
52                                       "argument. Named attribute arguments must be fields which are not " +\r
53                                       "readonly, static or const, or properties with a set accessor which "+\r
54                                       "are not static.");\r
55                 }\r
56 \r
57                 void error182 ()\r
58                 {\r
59                         Report.Error (182, Location,\r
60                                       "An attribute argument must be a constant expression, typeof " +\r
61                                       "expression or array creation expression");\r
62                 }\r
63 \r
64                 public CustomAttributeBuilder Resolve (EmitContext ec)\r
65                 {\r
66                         string name = Name;\r
67                         bool MethodImplAttr = false;\r
68 \r
69                         UsageAttr = false;\r
70 \r
71                         if (Name.IndexOf ("Attribute") == -1)\r
72                                 name = Name + "Attribute";\r
73                         else if (Name.LastIndexOf ("Attribute") == 0)\r
74                                 name = Name + "Attribute";\r
75 \r
76                         Type = RootContext.LookupType (ec.TypeContainer, name, false, Location);\r
77 \r
78                         if (Type == null) {\r
79                                 Report.Error (\r
80                                         246, Location, "Could not find attribute '" + Name + "' (are you" +\r
81                                         " missing a using directive or an assembly reference ?)");\r
82                                 return null;\r
83                         }\r
84 \r
85                         if (Type == TypeManager.attribute_usage_type)\r
86                                 UsageAttr = true;\r
87                         if (Type == TypeManager.methodimpl_attr_type)\r
88                                 MethodImplAttr = true;\r
89 \r
90                         // Now we extract the positional and named arguments\r
91                         \r
92                         ArrayList pos_args = new ArrayList ();\r
93                         ArrayList named_args = new ArrayList ();\r
94                         \r
95                         if (Arguments != null) {\r
96                                 pos_args = (ArrayList) Arguments [0];\r
97                                 if (Arguments.Count > 1)\r
98                                         named_args = (ArrayList) Arguments [1];\r
99                         }\r
100                                 \r
101                         object [] pos_values = new object [pos_args.Count];\r
102 \r
103                         //\r
104                         // First process positional arguments \r
105                         //\r
106                         \r
107                         int i;\r
108                         for (i = 0; i < pos_args.Count; i++) {\r
109                                 Argument a = (Argument) pos_args [i];\r
110                                 Expression e;\r
111 \r
112                                 if (!a.Resolve (ec, Location))\r
113                                         return null;\r
114 \r
115                                 e = a.Expr;\r
116                                 if (e is Constant) {\r
117                                         pos_values [i] = ((Constant) e).GetValue ();\r
118 \r
119                                         if (UsageAttr)\r
120                                                 this.Targets = (AttributeTargets) pos_values [0];\r
121 \r
122                                         if (MethodImplAttr)\r
123                                                 this.ImplOptions = (MethodImplOptions) pos_values [0];\r
124                                         \r
125                                 } else { \r
126                                         error182 ();\r
127                                         return null;\r
128                                 }\r
129                         }\r
130 \r
131                         //\r
132                         // Now process named arguments\r
133                         //\r
134 \r
135                         ArrayList field_infos = new ArrayList ();\r
136                         ArrayList prop_infos  = new ArrayList ();\r
137                         ArrayList field_values = new ArrayList ();\r
138                         ArrayList prop_values = new ArrayList ();\r
139                         \r
140                         for (i = 0; i < named_args.Count; i++) {\r
141                                 DictionaryEntry de = (DictionaryEntry) named_args [i];\r
142                                 string member_name = (string) de.Key;\r
143                                 Argument a  = (Argument) de.Value;\r
144                                 Expression e;\r
145                                 \r
146                                 if (!a.Resolve (ec, Location))\r
147                                         return null;\r
148 \r
149                                 Expression member = Expression.MemberLookup (\r
150                                         ec, Type, member_name,\r
151                                         MemberTypes.Field | MemberTypes.Property,\r
152                                         BindingFlags.Public | BindingFlags.Instance,\r
153                                         Location);\r
154 \r
155                                 if (member == null || !(member is PropertyExpr || member is FieldExpr)) {\r
156                                         error617 (member_name);\r
157                                         return null;\r
158                                 }\r
159 \r
160                                 e = a.Expr;\r
161                                 if (member is PropertyExpr) {\r
162                                         PropertyExpr pe = (PropertyExpr) member;\r
163                                         PropertyInfo pi = pe.PropertyInfo;\r
164 \r
165                                         if (!pi.CanWrite) {\r
166                                                 error617 (member_name);\r
167                                                 return null;\r
168                                         }\r
169 \r
170                                         if (e is Constant) {\r
171                                                 object o = ((Constant) e).GetValue ();\r
172                                                 prop_values.Add (o);\r
173                                                 \r
174                                                 if (UsageAttr) {\r
175                                                         if (member_name == "AllowMultiple")\r
176                                                                 this.AllowMultiple = (bool) o;\r
177                                                         if (member_name == "Inherited")\r
178                                                                 this.Inherited = (bool) o;\r
179                                                 }\r
180                                                 \r
181                                         } else { \r
182                                                 error182 ();\r
183                                                 return null;\r
184                                         }\r
185                                         \r
186                                         prop_infos.Add (pi);\r
187                                         \r
188                                 } else if (member is FieldExpr) {\r
189                                         FieldExpr fe = (FieldExpr) member;\r
190                                         FieldInfo fi = fe.FieldInfo;\r
191 \r
192                                         if (fi.IsInitOnly) {\r
193                                                 error617 (member_name);\r
194                                                 return null;\r
195                                         }\r
196 \r
197                                         if (e is Constant)\r
198                                                 field_values.Add (((Constant) e).GetValue ());\r
199                                         else { \r
200                                                 error182 ();\r
201                                                 return null;\r
202                                         }\r
203                                         \r
204                                         field_infos.Add (fi);\r
205                                 }\r
206                         }\r
207                         \r
208                         Expression mg = Expression.MemberLookup (\r
209                                 ec, Type, ".ctor", MemberTypes.Constructor,\r
210                                 BindingFlags.Public | BindingFlags.Instance, Location);\r
211 \r
212                         if (mg == 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                         MethodBase constructor = Invocation.OverloadResolve (\r
220                                 ec, (MethodGroupExpr) mg, pos_args, Location);\r
221 \r
222                         if (constructor == null) {\r
223                                 Report.Error (\r
224                                         -6, Location,\r
225                                         "Could not find a constructor for this argument list.");\r
226                                 return null;\r
227                         }\r
228                         \r
229                         PropertyInfo [] prop_info_arr = new PropertyInfo [prop_infos.Count];\r
230                         FieldInfo [] field_info_arr = new FieldInfo [field_infos.Count];\r
231                         object [] field_values_arr = new object [field_values.Count];\r
232                         object [] prop_values_arr = new object [prop_values.Count];\r
233 \r
234                         field_infos.CopyTo  (field_info_arr, 0);\r
235                         field_values.CopyTo (field_values_arr, 0);\r
236 \r
237                         prop_values.CopyTo  (prop_values_arr, 0);\r
238                         prop_infos.CopyTo   (prop_info_arr, 0);\r
239                         \r
240                         CustomAttributeBuilder cb = new CustomAttributeBuilder (\r
241                                 (ConstructorInfo) constructor, pos_values,\r
242                                 prop_info_arr, prop_values_arr,\r
243                                 field_info_arr, field_values_arr); \r
244                         \r
245                         return cb;\r
246                 }\r
247 \r
248                 static string GetValidPlaces (Attribute attr)\r
249                 {\r
250                         StringBuilder sb = new StringBuilder ();\r
251                         AttributeTargets targets = 0;\r
252                         \r
253                         TypeContainer a = TypeManager.LookupAttr (attr.Type);\r
254 \r
255                         if (a == null) {\r
256                                 System.Attribute [] attrs = System.Attribute.GetCustomAttributes (attr.Type);\r
257                                 \r
258                                 foreach (System.Attribute tmp in attrs)\r
259                                         if (tmp is AttributeUsageAttribute) \r
260                                                 targets = ((AttributeUsageAttribute) tmp).ValidOn;\r
261                         } else\r
262                                 targets = a.Targets;\r
263 \r
264                         \r
265                         if ((targets & AttributeTargets.Assembly) != 0)\r
266                                 sb.Append ("'assembly' ");\r
267 \r
268                         if ((targets & AttributeTargets.Class) != 0)\r
269                                 sb.Append ("'class' ");\r
270 \r
271                         if ((targets & AttributeTargets.Constructor) != 0)\r
272                                 sb.Append ("'constructor' ");\r
273 \r
274                         if ((targets & AttributeTargets.Delegate) != 0)\r
275                                 sb.Append ("'delegate' ");\r
276 \r
277                         if ((targets & AttributeTargets.Enum) != 0)\r
278                                 sb.Append ("'enum' ");\r
279 \r
280                         if ((targets & AttributeTargets.Event) != 0)\r
281                                 sb.Append ("'event' ");\r
282 \r
283                         if ((targets & AttributeTargets.Field) != 0)\r
284                                 sb.Append ("'field' ");\r
285 \r
286                         if ((targets & AttributeTargets.Interface) != 0)\r
287                                 sb.Append ("'interface' ");\r
288 \r
289                         if ((targets & AttributeTargets.Method) != 0)\r
290                                 sb.Append ("'method' ");\r
291 \r
292                         if ((targets & AttributeTargets.Module) != 0)\r
293                                 sb.Append ("'module' ");\r
294 \r
295                         if ((targets & AttributeTargets.Parameter) != 0)\r
296                                 sb.Append ("'parameter' ");\r
297 \r
298                         if ((targets & AttributeTargets.Property) != 0)\r
299                                 sb.Append ("'property' ");\r
300 \r
301                         if ((targets & AttributeTargets.ReturnValue) != 0)\r
302                                 sb.Append ("'return value' ");\r
303 \r
304                         if ((targets & AttributeTargets.Struct) != 0)\r
305                                 sb.Append ("'struct' ");\r
306 \r
307                         return sb.ToString ();\r
308 \r
309                 }\r
310 \r
311                 public static void error592 (Attribute a, Location loc)\r
312                 {\r
313                         Report.Error (\r
314                                 592, loc, "Attribute '" + a.Name +\r
315                                 "' is not valid on this declaration type. " +\r
316                                 "It is valid on " + GetValidPlaces (a) + "declarations only.");\r
317                 }\r
318 \r
319                 public static bool CheckAttribute (Attribute a, object element)\r
320                 {\r
321                         TypeContainer attr = TypeManager.LookupAttr (a.Type);\r
322                         AttributeTargets targets = 0;\r
323                         \r
324                         if (attr == null) {\r
325                                 System.Attribute [] attrs = System.Attribute.GetCustomAttributes (a.Type);\r
326 \r
327                                 foreach (System.Attribute tmp in attrs)\r
328                                         if (tmp is AttributeUsageAttribute) \r
329                                                 targets = ((AttributeUsageAttribute) tmp).ValidOn;\r
330                         } else\r
331                                 targets = attr.Targets;\r
332 \r
333                         if (element is Class) {\r
334                                 if ((targets & AttributeTargets.Class) != 0)\r
335                                         return true;\r
336                                 else\r
337                                         return false;\r
338                                 \r
339                         } else if (element is Struct) {\r
340                                 if ((targets & AttributeTargets.Struct) != 0)\r
341                                         return true;\r
342                                 else\r
343                                         return false;\r
344                         } else if (element is Constructor) {\r
345                                 if ((targets & AttributeTargets.Constructor) != 0)\r
346                                         return true;\r
347                                 else\r
348                                         return false;\r
349                         } else if (element is Delegate) {\r
350                                 if ((targets & AttributeTargets.Delegate) != 0)\r
351                                         return true;\r
352                                 else\r
353                                         return false;\r
354                         } else if (element is Enum) {\r
355                                 if ((targets & AttributeTargets.Enum) != 0)\r
356                                         return true;\r
357                                 else\r
358                                         return false;\r
359                         } else if (element is Event) {\r
360                                 if ((targets & AttributeTargets.Event) != 0)\r
361                                         return true;\r
362                                 else\r
363                                         return false;\r
364                         } else if (element is Field) {\r
365                                 if ((targets & AttributeTargets.Field) != 0)\r
366                                         return true;\r
367                                 else\r
368                                         return false;\r
369                         } else if (element is Interface) {\r
370                                 if ((targets & AttributeTargets.Interface) != 0)\r
371                                         return true;\r
372                                 else\r
373                                         return false;\r
374                         } else if (element is Method || element is Operator) {\r
375                                 if ((targets & AttributeTargets.Method) != 0)\r
376                                         return true;\r
377                                 else\r
378                                         return false;\r
379                         } else if (element is ParameterBuilder) {\r
380                                 if ((targets & AttributeTargets.Parameter) != 0)\r
381                                         return true;\r
382                                 else\r
383                                         return false;\r
384                         } else if (element is Property) {\r
385                                 if ((targets & AttributeTargets.Property) != 0)\r
386                                         return true;\r
387                                 else\r
388                                         return false;\r
389                         } else if (element is AssemblyBuilder){\r
390                                 if ((targets & AttributeTargets.Assembly) != 0)\r
391                                         return true;\r
392                                 else\r
393                                         return false;\r
394                         }\r
395 \r
396                         return false;\r
397                 }\r
398 \r
399                 public static void ApplyAttributes (EmitContext ec, object builder, object kind,\r
400                                                     Attributes opt_attrs, Location loc)\r
401                 {\r
402                         if (opt_attrs == null)\r
403                                 return;\r
404 \r
405                         if (opt_attrs.AttributeSections == null)\r
406                                 return;\r
407 \r
408                         foreach (AttributeSection asec in opt_attrs.AttributeSections) {\r
409 \r
410                                 if (asec.Attributes == null)\r
411                                         continue;\r
412 \r
413                                 foreach (Attribute a in asec.Attributes) {\r
414                                         CustomAttributeBuilder cb = a.Resolve (ec);\r
415                                         if (cb == null)\r
416                                                 continue;\r
417 \r
418                                         if (!(kind is TypeContainer))\r
419                                                 if (!CheckAttribute (a, kind)) {\r
420                                                         error592 (a, loc);\r
421                                                         return;\r
422                                                 }\r
423 \r
424                                         \r
425                                         if (kind is Method || kind is Operator) {\r
426                                                 if (a.Type == TypeManager.methodimpl_attr_type) {\r
427                                                         if (a.ImplOptions == MethodImplOptions.InternalCall)\r
428                                                                 ((MethodBuilder) builder).SetImplementationFlags (\r
429                                                                            MethodImplAttributes.InternalCall |\r
430                                                                            MethodImplAttributes.Runtime);\r
431                                                 } else if (a.Type != TypeManager.dllimport_type)\r
432                                                         ((MethodBuilder) builder).SetCustomAttribute (cb);\r
433                                         } else if (kind is Constructor) {\r
434                                                 ((ConstructorBuilder) builder).SetCustomAttribute (cb);\r
435                                         } else if (kind is Field) {\r
436                                                 ((FieldBuilder) builder).SetCustomAttribute (cb);\r
437                                         } else if (kind is Property || kind is Indexer) {\r
438                                                 ((PropertyBuilder) builder).SetCustomAttribute (cb);\r
439                                         } else if (kind is Event) {\r
440                                                 ((EventBuilder) builder).SetCustomAttribute (cb);\r
441                                         } else if (kind is ParameterBuilder){\r
442                                                 ((ParameterBuilder) builder).SetCustomAttribute (cb);\r
443                                         } else if (kind is Enum) {\r
444                                                 ((TypeBuilder) builder).SetCustomAttribute (cb); \r
445 \r
446                                         } else if (kind is TypeContainer) {\r
447 \r
448                                                 TypeContainer tc = (TypeContainer) kind;\r
449                                                 \r
450                                                 if (a.UsageAttr) {\r
451                                                         tc.Targets = a.Targets;\r
452                                                         tc.AllowMultiple = a.AllowMultiple;\r
453                                                         tc.Inherited = a.Inherited;\r
454                                                         \r
455                                                         RootContext.TypeManager.RegisterAttrType (\r
456                                                                                  (TypeBuilder) builder, tc);\r
457 \r
458                                                 } else if (a.Type == TypeManager.default_member_type) {\r
459                                                         if (tc.Indexers != null) {\r
460                                                                 Report.Error (646, loc,\r
461                                                                       "Cannot specify the DefaultMember attribute on " +\r
462                                                                       " a type containing an indexer");\r
463                                                                 return;\r
464                                                         }\r
465 \r
466                                                 } else {\r
467                                                         if (!CheckAttribute (a, kind)) {\r
468                                                                 error592 (a, loc);\r
469                                                                 return;\r
470                                                         }\r
471                                                 }\r
472                                                 \r
473                                                 ((TypeBuilder) builder).SetCustomAttribute (cb);\r
474                                                 \r
475                                         } else if (kind is AssemblyBuilder){\r
476                                                 ((AssemblyBuilder) builder).SetCustomAttribute (cb);\r
477                                         } else if (kind is ModuleBuilder) {\r
478                                                 ((ModuleBuilder) builder).SetCustomAttribute (cb);\r
479                                         } else\r
480                                                 throw new Exception ("Unknown kind: " + kind);\r
481                                 }\r
482                         }\r
483                 }\r
484 \r
485                 public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name,\r
486                                                           MethodAttributes flags, Type ret_type, Type [] param_types)\r
487                 {\r
488                         //\r
489                         // We extract from the attribute the information we need \r
490                         //\r
491 \r
492                         if (Arguments == null) {\r
493                                 Console.WriteLine ("Internal error : this is not supposed to happen !");\r
494                                 return null;\r
495                         }\r
496 \r
497                         string attr_name = Name;\r
498 \r
499                         if (Name.IndexOf ("Attribute") == -1)\r
500                                 attr_name = Name + "Attribute";\r
501                         else if (Name.LastIndexOf ("Attribute") == 0)\r
502                                 attr_name = Name + "Attribute";\r
503                         \r
504                         Type = RootContext.LookupType (ec.TypeContainer, attr_name, false, Location);\r
505 \r
506                         if (Type == null) {\r
507                                 Report.Error (246, Location, "Could not find attribute '" + Name + "' (are you" +\r
508                                               " missing a using directive or an assembly reference ?)");\r
509                                 return null;\r
510                         }\r
511                         \r
512                         ArrayList named_args = new ArrayList ();\r
513                         \r
514                         ArrayList pos_args = (ArrayList) Arguments [0];\r
515                         if (Arguments.Count > 1)\r
516                                 named_args = (ArrayList) Arguments [1];\r
517                         \r
518 \r
519                         string dll_name = null;\r
520                         \r
521                         Argument tmp = (Argument) pos_args [0];\r
522 \r
523                         if (!tmp.Resolve (ec, Location))\r
524                                 return null;\r
525                         \r
526                         if (tmp.Expr is Constant)\r
527                                 dll_name = (string) ((Constant) tmp.Expr).GetValue ();\r
528                         else { \r
529                                 error182 ();\r
530                                 return null;\r
531                         }\r
532 \r
533                         // Now we process the named arguments\r
534                         CallingConvention cc = CallingConvention.Winapi;\r
535                         CharSet charset = CharSet.Ansi;\r
536                         bool preserve_sig = true;\r
537                         bool exact_spelling = false;\r
538                         bool set_last_err = false;\r
539                         string entry_point = null;\r
540 \r
541                         for (int i = 0; i < named_args.Count; i++) {\r
542 \r
543                                 DictionaryEntry de = (DictionaryEntry) named_args [i];\r
544 \r
545                                 string member_name = (string) de.Key;\r
546                                 Argument a  = (Argument) de.Value;\r
547 \r
548                                 if (!a.Resolve (ec, Location))\r
549                                         return null;\r
550 \r
551                                 Expression member = Expression.MemberLookup (\r
552                                         ec, Type, member_name, \r
553                                         MemberTypes.Field | MemberTypes.Property,\r
554                                         BindingFlags.Public | BindingFlags.Instance,\r
555                                         Location);\r
556 \r
557                                 if (member == null || !(member is FieldExpr)) {\r
558                                         error617 (member_name);\r
559                                         return null;\r
560                                 }\r
561 \r
562                                 if (member is FieldExpr) {\r
563                                         FieldExpr fe = (FieldExpr) member;\r
564                                         FieldInfo fi = fe.FieldInfo;\r
565 \r
566                                         if (fi.IsInitOnly) {\r
567                                                 error617 (member_name);\r
568                                                 return null;\r
569                                         }\r
570 \r
571                                         if (a.Expr is Constant) {\r
572                                                 Constant c = (Constant) a.Expr;\r
573                                                 \r
574                                                 if (member_name == "CallingConvention")\r
575                                                         cc = (CallingConvention) c.GetValue ();\r
576                                                 else if (member_name == "CharSet")\r
577                                                         charset = (CharSet) c.GetValue ();\r
578                                                 else if (member_name == "EntryPoint")\r
579                                                         entry_point = (string) c.GetValue ();\r
580                                                 else if (member_name == "SetLastError")\r
581                                                         set_last_err = (bool) c.GetValue ();\r
582                                                 else if (member_name == "ExactSpelling")\r
583                                                         exact_spelling = (bool) c.GetValue ();\r
584                                                 else if (member_name == "PreserveSig")\r
585                                                         preserve_sig = (bool) c.GetValue ();\r
586                                         } else { \r
587                                                 error182 ();\r
588                                                 return null;\r
589                                         }\r
590                                         \r
591                                 }\r
592                         }\r
593 \r
594                         MethodBuilder mb = builder.DefinePInvokeMethod (\r
595                                 name, dll_name, flags | MethodAttributes.HideBySig,\r
596                                 CallingConventions.Standard,\r
597                                 ret_type,\r
598                                 param_types,\r
599                                 cc,\r
600                                 charset);\r
601 \r
602                         if (preserve_sig)\r
603                                 mb.SetImplementationFlags (MethodImplAttributes.PreserveSig);\r
604                         \r
605                         return mb;\r
606                 }\r
607                 \r
608         }\r
609         \r
610         public class AttributeSection {\r
611                 \r
612                 public readonly string    Target;\r
613                 public readonly ArrayList Attributes;\r
614                 \r
615                 public AttributeSection (string target, ArrayList attrs)\r
616                 {\r
617                         Target = target;\r
618                         Attributes = attrs;\r
619                 }\r
620                 \r
621         }\r
622 \r
623         public class Attributes {\r
624                 public ArrayList AttributeSections;\r
625                 public Location Location;\r
626 \r
627                 public Attributes (AttributeSection a, Location loc)\r
628                 {\r
629                         AttributeSections = new ArrayList ();\r
630                         AttributeSections.Add (a);\r
631 \r
632                 }\r
633 \r
634                 public void AddAttribute (AttributeSection a)\r
635                 {\r
636                         if (a != null)\r
637                                 AttributeSections.Add (a);\r
638                 }\r
639         }\r
640 }\r