Whitespace changes to make this more similar to the mcs version.
[mono.git] / mcs / gmcs / attribute.cs
1 //
2 // attribute.cs: Attribute Handler
3 //
4 // Author: Ravi Pratap (ravi@ximian.com)
5 //
6 // Licensed under the terms of the GNU GPL
7 //
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)
9 //
10 //
11
12 using System;
13 using System.Diagnostics;
14 using System.Collections;
15 using System.Collections.Specialized;
16 using System.Reflection;
17 using System.Reflection.Emit;
18 using System.Runtime.InteropServices;
19 using System.Runtime.CompilerServices;
20 using System.Text;
21
22 namespace Mono.CSharp {
23
24         /// <summary>
25         ///   Base class for objects that can have Attributes applied to them.
26         /// </summary>
27         public abstract class Attributable {
28                 /// <summary>
29                 ///   Attributes for this type
30                 /// </summary>
31                 Attributes attributes;
32
33                 public Attributable(Attributes attrs)
34                 {
35                         attributes = attrs;
36                         if (attributes != null)
37                                 attributes.CheckTargets (ValidAttributeTargets);
38                 }
39
40                 public Attributes OptAttributes 
41                 {
42                         get {
43                                 return attributes;
44                         }
45                         set {
46                                 attributes = value;
47                         }
48                 }
49
50                 /// <summary>
51                 /// Use member-specific procedure to apply attribute @a in @cb to the entity being built in @builder
52                 /// </summary>
53                 public abstract void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb);
54
55                 /// <summary>
56                 /// Returns combination of enabled AttributeTargets
57                 /// </summary>
58                 public abstract AttributeTargets AttributeTargets { get; }
59
60                 public abstract bool IsClsCompliaceRequired (DeclSpace ds);
61
62                 /// <summary>
63                 /// Gets list of valid attribute targets for explicit target declaration
64                 /// </summary>
65                 protected abstract string[] ValidAttributeTargets { get; }
66         };
67
68         public class Attribute {
69                 public readonly string Target;
70                 public readonly string    Name;
71                 public readonly ArrayList Arguments;
72
73                 public readonly Location Location;
74
75                 public Type Type;
76                 
77                 // Is non-null if type is AttributeUsageAttribute
78                 AttributeUsageAttribute usage_attribute;
79
80                 public AttributeUsageAttribute UsageAttribute {
81                         get {
82                                 return usage_attribute;
83                         }
84                 }
85                 
86                 MethodImplOptions ImplOptions;
87                 UnmanagedType     UnmanagedType;
88                 CustomAttributeBuilder cb;
89         
90                 // non-null if named args present after Resolve () is called
91                 PropertyInfo [] prop_info_arr;
92                 FieldInfo [] field_info_arr;
93                 object [] field_values_arr;
94                 object [] prop_values_arr;
95                 object [] pos_values;
96
97                 static PtrHashtable usage_attr_cache = new PtrHashtable ();
98                 
99                 public Attribute (string target, string name, ArrayList args, Location loc)
100                 {
101                         Name = name;
102                         Arguments = args;
103                         Location = loc;
104                         Target = target;
105                 }
106
107                 void Error_InvalidNamedArgument (string name)
108                 {
109                         Report.Error (617, Location, "'" + name + "' is not a valid named attribute " +
110                                       "argument. Named attribute arguments must be fields which are not " +
111                                       "readonly, static or const, or properties with a set accessor which "+
112                                       "are not static.");
113                 }
114
115                 static void Error_AttributeArgumentNotValid (Location loc)
116                 {
117                         Report.Error (182, loc,
118                                       "An attribute argument must be a constant expression, typeof " +
119                                       "expression or array creation expression");
120                 }
121
122                 void Error_AttributeConstructorMismatch ()
123                 {
124                         Report.Error (-6, Location,
125                                       "Could not find a constructor for this argument list.");
126                 }
127
128                 static void Error_TypeParameterInAttribute (Location loc)
129                 {
130                         Report.Error (
131                                 -202, loc, "Can not use a type parameter in an attribute");
132                 }
133
134                 /// <summary>
135                 ///   Tries to resolve the type of the attribute. Flags an error if it can't, and complain is true.
136                 /// </summary>
137                 protected virtual Type CheckAttributeType (EmitContext ec, bool complain)
138                 {
139                         TypeExpr t1 = RootContext.LookupType (ec.DeclSpace, Name, true, Location);
140                         // FIXME: Shouldn't do this for quoted attributes: [@A]
141                         TypeExpr t2 = RootContext.LookupType (ec.DeclSpace, Name + "Attribute", true, Location);
142
143                         String err0616 = null;
144                         if (t1 != null && ! t1.IsAttribute) {
145                                 t1 = null;
146                                 err0616 = "'" + Name + "': is not an attribute class";
147                         }
148                         if (t2 != null && ! t2.IsAttribute) {
149                                 t2 = null;
150                                 err0616 = (err0616 != null) 
151                                         ? "Neither '" + Name + "' nor '" + Name + "Attribute' is an attribute class"
152                                         : "'" + Name + "Attribute': is not an attribute class";
153                         }
154
155                         if (t1 != null && t2 != null) {
156                                 Report.Error(1614, Location, "'" + Name + "': is ambiguous; " 
157                                              + " use either '@" + Name + "' or '" + Name + "Attribute'");
158                                 return null;
159                         }
160                         if (t1 != null)
161                                 return t1.ResolveType (ec);
162                         if (t2 != null)
163                                 return t2.ResolveType (ec);
164                         if (err0616 != null) {
165                                 Report.Error (616, Location, err0616);
166                                 return null;
167                         }
168
169                         if (complain)
170                                 Report.Error (246, Location, 
171                                               "Could not find attribute '" + Name 
172                                               + "' (are you missing a using directive or an assembly reference ?)");
173                         return null;
174                 }
175
176                 public Type ResolveType (EmitContext ec, bool complain)
177                 {
178                         if (Type == null)
179                                 Type = CheckAttributeType (ec, complain);
180                         return Type;
181                 }
182
183                 /// <summary>
184                 ///   Validates the guid string
185                 /// </summary>
186                 bool ValidateGuid (string guid)
187                 {
188                         try {
189                                 new Guid (guid);
190                                 return true;
191                         } catch {
192                                 Report.Error (647, Location, "Format of GUID is invalid: " + guid);
193                                 return false;
194                         }
195                 }
196
197                 //
198                 // Given an expression, if the expression is a valid attribute-argument-expression
199                 // returns an object that can be used to encode it, or null on failure.
200                 //
201                 public static bool GetAttributeArgumentExpression (Expression e, Location loc, out object result)
202                 {
203                         if (e is Constant) {
204                                 result = ((Constant) e).GetValue ();
205                                 return true;
206                         } else if (e is TypeOf) {
207                                 result = ((TypeOf) e).TypeArg;
208                                 return true;
209                         } else if (e is ArrayCreation){
210                                 result =  ((ArrayCreation) e).EncodeAsAttribute ();
211                                 if (result != null)
212                                         return true;
213                         } else if (e is EmptyCast) {
214                                 result = e;
215                                 if (((EmptyCast) e).Child is Constant) {
216                                         result = ((Constant) ((EmptyCast)e).Child).GetValue();
217                                 }
218                                 return true;
219                         }
220
221                         result = null;
222                         Error_AttributeArgumentNotValid (loc);
223                         return false;
224                 }
225                 
226                 public CustomAttributeBuilder Resolve (EmitContext ec)
227                 {
228                         Type oldType = Type;
229                         
230                         // Sanity check.
231                         Type = CheckAttributeType (ec, true);
232                         if (oldType == null && Type == null)
233                                 return null;
234                         if (oldType != null && oldType != Type) {
235                                 Report.Error (-6, Location,
236                                               "Attribute {0} resolved to different types at different times: {1} vs. {2}",
237                                               Name, oldType, Type);
238                                 return null;
239                         }
240
241                         bool MethodImplAttr = false;
242                         bool MarshalAsAttr = false;
243                         bool GuidAttr = false;
244                         bool usage_attr = false;
245
246                         bool DoCompares = true;
247
248                         //
249                         // If we are a certain special attribute, we
250                         // set the information accordingly
251                         //
252                         
253                         if (Type == TypeManager.attribute_usage_type)
254                                 usage_attr = true;
255                         else if (Type == TypeManager.methodimpl_attr_type)
256                                 MethodImplAttr = true;
257                         else if (Type == TypeManager.marshal_as_attr_type)
258                                 MarshalAsAttr = true;
259                         else if (Type == TypeManager.guid_attr_type)
260                                 GuidAttr = true;
261                         else
262                                 DoCompares = false;
263
264                         // Now we extract the positional and named arguments
265                         
266                         ArrayList pos_args = new ArrayList ();
267                         ArrayList named_args = new ArrayList ();
268                         int pos_arg_count = 0;
269                         
270                         if (Arguments != null) {
271                                 pos_args = (ArrayList) Arguments [0];
272                                 if (pos_args != null)
273                                         pos_arg_count = pos_args.Count;
274                                 if (Arguments.Count > 1)
275                                         named_args = (ArrayList) Arguments [1];
276                         }
277
278                         pos_values = new object [pos_arg_count];
279
280                         //
281                         // First process positional arguments 
282                         //
283
284                         int i;
285                         for (i = 0; i < pos_arg_count; i++) {
286                                 Argument a = (Argument) pos_args [i];
287                                 Expression e;
288
289                                 if (!a.Resolve (ec, Location))
290                                         return null;
291
292                                 e = a.Expr;
293
294                                 object val;
295                                 if (!GetAttributeArgumentExpression (e, Location, out val))
296                                         return null;
297                                 
298                                 pos_values [i] = val;
299                                 if (DoCompares){
300                                         if (usage_attr)
301                                                 usage_attribute = new AttributeUsageAttribute ((AttributeTargets) pos_values [0]);
302                                         else if (MethodImplAttr)
303                                                 this.ImplOptions = (MethodImplOptions) pos_values [0];
304                                         else if (GuidAttr){
305                                                 //
306                                                 // we will later check the validity of the type
307                                                 //
308                                                 if (pos_values [0] is string){
309                                                         if (!ValidateGuid ((string) pos_values [0]))
310                                                                 return null;
311                                                 }
312                                                 
313                                         } else if (MarshalAsAttr)
314                                                 this.UnmanagedType =
315                                                 (System.Runtime.InteropServices.UnmanagedType) pos_values [0];
316                                 }
317                         }
318
319                         //
320                         // Now process named arguments
321                         //
322
323                         ArrayList field_infos = null;
324                         ArrayList prop_infos  = null;
325                         ArrayList field_values = null;
326                         ArrayList prop_values = null;
327
328                         if (named_args.Count > 0) {
329                                 field_infos = new ArrayList ();
330                                 prop_infos  = new ArrayList ();
331                                 field_values = new ArrayList ();
332                                 prop_values = new ArrayList ();
333                         }
334
335                         Hashtable seen_names = new Hashtable();
336                         
337                         for (i = 0; i < named_args.Count; i++) {
338                                 DictionaryEntry de = (DictionaryEntry) named_args [i];
339                                 string member_name = (string) de.Key;
340                                 Argument a  = (Argument) de.Value;
341                                 Expression e;
342
343                                 if (seen_names.Contains(member_name)) {
344                                         Report.Error(643, Location, "'" + member_name + "' duplicate named attribute argument");
345                                         return null;
346                                 }                               
347                                 seen_names.Add(member_name, 1);
348                                 
349                                 if (!a.Resolve (ec, Location))
350                                         return null;
351
352                                 Expression member = Expression.MemberLookup (
353                                         ec, Type, member_name,
354                                         MemberTypes.Field | MemberTypes.Property,
355                                         BindingFlags.Public | BindingFlags.Instance,
356                                         Location);
357
358                                 if (member == null || !(member is PropertyExpr || member is FieldExpr)) {
359                                         Error_InvalidNamedArgument (member_name);
360                                         return null;
361                                 }
362
363                                 e = a.Expr;
364                                 if (e is TypeParameterExpr){
365                                         Error_TypeParameterInAttribute (Location);
366                                         return null;
367                                 }
368                                         
369                                 if (member is PropertyExpr) {
370                                         PropertyExpr pe = (PropertyExpr) member;
371                                         PropertyInfo pi = pe.PropertyInfo;
372
373                                         if (!pi.CanWrite) {
374                                                 Error_InvalidNamedArgument (member_name);
375                                                 return null;
376                                         }
377
378                                         if (e is Constant) {
379                                                 Constant c;
380                                                 
381                                                 if (e.Type != pi.PropertyType){
382                                                         c = Const.ChangeType (Location, (Constant) e, pi.PropertyType);
383                                                         if (c == null)
384                                                                 return null;
385                                                 } else
386                                                         c = (Constant) e;
387                                                 
388                                                 object o = c.GetValue ();
389                                                 prop_values.Add (o);
390                                                 
391                                                 if (usage_attribute != null) {
392                                                         if (member_name == "AllowMultiple")
393                                                                 usage_attribute.AllowMultiple = (bool) o;
394                                                         if (member_name == "Inherited")
395                                                                 usage_attribute.Inherited = (bool) o;
396                                                 }
397                                                 
398                                         } else if (e is TypeOf) {
399                                                 prop_values.Add (((TypeOf) e).TypeArg);
400                                         } else if (e is ArrayCreation) {
401                                                 prop_values.Add (((ArrayCreation) e).EncodeAsAttribute());
402                                         } else {
403                                                 Error_AttributeArgumentNotValid (Location);
404                                                 return null;
405                                         }
406                                         
407                                         prop_infos.Add (pi);
408                                         
409                                 } else if (member is FieldExpr) {
410                                         FieldExpr fe = (FieldExpr) member;
411                                         FieldInfo fi = fe.FieldInfo;
412
413                                         if (fi.IsInitOnly) {
414                                                 Error_InvalidNamedArgument (member_name);
415                                                 return null;
416                                         }
417
418                                         //
419                                         // Handle charset here, and set the TypeAttributes
420                                         
421                                         if (e is Constant){
422                                                 Constant c = (Constant) e;;
423                                                 
424                                                 if (c.Type != fi.FieldType){
425                                                         c = Const.ChangeType (Location, (Constant) e, fi.FieldType);
426                                                         if (c == null)
427                                                                 return null;
428                                                 } 
429                                                 
430                                                 object value = c.GetValue ();
431                                                 field_values.Add (value);
432                                         } else if (e is TypeOf) {
433                                                 field_values.Add (((TypeOf) e).TypeArg);
434                                         } else {
435                                                 Error_AttributeArgumentNotValid (Location);
436                                                 return null;
437                                         }
438                                         
439                                         field_infos.Add (fi);
440                                 }
441                         }
442
443                         Expression mg = Expression.MemberLookup (
444                                 ec, Type, ".ctor", MemberTypes.Constructor,
445                                 BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly,
446                                 Location);
447
448                         if (mg == null) {
449                                 Error_AttributeConstructorMismatch ();
450                                 return null;
451                         }
452
453                         MethodBase constructor = Invocation.OverloadResolve (
454                                 ec, (MethodGroupExpr) mg, pos_args, false, Location);
455
456                         if (constructor == null) {
457                                 Error_AttributeConstructorMismatch ();
458                                 return null;
459                         }
460
461                         //
462                         // Now we perform some checks on the positional args as they
463                         // cannot be null for a constructor which expects a parameter
464                         // of type object
465                         //
466
467                         ParameterData pd = Invocation.GetParameterData (constructor);
468
469                         int group_in_params_array = Int32.MaxValue;
470                         int pc = pd.Count;
471                         if (pc > 0 && pd.ParameterModifier (pc-1) == Parameter.Modifier.PARAMS)
472                                 group_in_params_array = pc-1;
473
474                         for (int j = 0; j < pos_arg_count; ++j) {
475                                 Argument a = (Argument) pos_args [j];
476                                 
477                                 if (a.Expr is NullLiteral && pd.ParameterType (j) == TypeManager.object_type) {
478                                         Error_AttributeArgumentNotValid (Location);
479                                         return null;
480                                 }
481
482                                 if (j < group_in_params_array)
483                                         continue;
484                                 
485                                 if (j == group_in_params_array){
486                                         object v = pos_values [j];
487                                         int count = pos_arg_count - j;
488
489                                         object [] array = new object [count];
490                                         pos_values [j] = array;
491                                         array [0] = v;
492                                 } else {
493                                         object [] array = (object []) pos_values [group_in_params_array];
494
495                                         array [j - group_in_params_array] = pos_values [j];
496                                 }
497                         }
498
499                         //
500                         // Adjust the size of the pos_values if it had params
501                         //
502                         if (group_in_params_array != Int32.MaxValue){
503                                 int argc = group_in_params_array+1;
504                                 object [] new_pos_values = new object [argc];
505
506                                 for (int p = 0; p < argc; p++)
507                                         new_pos_values [p] = pos_values [p];
508                                 pos_values = new_pos_values;
509                         }
510
511                         try {
512                                 if (named_args.Count > 0) {
513                                         prop_info_arr = new PropertyInfo [prop_infos.Count];
514                                         field_info_arr = new FieldInfo [field_infos.Count];
515                                         field_values_arr = new object [field_values.Count];
516                                         prop_values_arr = new object [prop_values.Count];
517
518                                         field_infos.CopyTo  (field_info_arr, 0);
519                                         field_values.CopyTo (field_values_arr, 0);
520
521                                         prop_values.CopyTo  (prop_values_arr, 0);
522                                         prop_infos.CopyTo   (prop_info_arr, 0);
523
524                                         cb = new CustomAttributeBuilder (
525                                                 (ConstructorInfo) constructor, pos_values,
526                                                 prop_info_arr, prop_values_arr,
527                                                 field_info_arr, field_values_arr);
528                                 }
529                                 else
530                                         cb = new CustomAttributeBuilder (
531                                                 (ConstructorInfo) constructor, pos_values);
532                         } catch (NullReferenceException) {
533                                 // 
534                                 // Don't know what to do here
535                                 //
536                                 Report.Warning (
537                                         -101, Location, "NullReferenceException while trying to create attribute." +
538                                         "Something's wrong!");
539                         } catch (Exception e) {
540                                 //
541                                 // Sample:
542                                 // using System.ComponentModel;
543                                 // [DefaultValue (CollectionChangeAction.Add)]
544                                 // class X { static void Main () {} }
545                                 //
546                                 Report.Warning (
547                                         -23, Location,
548                                         "The compiler can not encode this attribute in .NET due to\n" +
549                                         "\ta bug in the .NET runtime.  Try the Mono runtime.\nThe error was: " + e.Message);
550                         }
551                         
552                         return cb;
553                 }
554
555                 /// <summary>
556                 ///   Get a string containing a list of valid targets for the attribute 'attr'
557                 /// </summary>
558                 static string GetValidTargets (Attribute attr)
559                 {
560                         StringBuilder sb = new StringBuilder ();
561                         AttributeTargets targets = attr.GetAttributeUsage ().ValidOn;
562
563                         if ((targets & AttributeTargets.Assembly) != 0)
564                                 sb.Append ("'assembly' ");
565
566                         if ((targets & AttributeTargets.Class) != 0)
567                                 sb.Append ("'class' ");
568
569                         if ((targets & AttributeTargets.Constructor) != 0)
570                                 sb.Append ("'constructor' ");
571
572                         if ((targets & AttributeTargets.Delegate) != 0)
573                                 sb.Append ("'delegate' ");
574
575                         if ((targets & AttributeTargets.Enum) != 0)
576                                 sb.Append ("'enum' ");
577
578                         if ((targets & AttributeTargets.Event) != 0)
579                                 sb.Append ("'event' ");
580
581                         if ((targets & AttributeTargets.Field) != 0)
582                                 sb.Append ("'field' ");
583
584                         if ((targets & AttributeTargets.Interface) != 0)
585                                 sb.Append ("'interface' ");
586
587                         if ((targets & AttributeTargets.Method) != 0)
588                                 sb.Append ("'method' ");
589
590                         if ((targets & AttributeTargets.Module) != 0)
591                                 sb.Append ("'module' ");
592
593                         if ((targets & AttributeTargets.Parameter) != 0)
594                                 sb.Append ("'parameter' ");
595
596                         if ((targets & AttributeTargets.Property) != 0)
597                                 sb.Append ("'property' ");
598
599                         if ((targets & AttributeTargets.ReturnValue) != 0)
600                                 sb.Append ("'return' ");
601
602                         if ((targets & AttributeTargets.Struct) != 0)
603                                 sb.Append ("'struct' ");
604
605                         return sb.ToString ();
606
607                 }
608
609                 public static void Error_AttributeNotValidForElement (Attribute a, Location loc)
610                 {
611                         Report.Error (
612                                 592, loc, "Attribute '" + a.Name +
613                                 "' is not valid on this declaration type. " +
614                                 "It is valid on " + GetValidTargets (a) + "declarations only.");
615                 }
616
617
618                 /// <summary>
619                 /// Returns AttributeUsage attribute for this type
620                 /// </summary>
621                 public AttributeUsageAttribute GetAttributeUsage ()
622                 {
623                         AttributeUsageAttribute ua = usage_attr_cache [Type] as AttributeUsageAttribute;
624                         if (ua != null)
625                                 return ua;
626
627                         Class attr_class = TypeManager.LookupClass (Type);
628
629                         if (attr_class == null) {
630                                 object[] usage_attr = Type.GetCustomAttributes (TypeManager.attribute_usage_type, true);
631                                 ua = (AttributeUsageAttribute)usage_attr [0];
632                                 usage_attr_cache.Add (Type, ua);
633                                 return ua;
634                         }
635                 
636                         return attr_class.AttributeUsage;
637                 }
638
639
640                 //
641                 // This pulls the condition name out of a Conditional attribute
642                 //
643                 public string Conditional_GetConditionName ()
644                 {
645                         //
646                         // So we have a Conditional, pull the data out.
647                         //
648                         if (Arguments == null || Arguments [0] == null){
649                                 Error_AttributeConstructorMismatch ();
650                                 return null;
651                         }
652
653                         ArrayList pos_args = (ArrayList) Arguments [0];
654                         if (pos_args.Count != 1){
655                                 Error_AttributeConstructorMismatch ();
656                                 return null;
657                         }
658
659                         Argument arg = (Argument) pos_args [0]; 
660                         if (!(arg.Expr is StringConstant)){
661                                 Error_AttributeConstructorMismatch ();
662                                 return null;
663                         }
664
665                         return ((StringConstant) arg.Expr).Value;
666                 }
667
668                 //
669                 // This pulls the obsolete message and error flag out of an Obsolete attribute
670                 //
671                 public string Obsolete_GetObsoleteMessage (out bool is_error)
672                 {
673                         is_error = false;
674                         //
675                         // So we have an Obsolete, pull the data out.
676                         //
677                         if (Arguments == null || Arguments [0] == null)
678                                 return "";
679
680                         ArrayList pos_args = (ArrayList) Arguments [0];
681                         if (pos_args.Count == 0)
682                                 return "";
683                         else if (pos_args.Count > 2){
684                                 Error_AttributeConstructorMismatch ();
685                                 return null;
686                         }
687
688                         Argument arg = (Argument) pos_args [0]; 
689                         if (!(arg.Expr is StringConstant)){
690                                 Error_AttributeConstructorMismatch ();
691                                 return null;
692                         }
693
694                         if (pos_args.Count == 2){
695                                 Argument arg2 = (Argument) pos_args [1];
696                                 if (!(arg2.Expr is BoolConstant)){
697                                         Error_AttributeConstructorMismatch ();
698                                         return null;
699                                 }
700                                 is_error = ((BoolConstant) arg2.Expr).Value;
701                         }
702
703                         return ((StringConstant) arg.Expr).Value;
704                 }
705
706                 public string IndexerName_GetIndexerName (EmitContext ec)
707                 {
708                         if (Arguments == null || Arguments [0] == null){
709                                 Error_AttributeConstructorMismatch ();
710                                 return null;
711                         }
712                         ArrayList pos_args = (ArrayList) Arguments [0];
713                         if (pos_args.Count != 1) {
714                                 Error_AttributeConstructorMismatch ();
715                                 return null;
716                         }
717                         
718                         Argument arg = (Argument) pos_args [0];
719                         if (!arg.Resolve (ec, Location))
720                                 return null;
721                         
722                         StringConstant sc = arg.Expr as StringConstant;
723                         if (sc == null){
724                                 Error_AttributeConstructorMismatch ();
725                                 return null;
726                         }
727                         
728                         return sc.Value;
729                 }
730
731                 /// <summary>
732                 /// Returns value of CLSCompliantAttribute contructor parameter but because the method can be called
733                 /// before ApplyAttribute. We need to resolve the arguments.
734                 /// This situation occurs when class deps is differs from Emit order.  
735                 /// </summary>
736                 public bool GetClsCompliantAttributeValue (DeclSpace ds)
737                 {
738                         if (pos_values == null) {
739                                 EmitContext ec = new EmitContext (ds, ds, Location, null, null, 0, false);
740
741                                 // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args.
742                                 // But because a lot of attribute class code must be rewritten will be better to wait...
743                                 Resolve (ec);
744                         }
745
746                         // Some error occurred
747                         if (pos_values [0] == null)
748                                 return false;
749
750                         return (bool)pos_values [0];
751                 }
752
753                 public object GetPositionalValue (int i)
754                 {
755                         return (pos_values == null) ? null : pos_values[i];
756                 }
757
758                 object GetFieldValue (string name)
759                 {
760                         int i;
761                         if (field_info_arr == null)
762                                 return null;
763                         i = 0;
764                         foreach (FieldInfo fi in field_info_arr) {
765                                 if (fi.Name == name)
766                                         return field_values_arr [i];
767                                 i++;
768                         }
769                         return null;
770                 }
771
772                 public UnmanagedMarshal GetMarshal ()
773                 {
774                         object o = GetFieldValue ("ArraySubType");
775                         UnmanagedType array_sub_type = o == null ? UnmanagedType.I4 : (UnmanagedType) o;
776                         
777                         switch (UnmanagedType) {
778                         case UnmanagedType.CustomMarshaler:
779                                 MethodInfo define_custom = typeof (UnmanagedMarshal).GetMethod ("DefineCustom",
780                                                                        BindingFlags.Static | BindingFlags.Public);
781                                 if (define_custom == null)
782                                         return null;
783                                 
784                                 object [] args = new object [4];
785                                 args [0] = GetFieldValue ("MarshalTypeRef");
786                                 args [1] = GetFieldValue ("MarshalCookie");
787                                 args [2] = GetFieldValue ("MarshalType");
788                                 args [3] = Guid.Empty;
789                                 return (UnmanagedMarshal) define_custom.Invoke (null, args);
790                                 
791                         case UnmanagedType.LPArray:                             
792                                 return UnmanagedMarshal.DefineLPArray (array_sub_type);
793                         
794                         case UnmanagedType.SafeArray:
795                                 return UnmanagedMarshal.DefineSafeArray (array_sub_type);
796                         
797                         case UnmanagedType.ByValArray:
798                                 return UnmanagedMarshal.DefineByValArray ((int) GetFieldValue ("SizeConst"));
799                         
800                         case UnmanagedType.ByValTStr:
801                                 return UnmanagedMarshal.DefineByValTStr ((int) GetFieldValue ("SizeConst"));
802                         
803                         default:
804                                 return UnmanagedMarshal.DefineUnmanagedMarshal (UnmanagedType);
805                         }
806                 }
807
808                 public bool IsInternalCall
809                 {
810                         get { return ImplOptions == MethodImplOptions.InternalCall; }
811                 }
812
813                 protected virtual bool CanIgnoreInvalidAttribute (Attributable ias)
814                 {
815                         return false;
816                 }
817
818                 /// <summary>
819                 /// Emit attribute for Attributable symbol
820                 /// </summary>
821                 public void Emit (EmitContext ec, Attributable ias, ListDictionary emitted_attr)
822                 {
823                         CustomAttributeBuilder cb = Resolve (ec);
824                         if (cb == null)
825                                 return;
826
827                         AttributeUsageAttribute usage_attr = GetAttributeUsage ();
828                         if ((usage_attr.ValidOn & ias.AttributeTargets) == 0) {
829                                 // The parser applies toplevel attributes both to the assembly and
830                                 // to a top-level class, if any.  So, be silent about them.
831                                 if (! CanIgnoreInvalidAttribute (ias))
832                                         Error_AttributeNotValidForElement (this, Location);
833                                 return;
834                         }
835
836                         ias.ApplyAttributeBuilder (this, cb);
837
838                         // Because default target is null (we save some space). We need to transform it here
839                         // for distinction between "default" and "doesn't exist"
840                         string target = Target == null ? "default" : Target;
841                         string emitted = emitted_attr [Type] as string;
842                         if (emitted == target && !usage_attr.AllowMultiple) {
843                                 Report.Error (579, Location, "Duplicate '" + Name + "' attribute");
844                         }
845
846                         emitted_attr [Type] = target;
847
848                         // Here we are testing attribute arguments for array usage (error 3016)
849                         if (ias.IsClsCompliaceRequired (ec.DeclSpace)) {
850                                 if (Arguments == null)
851                                         return;
852
853                                 ArrayList pos_args = (ArrayList) Arguments [0];
854                                 if (pos_args != null) {
855                                         foreach (Argument arg in pos_args) { 
856                                                 // Type is undefined (was error 246)
857                                                 if (arg.Type == null)
858                                                         return;
859
860                                                 if (arg.Type.IsArray) {
861                                                         Report.Error_T (3016, Location);
862                                                         return;
863                                                 }
864                                         }
865                                 }
866                         
867                                 if (Arguments.Count < 2)
868                                         return;
869                         
870                                 ArrayList named_args = (ArrayList) Arguments [1];
871                                 foreach (DictionaryEntry de in named_args) {
872                                         Argument arg  = (Argument) de.Value;
873
874                                         // Type is undefined (was error 246)
875                                         if (arg.Type == null)
876                                                 return;
877
878                                         if (arg.Type.IsArray) {
879                                                 Report.Error_T (3016, Location);
880                                                 return;
881                                         }
882                                 }
883                         }
884                 }
885
886                 public object GetValue (EmitContext ec, Constant c, Type target)
887                 {
888                         if (Convert.ImplicitConversionExists (ec, c, target))
889                                 return c.GetValue ();
890
891                         Convert.Error_CannotImplicitConversion (Location, c.Type, target);
892                         return null;
893                 }
894                 
895                 public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name,
896                                                           MethodAttributes flags, Type ret_type, Type [] param_types)
897                 {
898                         //
899                         // We extract from the attribute the information we need 
900                         //
901
902                         if (Arguments == null) {
903                                 Console.WriteLine ("Internal error : this is not supposed to happen !");
904                                 return null;
905                         }
906
907                         ResolveType (ec, true);
908                         if (Type == null)
909                                 return null;
910                         
911                         ArrayList named_args = new ArrayList ();
912                         
913                         ArrayList pos_args = (ArrayList) Arguments [0];
914                         if (Arguments.Count > 1)
915                                 named_args = (ArrayList) Arguments [1];
916                         
917
918                         string dll_name = null;
919                         
920                         Argument tmp = (Argument) pos_args [0];
921
922                         if (!tmp.Resolve (ec, Location))
923                                 return null;
924                         
925                         if (tmp.Expr is Constant)
926                                 dll_name = (string) ((Constant) tmp.Expr).GetValue ();
927                         else { 
928                                 Error_AttributeArgumentNotValid (Location);
929                                 return null;
930                         }
931
932                         // Now we process the named arguments
933                         CallingConvention cc = CallingConvention.Winapi;
934                         CharSet charset = CharSet.Ansi;
935                         bool preserve_sig = true;
936 #if FIXME
937                         bool exact_spelling = false;
938 #endif
939                         bool set_last_err = false;
940                         string entry_point = null;
941
942                         for (int i = 0; i < named_args.Count; i++) {
943
944                                 DictionaryEntry de = (DictionaryEntry) named_args [i];
945
946                                 string member_name = (string) de.Key;
947                                 Argument a  = (Argument) de.Value;
948
949                                 if (!a.Resolve (ec, Location))
950                                         return null;
951
952                                 Expression member = Expression.MemberLookup (
953                                         ec, Type, member_name, 
954                                         MemberTypes.Field | MemberTypes.Property,
955                                         BindingFlags.Public | BindingFlags.Instance,
956                                         Location);
957
958                                 if (member == null || !(member is FieldExpr)) {
959                                         Error_InvalidNamedArgument (member_name);
960                                         return null;
961                                 }
962
963                                 if (member is FieldExpr) {
964                                         FieldExpr fe = (FieldExpr) member;
965                                         FieldInfo fi = fe.FieldInfo;
966
967                                         if (fi.IsInitOnly) {
968                                                 Error_InvalidNamedArgument (member_name);
969                                                 return null;
970                                         }
971
972                                         if (a.Expr is Constant) {
973                                                 Constant c = (Constant) a.Expr;
974
975                                                 try {
976                                                         if (member_name == "CallingConvention"){
977                                                                 object val = GetValue (ec, c, typeof (CallingConvention));
978                                                                 if (val == null)
979                                                                         return null;
980                                                                 cc = (CallingConvention) val;
981                                                         } else if (member_name == "CharSet"){
982                                                                 charset = (CharSet) c.GetValue ();
983                                                         } else if (member_name == "EntryPoint")
984                                                                 entry_point = (string) c.GetValue ();
985                                                         else if (member_name == "SetLastError")
986                                                                 set_last_err = (bool) c.GetValue ();
987 #if FIXME
988                                                         else if (member_name == "ExactSpelling")
989                                                                 exact_spelling = (bool) c.GetValue ();
990 #endif
991                                                         else if (member_name == "PreserveSig")
992                                                                 preserve_sig = (bool) c.GetValue ();
993                                                 } catch (InvalidCastException){
994                                                         Error_InvalidNamedArgument (member_name);
995                                                         Error_AttributeArgumentNotValid (Location);
996                                                 }
997                                         } else { 
998                                                 Error_AttributeArgumentNotValid (Location);
999                                                 return null;
1000                                         }
1001                                         
1002                                 }
1003                         }
1004
1005                         if (entry_point == null)
1006                                 entry_point = name;
1007                         if (set_last_err)
1008                                 charset = (CharSet)((int)charset | 0x40);
1009                         
1010                         MethodBuilder mb = builder.DefinePInvokeMethod (
1011                                 name, dll_name, entry_point, flags | MethodAttributes.HideBySig,
1012                                 CallingConventions.Standard,
1013                                 ret_type,
1014                                 param_types,
1015                                 cc,
1016                                 charset);
1017
1018                         if (preserve_sig)
1019                                 mb.SetImplementationFlags (MethodImplAttributes.PreserveSig);
1020                         
1021                         return mb;
1022                 }
1023
1024                 private Expression GetValue () 
1025                 {
1026                         if ((Arguments == null) || (Arguments.Count < 1))
1027                                 return null;
1028                         ArrayList al = (ArrayList) Arguments [0];
1029                         if ((al == null) || (al.Count < 1))
1030                                 return null;
1031                         Argument arg = (Argument) al [0];
1032                         if ((arg == null) || (arg.Expr == null))
1033                                 return null;
1034                         return arg.Expr;
1035                 }
1036
1037                 public string GetString () 
1038                 {
1039                         Expression e = GetValue ();
1040                         if (e is StringLiteral)
1041                                 return (e as StringLiteral).Value;
1042                         return null;
1043                 }
1044
1045                 public bool GetBoolean () 
1046                 {
1047                         Expression e = GetValue ();
1048                         if (e is BoolLiteral)
1049                                 return (e as BoolLiteral).Value;
1050                         return false;
1051                 }
1052         }
1053         
1054
1055         /// <summary>
1056         /// For global attributes (assembly, module) we need special handling.
1057         /// Attributes can be located in the several files
1058         /// </summary>
1059         public class GlobalAttribute: Attribute
1060         {
1061                 public readonly NamespaceEntry ns;
1062
1063                 public GlobalAttribute (TypeContainer container, string target, string name, ArrayList args, Location loc):
1064                         base (target, name, args, loc)
1065                 {
1066                         ns = container.NamespaceEntry;
1067                 }
1068
1069                 protected override Type CheckAttributeType (EmitContext ec, bool complain)
1070                 {
1071                         NamespaceEntry old = ec.DeclSpace.NamespaceEntry;
1072                         if (old == null || old.NS == null || old.NS == Namespace.Root) 
1073                                 ec.DeclSpace.NamespaceEntry = ns;
1074                         return base.CheckAttributeType (ec, complain);
1075                 }
1076
1077                 protected override bool CanIgnoreInvalidAttribute (Attributable ias)
1078                 {
1079                         // Ignore error if this attribute is shared between the Assembly
1080                         // and a top-level class.  The parser couldn't figure out which entity
1081                         // this attribute belongs to.  If this attribute is erroneous, it should
1082                         // be caught when it is processed by the top-level class.
1083
1084                         return (Target == null && ias is CommonAssemblyModulClass);
1085                 }
1086         }
1087
1088         public class Attributes {
1089                 public ArrayList Attrs;
1090
1091                 public Attributes (Attribute a)
1092                 {
1093                         Attrs = new ArrayList ();
1094                         Attrs.Add (a);
1095                 }
1096
1097                 public Attributes (ArrayList attrs)
1098                 {
1099                         Attrs = attrs;
1100                 }
1101
1102                 public void AddAttributes (ArrayList attrs)
1103                 {
1104                         Attrs.AddRange (attrs);
1105                 }
1106
1107                 /// <summary>
1108                 /// Checks whether attribute target is valid for the current element
1109                 /// </summary>
1110                 public void CheckTargets (string[] possible_targets)
1111                 {
1112                         foreach (Attribute a in Attrs) {
1113                                 if (a.Target == null)
1114                                         continue;
1115
1116                                 if (((IList) possible_targets).Contains (a.Target))
1117                                         continue;
1118
1119                                 StringBuilder sb = new StringBuilder ();
1120                                 foreach (string s in possible_targets) {
1121                                         sb.Append (s);
1122                                         sb.Append (", ");
1123                                 }
1124                                 sb.Remove (sb.Length - 2, 2);
1125                                 Report.Error_T (657, a.Location, a.Target, sb.ToString ());
1126                         }
1127                 }
1128
1129                 private Attribute Search (Type t, EmitContext ec, bool complain)
1130                 {
1131                         foreach (Attribute a in Attrs) {
1132                                 if (a.ResolveType (ec, false) == t)
1133                                         return a;
1134                         }
1135                         return null;
1136                 }
1137
1138                 public Attribute Search (Type t, EmitContext ec)
1139                 {
1140                         return Search (t, ec, true);
1141                 }
1142
1143                 public void Emit (EmitContext ec, Attributable ias)
1144                 {
1145                         ListDictionary ld = new ListDictionary ();
1146
1147                         foreach (Attribute a in Attrs)
1148                                 a.Emit (ec, ias, ld);
1149                 }
1150
1151                 public bool Contains (Type t, EmitContext ec)
1152                 {
1153                         return Search (t, ec) != null;
1154                 }
1155
1156                 public Attribute GetClsCompliantAttribute (EmitContext ec)
1157                 {
1158                         return Search (TypeManager.cls_compliant_attribute_type, ec, false);
1159                 }
1160
1161                 /// <summary>
1162                 /// Pulls the IndexerName attribute from an Indexer if it exists.
1163                 /// </summary>
1164                 public string ScanForIndexerName (EmitContext ec)
1165                 {
1166                         Attribute a = Search (TypeManager.indexer_name_type, ec);
1167                         if (a == null)
1168                                 return null;
1169
1170                         // Remove the attribute from the list
1171                         //TODO: It is very close to hack and it can crash here
1172                         Attrs.Remove (a);
1173
1174                         return a.IndexerName_GetIndexerName (ec);
1175                 }
1176
1177         }
1178
1179         /// <summary>
1180         /// Helper class for attribute verification routine.
1181         /// </summary>
1182         sealed class AttributeTester
1183         {
1184                 static PtrHashtable analyzed_types = new PtrHashtable ();
1185
1186                 private AttributeTester ()
1187                 {
1188                 }
1189
1190                 /// <summary>
1191                 /// Returns true if parameters of two compared methods are CLS-Compliant.
1192                 /// It tests differing only in ref or out, or in array rank.
1193                 /// </summary>
1194                 public static bool AreOverloadedMethodParamsClsCompliant (Type[] types_a, Type[] types_b) 
1195                 {
1196                         if (types_a == null || types_b == null)
1197                                 return true;
1198
1199                         if (types_a.Length != types_b.Length)
1200                                 return true;
1201
1202                         for (int i = 0; i < types_b.Length; ++i) {
1203                                 Type aType = types_a [i];
1204                                 Type bType = types_b [i];
1205
1206                                 if (aType.IsArray && bType.IsArray && aType.GetArrayRank () != bType.GetArrayRank () && aType.GetElementType () == bType.GetElementType ()) {
1207                                         return false;
1208                                 }
1209
1210                                 Type aBaseType = aType;
1211                                 bool is_either_ref_or_out = false;
1212
1213                                 if (aType.IsByRef || aType.IsPointer) {
1214                                         aBaseType = aType.GetElementType ();
1215                                         is_either_ref_or_out = true;
1216                                 }
1217
1218                                 Type bBaseType = bType;
1219                                 if (bType.IsByRef || bType.IsPointer) 
1220                                 {
1221                                         bBaseType = bType.GetElementType ();
1222                                         is_either_ref_or_out = !is_either_ref_or_out;
1223                                 }
1224
1225                                 if (aBaseType != bBaseType)
1226                                         continue;
1227
1228                                 if (is_either_ref_or_out)
1229                                         return false;
1230                         }
1231                         return true;
1232                 }
1233
1234                 /// <summary>
1235                 /// Goes through all parameters and test if they are CLS-Compliant.
1236                 /// </summary>
1237                 public static bool AreParametersCompliant (Parameter[] fixedParameters, Location loc)
1238                 {
1239                         if (fixedParameters == null)
1240                                 return true;
1241
1242                         foreach (Parameter arg in fixedParameters) {
1243                                 if (!AttributeTester.IsClsCompliant (arg.ParameterType)) {
1244                                         Report.Error_T (3001, loc, arg.GetSignatureForError ());
1245                                         return false;
1246                                 }
1247                         }
1248                         return true;
1249                 }
1250
1251
1252                 /// <summary>
1253                 /// This method tests the CLS compliance of external types. It doesn't test type visibility.
1254                 /// </summary>
1255                 public static bool IsClsCompliant (Type type) 
1256                 {
1257                         if (type == null)
1258                                 return true;
1259
1260                         object type_compliance = analyzed_types[type];
1261                         if (type_compliance != null)
1262                                 return type_compliance == TRUE;
1263
1264                         if (type.IsPointer) {
1265                                 analyzed_types.Add (type, null);
1266                                 return false;
1267                         }
1268
1269                         bool result;
1270                         if (type.IsArray || type.IsByRef)       {
1271                                 result = IsClsCompliant (TypeManager.GetElementType (type));
1272                         } else {
1273                                 result = AnalyzeTypeCompliance (type);
1274                         }
1275                         analyzed_types.Add (type, result ? TRUE : FALSE);
1276                         return result;
1277                 }                
1278
1279                 static object TRUE = new object ();
1280                 static object FALSE = new object ();
1281
1282                 /// <summary>
1283                 /// Non-hierarchical CLS Compliance analyzer
1284                 /// </summary>
1285                 public static bool IsComplianceRequired (MemberInfo mi, DeclSpace ds)
1286                 {
1287                         DeclSpace temp_ds = TypeManager.LookupDeclSpace (mi.DeclaringType);
1288
1289                         // Type is external, we can get attribute directly
1290                         if (temp_ds == null) {
1291                                 object[] cls_attribute = mi.GetCustomAttributes (TypeManager.cls_compliant_attribute_type, false);
1292                                 return (cls_attribute.Length == 1 && ((CLSCompliantAttribute)cls_attribute[0]).IsCompliant);
1293                         }
1294
1295                         string tmp_name;
1296                         // Interface doesn't store full name
1297                         if (temp_ds is Interface)
1298                                 tmp_name = mi.Name;
1299                         else
1300                                 tmp_name = String.Concat (temp_ds.Name, ".", mi.Name);
1301
1302                         MemberCore mc = temp_ds.GetDefinition (tmp_name) as MemberCore;
1303                         return mc.IsClsCompliaceRequired (ds);
1304                 }
1305
1306                 public static void VerifyModulesClsCompliance ()
1307                 {
1308                         Module[] modules = TypeManager.Modules;
1309                         if (modules == null)
1310                                 return;
1311
1312                         // The first module is generated assembly
1313                         for (int i = 1; i < modules.Length; ++i) {
1314                                 Module module = modules [i];
1315                                 if (!IsClsCompliant (module)) {
1316                                         Report.Error_T (3013, module.Name);
1317                                         return;
1318                                 }
1319                         }
1320                 }
1321
1322                 static bool IsClsCompliant (ICustomAttributeProvider attribute_provider) 
1323                 {
1324                         object[] CompliantAttribute = attribute_provider.GetCustomAttributes (TypeManager.cls_compliant_attribute_type, false);
1325                         if (CompliantAttribute.Length == 0)
1326                                 return false;
1327
1328                         return ((CLSCompliantAttribute)CompliantAttribute[0]).IsCompliant;
1329                 }
1330
1331                 static bool AnalyzeTypeCompliance (Type type)
1332                 {
1333                         DeclSpace ds = TypeManager.LookupDeclSpace (type);
1334                         if (ds != null) {
1335                                 return ds.IsClsCompliaceRequired (ds.Parent);
1336                         }
1337
1338                         if (type.IsGenericParameter)
1339                                 return false;
1340
1341                         object[] CompliantAttribute = type.GetCustomAttributes (TypeManager.cls_compliant_attribute_type, false);
1342                         if (CompliantAttribute.Length == 0) 
1343                                 return IsClsCompliant (type.Assembly);
1344
1345                         return ((CLSCompliantAttribute)CompliantAttribute[0]).IsCompliant;
1346                 }
1347         }
1348 }