2002-01-24 Miguel de Icaza <miguel@ximian.com>
[mono.git] / mcs / mcs / attribute.cs
1 //\r
2 // attribute.cs: Attribute Handler\r
3 //\r
4 // Author: Ravi Pratap (ravi@ximian.com)\r
5 //\r
6 // Licensed under the terms of the GNU GPL\r
7 //\r
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)\r
9 //\r
10 //\r
11 \r
12 using System;\r
13 using System.Collections;\r
14 using System.Reflection;\r
15 using System.Reflection.Emit;\r
16 using System.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) {\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                         } \r
390 \r
391                         return false;\r
392                 }\r
393 \r
394                 public static void ApplyAttributes (EmitContext ec, object builder, object kind,\r
395                                                     Attributes opt_attrs, Location loc)\r
396                 {\r
397                         if (opt_attrs == null)\r
398                                 return;\r
399 \r
400                         if (opt_attrs.AttributeSections == null)\r
401                                 return;\r
402 \r
403                         foreach (AttributeSection asec in opt_attrs.AttributeSections) {\r
404 \r
405                                 if (asec.Attributes == null)\r
406                                         continue;\r
407 \r
408                                 foreach (Attribute a in asec.Attributes) {\r
409                                         CustomAttributeBuilder cb = a.Resolve (ec);\r
410                                         if (cb == null)\r
411                                                 continue;\r
412 \r
413                                         if (!(kind is TypeContainer))\r
414                                                 if (!CheckAttribute (a, kind)) {\r
415                                                         Console.WriteLine ("Kind is: " + kind);\r
416                                                         error592 (a, loc);\r
417                                                         return;\r
418                                                 }\r
419 \r
420                                         \r
421                                         if (kind is Method) {\r
422                                                 if (a.Type == TypeManager.methodimpl_attr_type) {\r
423                                                         if (a.ImplOptions == MethodImplOptions.InternalCall)\r
424                                                                 ((MethodBuilder) builder).SetImplementationFlags (\r
425                                                                            MethodImplAttributes.InternalCall |\r
426                                                                            MethodImplAttributes.Runtime);\r
427                                                 } else if (a.Type != TypeManager.dllimport_type)\r
428                                                         ((MethodBuilder) builder).SetCustomAttribute (cb);\r
429                                                 \r
430                                         } else if (kind is Constructor) {\r
431                                                 ((ConstructorBuilder) builder).SetCustomAttribute (cb);\r
432                                         } else if (kind is Field) {\r
433                                                 ((FieldBuilder) builder).SetCustomAttribute (cb);\r
434                                         } else if (kind is Property || kind is Indexer) {\r
435                                                 ((PropertyBuilder) builder).SetCustomAttribute (cb);\r
436                                         } else if (kind is Event) {\r
437                                                 ((EventBuilder) builder).SetCustomAttribute (cb);\r
438                                         } else if (kind is ParameterBuilder){\r
439                                                 ((ParameterBuilder) builder).SetCustomAttribute (cb);\r
440                                         } else if (kind is Operator) {\r
441                                                 ((MethodBuilder) builder).SetCustomAttribute (cb);\r
442                                         } else if (kind is Enum) {\r
443                                                 ((TypeBuilder) builder).SetCustomAttribute (cb); \r
444 \r
445                                         } else if (kind is TypeContainer) {\r
446 \r
447                                                 TypeContainer tc = (TypeContainer) kind;\r
448                                                 \r
449                                                 if (a.UsageAttr) {\r
450                                                         tc.Targets = a.Targets;\r
451                                                         tc.AllowMultiple = a.AllowMultiple;\r
452                                                         tc.Inherited = a.Inherited;\r
453                                                         \r
454                                                         RootContext.TypeManager.RegisterAttrType (\r
455                                                                                  (TypeBuilder) builder, tc);\r
456 \r
457                                                 } else if (a.Type == TypeManager.default_member_type) {\r
458                                                         if (tc.Indexers != null) {\r
459                                                                 Report.Error (646, loc,\r
460                                                                       "Cannot specify the DefaultMember attribute on " +\r
461                                                                       " a type containing an indexer");\r
462                                                                 return;\r
463                                                         }\r
464 \r
465                                                 } else {\r
466                                                         if (!CheckAttribute (a, kind)) {\r
467                                                                 error592 (a, loc);\r
468                                                                 return;\r
469                                                         }\r
470                                                 }\r
471                                                 \r
472                                                 ((TypeBuilder) builder).SetCustomAttribute (cb);\r
473                                                 \r
474                                         }\r
475                                 }\r
476                         }\r
477                 }\r
478 \r
479                 public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name,\r
480                                                           MethodAttributes flags, Type ret_type, Type [] param_types)\r
481                 {\r
482                         //\r
483                         // We extract from the attribute the information we need \r
484                         //\r
485 \r
486                         if (Arguments == null) {\r
487                                 Console.WriteLine ("Internal error : this is not supposed to happen !");\r
488                                 return null;\r
489                         }\r
490 \r
491                         string attr_name = Name;\r
492 \r
493                         if (Name.IndexOf ("Attribute") == -1)\r
494                                 attr_name = Name + "Attribute";\r
495                         else if (Name.LastIndexOf ("Attribute") == 0)\r
496                                 attr_name = Name + "Attribute";\r
497                         \r
498                         Type = RootContext.LookupType (ec.TypeContainer, attr_name, false, Location);\r
499 \r
500                         if (Type == null) {\r
501                                 Report.Error (246, Location, "Could not find attribute '" + Name + "' (are you" +\r
502                                               " missing a using directive or an assembly reference ?)");\r
503                                 return null;\r
504                         }\r
505                         \r
506                         ArrayList named_args = new ArrayList ();\r
507                         \r
508                         ArrayList pos_args = (ArrayList) Arguments [0];\r
509                         if (Arguments.Count > 1)\r
510                                 named_args = (ArrayList) Arguments [1];\r
511                         \r
512 \r
513                         string dll_name = null;\r
514                         \r
515                         Argument tmp = (Argument) pos_args [0];\r
516 \r
517                         if (!tmp.Resolve (ec, Location))\r
518                                 return null;\r
519                         \r
520                         if (tmp.Expr is Constant)\r
521                                 dll_name = (string) ((Constant) tmp.Expr).GetValue ();\r
522                         else { \r
523                                 error182 ();\r
524                                 return null;\r
525                         }\r
526 \r
527                         // Now we process the named arguments\r
528                         CallingConvention cc = CallingConvention.Winapi;\r
529                         CharSet charset = CharSet.Ansi;\r
530                         bool preserve_sig = true;\r
531                         bool exact_spelling = false;\r
532                         bool set_last_err = false;\r
533                         string entry_point = null;\r
534 \r
535                         for (int i = 0; i < named_args.Count; i++) {\r
536 \r
537                                 DictionaryEntry de = (DictionaryEntry) named_args [i];\r
538 \r
539                                 string member_name = (string) de.Key;\r
540                                 Argument a  = (Argument) de.Value;\r
541 \r
542                                 if (!a.Resolve (ec, Location))\r
543                                         return null;\r
544 \r
545                                 Expression member = Expression.MemberLookup (\r
546                                         ec, Type, member_name, \r
547                                         MemberTypes.Field | MemberTypes.Property,\r
548                                         BindingFlags.Public | BindingFlags.Instance,\r
549                                         Location);\r
550 \r
551                                 if (member == null || !(member is FieldExpr)) {\r
552                                         error617 (member_name);\r
553                                         return null;\r
554                                 }\r
555 \r
556                                 if (member is FieldExpr) {\r
557                                         FieldExpr fe = (FieldExpr) member;\r
558                                         FieldInfo fi = fe.FieldInfo;\r
559 \r
560                                         if (fi.IsInitOnly) {\r
561                                                 error617 (member_name);\r
562                                                 return null;\r
563                                         }\r
564 \r
565                                         if (a.Expr is Constant) {\r
566                                                 Constant c = (Constant) a.Expr;\r
567                                                 \r
568                                                 if (member_name == "CallingConvention")\r
569                                                         cc = (CallingConvention) c.GetValue ();\r
570                                                 else if (member_name == "CharSet")\r
571                                                         charset = (CharSet) c.GetValue ();\r
572                                                 else if (member_name == "EntryPoint")\r
573                                                         entry_point = (string) c.GetValue ();\r
574                                                 else if (member_name == "SetLastError")\r
575                                                         set_last_err = (bool) c.GetValue ();\r
576                                                 else if (member_name == "ExactSpelling")\r
577                                                         exact_spelling = (bool) c.GetValue ();\r
578                                                 else if (member_name == "PreserveSig")\r
579                                                         preserve_sig = (bool) c.GetValue ();\r
580                                         } else { \r
581                                                 error182 ();\r
582                                                 return null;\r
583                                         }\r
584                                         \r
585                                 }\r
586                         }\r
587 \r
588                         MethodBuilder mb = builder.DefinePInvokeMethod (\r
589                                 name, dll_name, flags | MethodAttributes.HideBySig,\r
590                                 CallingConventions.Standard,\r
591                                 ret_type,\r
592                                 param_types,\r
593                                 cc,\r
594                                 charset);\r
595 \r
596                         if (preserve_sig)\r
597                                 mb.SetImplementationFlags (MethodImplAttributes.PreserveSig);\r
598                         \r
599                         return mb;\r
600                 }\r
601                 \r
602         }\r
603         \r
604         public class AttributeSection {\r
605                 \r
606                 public readonly string    Target;\r
607                 public readonly ArrayList Attributes;\r
608                 \r
609                 public AttributeSection (string target, ArrayList attrs)\r
610                 {\r
611                         Target = target;\r
612                         Attributes = attrs;\r
613                 }\r
614                 \r
615         }\r
616 \r
617         public class Attributes {\r
618 \r
619                 public ArrayList AttributeSections;\r
620 \r
621                 public Attributes (AttributeSection a)\r
622                 {\r
623                         AttributeSections = new ArrayList ();\r
624                         AttributeSections.Add (a);\r
625 \r
626                 }\r
627 \r
628                 public void AddAttribute (AttributeSection a)\r
629                 {\r
630                         if (a != null)\r
631                                 AttributeSections.Add (a);\r
632                 }\r
633         }\r
634 }\r