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