1 // -*- coding: dos -*-
\r
3 // attribute.cs: Attribute Handler
\r
5 // Author: Ravi Pratap (ravi@ximian.com)
\r
7 // Licensed under the terms of the GNU GPL
\r
9 // (C) 2001 Ximian, Inc (http://www.ximian.com)
\r
14 using System.Diagnostics;
\r
15 using System.Collections;
\r
16 using System.Collections.Specialized;
\r
17 using System.Reflection;
\r
18 using System.Reflection.Emit;
\r
19 using System.Runtime.InteropServices;
\r
20 using System.Runtime.CompilerServices;
\r
23 namespace Mono.CSharp {
\r
25 /// Base class for objects that can have Attributes applied to them.
\r
27 public abstract class Attributable {
\r
29 /// Attributes for this type
\r
31 Attributes attributes;
\r
33 public Attributable(Attributes attrs)
\r
36 if (attributes != null)
\r
37 attributes.CheckTargets (ValidAttributeTargets);
\r
40 public Attributes OptAttributes
\r
51 /// Use member-specific procedure to apply attribute @a in @cb to the entity being built in @builder
\r
53 public abstract void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb);
\r
56 /// Returns combination of enabled AttributeTargets
\r
58 public abstract AttributeTargets AttributeTargets { get; }
\r
60 public abstract bool IsClsCompliaceRequired (DeclSpace ds);
\r
63 /// Gets list of valid attribute targets for explicit target declaration
\r
65 protected abstract string[] ValidAttributeTargets { get; }
\r
68 public class Attribute {
\r
69 public readonly string Target;
\r
70 public readonly string Name;
\r
71 public readonly ArrayList Arguments;
\r
73 public readonly Location Location;
\r
77 // Is non-null if type is AttributeUsageAttribute
\r
78 AttributeUsageAttribute usage_attribute;
\r
80 public AttributeUsageAttribute UsageAttribute {
\r
82 return usage_attribute;
\r
86 MethodImplOptions ImplOptions;
\r
87 UnmanagedType UnmanagedType;
\r
88 CustomAttributeBuilder cb;
\r
90 // non-null if named args present after Resolve () is called
\r
91 PropertyInfo [] prop_info_arr;
\r
92 FieldInfo [] field_info_arr;
\r
93 object [] field_values_arr;
\r
94 object [] prop_values_arr;
\r
96 object [] pos_values;
\r
98 static PtrHashtable usage_attr_cache = new PtrHashtable ();
\r
100 public Attribute (string target, string name, ArrayList args, Location loc)
\r
108 void Error_InvalidNamedArgument (string name)
\r
110 Report.Error (617, Location, "'" + name + "' is not a valid named attribute " +
\r
111 "argument. Named attribute arguments must be fields which are not " +
\r
112 "readonly, static or const, or properties with a set accessor which "+
\r
113 "are not static.");
\r
116 static void Error_AttributeArgumentNotValid (Location loc)
\r
118 Report.Error (182, loc,
\r
119 "An attribute argument must be a constant expression, typeof " +
\r
120 "expression or array creation expression");
\r
123 static void Error_AttributeConstructorMismatch (Location loc)
\r
125 Report.Error (-6, loc,
\r
126 "Could not find a constructor for this argument list.");
\r
129 static void Error_TypeParameterInAttribute (Location loc)
\r
132 -202, loc, "Can not use a type parameter in an attribute");
\r
136 /// Tries to resolve the type of the attribute. Flags an error if it can't, and complain is true.
\r
138 protected virtual Type CheckAttributeType (EmitContext ec, bool complain)
\r
140 TypeExpr t1 = RootContext.LookupType (ec.DeclSpace, Name, true, Location);
\r
141 // FIXME: Shouldn't do this for quoted attributes: [@A]
\r
142 TypeExpr t2 = RootContext.LookupType (ec.DeclSpace, Name + "Attribute", true, Location);
\r
144 String err0616 = null;
\r
145 if (t1 != null && ! t1.IsAttribute) {
\r
147 err0616 = "'" + Name + "': is not an attribute class";
\r
149 if (t2 != null && ! t2.IsAttribute) {
\r
151 err0616 = (err0616 != null)
\r
152 ? "Neither '" + Name + "' nor '" + Name + "Attribute' is an attribute class"
\r
153 : "'" + Name + "Attribute': is not an attribute class";
\r
156 if (t1 != null && t2 != null) {
\r
157 Report.Error(1614, Location, "'" + Name + "': is ambiguous; "
\r
158 + " use either '@" + Name + "' or '" + Name + "Attribute'");
\r
163 return t1.ResolveType (ec);
\r
165 return t2.ResolveType (ec);
\r
166 if (err0616 != null) {
\r
167 Report.Error (616, Location, err0616);
\r
172 246, Location, "Could not find attribute '" + Name + "' (are you" +
\r
173 " missing a using directive or an assembly reference ?)");
\r
177 public Type ResolveType (EmitContext ec, bool complain)
\r
180 Type = CheckAttributeType (ec, complain);
\r
185 /// Validates the guid string
\r
187 bool ValidateGuid (string guid)
\r
193 Report.Error (647, Location, "Format of GUID is invalid: " + guid);
\r
199 // Given an expression, if the expression is a valid attribute-argument-expression
\r
200 // returns an object that can be used to encode it, or null on failure.
\r
202 public static bool GetAttributeArgumentExpression (Expression e, Location loc, out object result)
\r
204 if (e is Constant) {
\r
205 result = ((Constant) e).GetValue ();
\r
207 } else if (e is TypeOf) {
\r
208 result = ((TypeOf) e).TypeArg;
\r
210 } else if (e is ArrayCreation){
\r
211 result = ((ArrayCreation) e).EncodeAsAttribute ();
\r
212 if (result != null)
\r
214 } else if (e is EmptyCast) {
\r
216 if (((EmptyCast) e).Child is Constant) {
\r
217 result = ((Constant) ((EmptyCast)e).Child).GetValue();
\r
223 Error_AttributeArgumentNotValid (loc);
\r
227 public CustomAttributeBuilder Resolve (EmitContext ec)
\r
229 Type oldType = Type;
\r
232 Type = CheckAttributeType (ec, true);
\r
233 if (oldType == null && Type == null)
\r
235 if (oldType != null && oldType != Type) {
\r
236 Report.Error (-6, Location,
\r
237 "Attribute {0} resolved to different types at different times: {1} vs. {2}",
\r
238 Name, oldType, Type);
\r
242 bool MethodImplAttr = false;
\r
243 bool MarshalAsAttr = false;
\r
244 bool GuidAttr = false;
\r
245 bool usage_attr = false;
\r
247 bool DoCompares = true;
\r
250 // If we are a certain special attribute, we
\r
251 // set the information accordingly
\r
254 if (Type == TypeManager.attribute_usage_type)
\r
256 else if (Type == TypeManager.methodimpl_attr_type)
\r
257 MethodImplAttr = true;
\r
258 else if (Type == TypeManager.marshal_as_attr_type)
\r
259 MarshalAsAttr = true;
\r
260 else if (Type == TypeManager.guid_attr_type)
\r
263 DoCompares = false;
\r
265 // Now we extract the positional and named arguments
\r
267 ArrayList pos_args = new ArrayList ();
\r
268 ArrayList named_args = new ArrayList ();
\r
269 int pos_arg_count = 0;
\r
271 if (Arguments != null) {
\r
272 pos_args = (ArrayList) Arguments [0];
\r
273 if (pos_args != null)
\r
274 pos_arg_count = pos_args.Count;
\r
275 if (Arguments.Count > 1)
\r
276 named_args = (ArrayList) Arguments [1];
\r
279 pos_values = new object [pos_arg_count];
\r
282 // First process positional arguments
\r
286 for (i = 0; i < pos_arg_count; i++) {
\r
287 Argument a = (Argument) pos_args [i];
\r
290 if (!a.Resolve (ec, Location))
\r
296 if (!GetAttributeArgumentExpression (e, Location, out val))
\r
299 pos_values [i] = val;
\r
302 usage_attribute = new AttributeUsageAttribute ((AttributeTargets) pos_values [0]);
\r
303 else if (MethodImplAttr)
\r
304 this.ImplOptions = (MethodImplOptions) pos_values [0];
\r
305 else if (GuidAttr){
\r
307 // we will later check the validity of the type
\r
309 if (pos_values [0] is string){
\r
310 if (!ValidateGuid ((string) pos_values [0]))
\r
314 } else if (MarshalAsAttr)
\r
315 this.UnmanagedType =
\r
316 (System.Runtime.InteropServices.UnmanagedType) pos_values [0];
\r
321 // Now process named arguments
\r
324 ArrayList field_infos = null;
\r
325 ArrayList prop_infos = null;
\r
326 ArrayList field_values = null;
\r
327 ArrayList prop_values = null;
\r
329 Hashtable seen_names = new Hashtable();
\r
331 if (named_args.Count > 0) {
\r
332 field_infos = new ArrayList ();
\r
333 prop_infos = new ArrayList ();
\r
334 field_values = new ArrayList ();
\r
335 prop_values = new ArrayList ();
\r
338 for (i = 0; i < named_args.Count; i++) {
\r
339 DictionaryEntry de = (DictionaryEntry) named_args [i];
\r
340 string member_name = (string) de.Key;
\r
341 Argument a = (Argument) de.Value;
\r
344 if (seen_names.Contains(member_name)) {
\r
345 Report.Error(643, Location, "'" + member_name + "' duplicate named attribute argument");
\r
348 seen_names.Add(member_name, 1);
\r
350 if (!a.Resolve (ec, Location))
\r
353 Expression member = Expression.MemberLookup (
\r
354 ec, Type, member_name,
\r
355 MemberTypes.Field | MemberTypes.Property,
\r
356 BindingFlags.Public | BindingFlags.Instance,
\r
359 if (member == null || !(member is PropertyExpr || member is FieldExpr)) {
\r
360 Error_InvalidNamedArgument (member_name);
\r
365 if (e is TypeParameterExpr){
\r
366 Error_TypeParameterInAttribute (Location);
\r
370 if (member is PropertyExpr) {
\r
371 PropertyExpr pe = (PropertyExpr) member;
\r
372 PropertyInfo pi = pe.PropertyInfo;
\r
374 if (!pi.CanWrite) {
\r
375 Error_InvalidNamedArgument (member_name);
\r
379 if (e is Constant) {
\r
382 if (e.Type != pi.PropertyType){
\r
383 c = Const.ChangeType (Location, (Constant) e, pi.PropertyType);
\r
389 object o = c.GetValue ();
\r
390 prop_values.Add (o);
\r
392 if (usage_attribute != null) {
\r
393 if (member_name == "AllowMultiple")
\r
394 usage_attribute.AllowMultiple = (bool) o;
\r
395 if (member_name == "Inherited")
\r
396 usage_attribute.Inherited = (bool) o;
\r
399 } else if (e is TypeOf) {
\r
400 prop_values.Add (((TypeOf) e).TypeArg);
\r
401 } else if (e is ArrayCreation) {
\r
402 prop_values.Add (((ArrayCreation) e).EncodeAsAttribute());
\r
404 Error_AttributeArgumentNotValid (Location);
\r
408 prop_infos.Add (pi);
\r
410 } else if (member is FieldExpr) {
\r
411 FieldExpr fe = (FieldExpr) member;
\r
412 FieldInfo fi = fe.FieldInfo;
\r
414 if (fi.IsInitOnly) {
\r
415 Error_InvalidNamedArgument (member_name);
\r
420 // Handle charset here, and set the TypeAttributes
\r
422 if (e is Constant){
\r
423 Constant c = (Constant) e;;
\r
425 if (c.Type != fi.FieldType){
\r
426 c = Const.ChangeType (Location, (Constant) e, fi.FieldType);
\r
431 object value = c.GetValue ();
\r
432 field_values.Add (value);
\r
433 } else if (e is TypeOf) {
\r
434 field_values.Add (((TypeOf) e).TypeArg);
\r
436 Error_AttributeArgumentNotValid (Location);
\r
440 field_infos.Add (fi);
\r
444 Expression mg = Expression.MemberLookup (
\r
445 ec, Type, ".ctor", MemberTypes.Constructor,
\r
446 BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly,
\r
450 Error_AttributeConstructorMismatch (Location);
\r
454 MethodBase constructor = Invocation.OverloadResolve (
\r
455 ec, (MethodGroupExpr) mg, pos_args, false, Location);
\r
457 if (constructor == null) {
\r
458 Error_AttributeConstructorMismatch (Location);
\r
463 // Now we perform some checks on the positional args as they
\r
464 // cannot be null for a constructor which expects a parameter
\r
468 ParameterData pd = Invocation.GetParameterData (constructor);
\r
470 int group_in_params_array = Int32.MaxValue;
\r
472 if (pc > 0 && pd.ParameterModifier (pc-1) == Parameter.Modifier.PARAMS)
\r
473 group_in_params_array = pc-1;
\r
475 for (int j = 0; j < pos_arg_count; ++j) {
\r
476 Argument a = (Argument) pos_args [j];
\r
478 if (a.Expr is NullLiteral && pd.ParameterType (j) == TypeManager.object_type) {
\r
479 Error_AttributeArgumentNotValid (Location);
\r
483 if (j < group_in_params_array)
\r
486 if (j == group_in_params_array){
\r
487 object v = pos_values [j];
\r
488 int count = pos_arg_count - j;
\r
490 object [] array = new object [count];
\r
491 pos_values [j] = array;
\r
494 object [] array = (object []) pos_values [group_in_params_array];
\r
496 array [j - group_in_params_array] = pos_values [j];
\r
501 // Adjust the size of the pos_values if it had params
\r
503 if (group_in_params_array != Int32.MaxValue){
\r
504 int argc = group_in_params_array+1;
\r
505 object [] new_pos_values = new object [argc];
\r
507 for (int p = 0; p < argc; p++)
\r
508 new_pos_values [p] = pos_values [p];
\r
509 pos_values = new_pos_values;
\r
513 if (named_args.Count > 0) {
\r
514 prop_info_arr = new PropertyInfo [prop_infos.Count];
\r
515 field_info_arr = new FieldInfo [field_infos.Count];
\r
516 field_values_arr = new object [field_values.Count];
\r
517 prop_values_arr = new object [prop_values.Count];
\r
519 field_infos.CopyTo (field_info_arr, 0);
\r
520 field_values.CopyTo (field_values_arr, 0);
\r
522 prop_values.CopyTo (prop_values_arr, 0);
\r
523 prop_infos.CopyTo (prop_info_arr, 0);
\r
525 cb = new CustomAttributeBuilder (
\r
526 (ConstructorInfo) constructor, pos_values,
\r
527 prop_info_arr, prop_values_arr,
\r
528 field_info_arr, field_values_arr);
\r
531 cb = new CustomAttributeBuilder (
\r
532 (ConstructorInfo) constructor, pos_values);
\r
533 } catch (NullReferenceException) {
\r
535 // Don't know what to do here
\r
538 -101, Location, "NullReferenceException while trying to create attribute." +
\r
539 "Something's wrong!");
\r
540 } catch (Exception e) {
\r
543 // using System.ComponentModel;
\r
544 // [DefaultValue (CollectionChangeAction.Add)]
\r
545 // class X { static void Main () {} }
\r
549 "The compiler can not encode this attribute in .NET due to\n" +
\r
550 "\ta bug in the .NET runtime. Try the Mono runtime.\nThe error was: " + e.Message);
\r
557 /// Get a string containing a list of valid targets for the attribute 'attr'
\r
559 static string GetValidTargets (Attribute attr)
\r
561 StringBuilder sb = new StringBuilder ();
\r
562 AttributeTargets targets = attr.GetAttributeUsage ().ValidOn;
\r
564 if ((targets & AttributeTargets.Assembly) != 0)
\r
565 sb.Append ("'assembly' ");
\r
567 if ((targets & AttributeTargets.Class) != 0)
\r
568 sb.Append ("'class' ");
\r
570 if ((targets & AttributeTargets.Constructor) != 0)
\r
571 sb.Append ("'constructor' ");
\r
573 if ((targets & AttributeTargets.Delegate) != 0)
\r
574 sb.Append ("'delegate' ");
\r
576 if ((targets & AttributeTargets.Enum) != 0)
\r
577 sb.Append ("'enum' ");
\r
579 if ((targets & AttributeTargets.Event) != 0)
\r
580 sb.Append ("'event' ");
\r
582 if ((targets & AttributeTargets.Field) != 0)
\r
583 sb.Append ("'field' ");
\r
585 if ((targets & AttributeTargets.Interface) != 0)
\r
586 sb.Append ("'interface' ");
\r
588 if ((targets & AttributeTargets.Method) != 0)
\r
589 sb.Append ("'method' ");
\r
591 if ((targets & AttributeTargets.Module) != 0)
\r
592 sb.Append ("'module' ");
\r
594 if ((targets & AttributeTargets.Parameter) != 0)
\r
595 sb.Append ("'parameter' ");
\r
597 if ((targets & AttributeTargets.Property) != 0)
\r
598 sb.Append ("'property' ");
\r
600 if ((targets & AttributeTargets.ReturnValue) != 0)
\r
601 sb.Append ("'return' ");
\r
603 if ((targets & AttributeTargets.Struct) != 0)
\r
604 sb.Append ("'struct' ");
\r
606 return sb.ToString ();
\r
610 public static void Error_AttributeNotValidForElement (Attribute a, Location loc)
\r
613 592, loc, "Attribute '" + a.Name +
\r
614 "' is not valid on this declaration type. " +
\r
615 "It is valid on " + GetValidTargets (a) + "declarations only.");
\r
619 /// Returns AttributeUsage attribute for this type
\r
621 public AttributeUsageAttribute GetAttributeUsage ()
\r
623 AttributeUsageAttribute ua = usage_attr_cache [Type] as AttributeUsageAttribute;
\r
627 Class attr_class = TypeManager.LookupClass (Type);
\r
629 if (attr_class == null) {
\r
630 object[] usage_attr = Type.GetCustomAttributes (TypeManager.attribute_usage_type, true);
\r
631 ua = (AttributeUsageAttribute)usage_attr [0];
\r
632 usage_attr_cache.Add (Type, ua);
\r
636 return attr_class.AttributeUsage;
\r
640 // This method should be invoked to pull the IndexerName attribute from an
\r
641 // Indexer if it exists.
\r
643 public static string ScanForIndexerName (EmitContext ec, Attributes opt_attrs)
\r
645 if (opt_attrs == null)
\r
647 if (opt_attrs.AttributeSections == null)
\r
650 foreach (Attribute a in opt_attrs.AttributeSections) {
\r
651 if (a.ResolveType (ec, true) == null)
\r
654 if (a.Type != TypeManager.indexer_name_type)
\r
658 // So we have found an IndexerName, pull the data out.
\r
660 if (a.Arguments == null || a.Arguments [0] == null){
\r
661 Error_AttributeConstructorMismatch (a.Location);
\r
664 ArrayList pos_args = (ArrayList) a.Arguments [0];
\r
665 if (pos_args.Count == 0){
\r
666 Error_AttributeConstructorMismatch (a.Location);
\r
670 Argument arg = (Argument) pos_args [0];
\r
671 if (!arg.Resolve (ec, a.Location))
\r
674 Expression e = arg.Expr;
\r
675 if (!(e is StringConstant)){
\r
676 Error_AttributeConstructorMismatch (a.Location);
\r
681 // Remove the attribute from the list
\r
684 //TODO: It is very close to hack and it can crash here
\r
685 opt_attrs.AttributeSections.Remove (a);
\r
687 return (((StringConstant) e).Value);
\r
693 // This pulls the condition name out of a Conditional attribute
\r
695 public string Conditional_GetConditionName ()
\r
698 // So we have a Conditional, pull the data out.
\r
700 if (Arguments == null || Arguments [0] == null){
\r
701 Error_AttributeConstructorMismatch (Location);
\r
705 ArrayList pos_args = (ArrayList) Arguments [0];
\r
706 if (pos_args.Count != 1){
\r
707 Error_AttributeConstructorMismatch (Location);
\r
711 Argument arg = (Argument) pos_args [0];
\r
712 if (!(arg.Expr is StringConstant)){
\r
713 Error_AttributeConstructorMismatch (Location);
\r
717 return ((StringConstant) arg.Expr).Value;
\r
721 // This pulls the obsolete message and error flag out of an Obsolete attribute
\r
723 public string Obsolete_GetObsoleteMessage (out bool is_error)
\r
727 // So we have an Obsolete, pull the data out.
\r
729 if (Arguments == null || Arguments [0] == null)
\r
732 ArrayList pos_args = (ArrayList) Arguments [0];
\r
733 if (pos_args.Count == 0)
\r
735 else if (pos_args.Count > 2){
\r
736 Error_AttributeConstructorMismatch (Location);
\r
740 Argument arg = (Argument) pos_args [0];
\r
741 if (!(arg.Expr is StringConstant)){
\r
742 Error_AttributeConstructorMismatch (Location);
\r
746 if (pos_args.Count == 2){
\r
747 Argument arg2 = (Argument) pos_args [1];
\r
748 if (!(arg2.Expr is BoolConstant)){
\r
749 Error_AttributeConstructorMismatch (Location);
\r
752 is_error = ((BoolConstant) arg2.Expr).Value;
\r
755 return ((StringConstant) arg.Expr).Value;
\r
759 /// Returns value of CLSCompliantAttribute contructor parameter but because the method can be called
\r
760 /// before ApplyAttribute. We need to resolve the arguments.
\r
761 /// This situation occurs when class deps is differs from Emit order.
\r
763 public bool GetClsCompliantAttributeValue (DeclSpace ds)
\r
765 if (pos_values == null) {
\r
766 EmitContext ec = new EmitContext (ds, ds, Location, null, null, 0, false);
\r
768 // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args.
\r
769 // But because a lot of attribute class code must be rewritten will be better to wait...
\r
774 // Some error occurred
\r
775 if (pos_values [0] == null)
\r
778 return (bool)pos_values [0];
\r
781 public object GetPositionalValue (int i)
\r
783 return (pos_values == null) ? null : pos_values[i];
\r
786 object GetFieldValue (string name)
\r
789 if (field_info_arr == null)
\r
792 foreach (FieldInfo fi in field_info_arr) {
\r
793 if (fi.Name == name)
\r
794 return field_values_arr [i];
\r
800 public UnmanagedMarshal GetMarshal ()
\r
802 object o = GetFieldValue ("ArraySubType");
\r
803 UnmanagedType array_sub_type = o == null ? UnmanagedType.I4 : (UnmanagedType) o;
\r
804 switch (UnmanagedType) {
\r
805 case UnmanagedType.CustomMarshaler:
\r
806 MethodInfo define_custom = typeof (UnmanagedMarshal).GetMethod ("DefineCustom",
\r
807 BindingFlags.Static | BindingFlags.Public);
\r
808 if (define_custom == null)
\r
811 object [] args = new object [4];
\r
812 args [0] = GetFieldValue ("MarshalTypeRef");
\r
813 args [1] = GetFieldValue ("MarshalCookie");
\r
814 args [2] = GetFieldValue ("MarshalType");
\r
815 args [3] = Guid.Empty;
\r
816 return (UnmanagedMarshal) define_custom.Invoke (null, args);
\r
818 case UnmanagedType.LPArray:
\r
819 return UnmanagedMarshal.DefineLPArray (array_sub_type);
\r
821 case UnmanagedType.SafeArray:
\r
822 return UnmanagedMarshal.DefineSafeArray (array_sub_type);
\r
824 case UnmanagedType.ByValArray:
\r
825 return UnmanagedMarshal.DefineByValArray ((int) GetFieldValue ("SizeConst"));
\r
827 case UnmanagedType.ByValTStr:
\r
828 return UnmanagedMarshal.DefineByValTStr ((int) GetFieldValue ("SizeConst"));
\r
831 return UnmanagedMarshal.DefineUnmanagedMarshal (UnmanagedType);
\r
835 public bool IsInternalCall
\r
837 get { return ImplOptions == MethodImplOptions.InternalCall; }
\r
841 /// Emit attribute for Attributable symbol
\r
843 public void Emit (EmitContext ec, Attributable ias, ListDictionary emitted_attr)
\r
845 CustomAttributeBuilder cb = Resolve (ec);
\r
849 AttributeUsageAttribute usage_attr = GetAttributeUsage ();
\r
850 if ((usage_attr.ValidOn & ias.AttributeTargets) == 0) {
\r
851 Error_AttributeNotValidForElement (this, Location);
\r
855 ias.ApplyAttributeBuilder (this, cb);
\r
857 string emitted = emitted_attr [Type] as string;
\r
858 if (Target != null && emitted == Target && !usage_attr.AllowMultiple) {
\r
859 Report.Error (579, Location, "Duplicate '" + Name + "' attribute");
\r
862 emitted_attr [Type] = Target;
\r
864 // Here we are testing attribute arguments for array usage (error 3016)
\r
865 if (ias.IsClsCompliaceRequired (ec.DeclSpace)) {
\r
866 if (Arguments == null)
\r
869 ArrayList pos_args = (ArrayList) Arguments [0];
\r
871 if (pos_args != null) {
\r
872 foreach (Argument arg in pos_args) {
\r
873 // Type is undefined (was error 246)
\r
874 if (arg.Type == null)
\r
877 if (arg.Type.IsArray) {
\r
878 Report.Error_T (3016, Location);
\r
884 if (Arguments.Count < 2)
\r
887 ArrayList named_args = (ArrayList) Arguments [1];
\r
889 foreach (DictionaryEntry de in named_args) {
\r
890 Argument arg = (Argument) de.Value;
\r
892 // Type is undefined (was error 246)
\r
893 if (arg.Type == null)
\r
896 if (arg.Type.IsArray) {
\r
897 Report.Error_T (3016, Location);
\r
904 public object GetValue (EmitContext ec, Constant c, Type target)
\r
906 if (Convert.ImplicitConversionExists (ec, c, target))
\r
907 return c.GetValue ();
\r
909 Convert.Error_CannotImplicitConversion (Location, c.Type, target);
\r
913 public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name,
\r
914 MethodAttributes flags, Type ret_type, Type [] param_types)
\r
917 // We extract from the attribute the information we need
\r
920 if (Arguments == null) {
\r
921 Console.WriteLine ("Internal error : this is not supposed to happen !");
\r
925 ResolveType (ec, true);
\r
929 ArrayList named_args = new ArrayList ();
\r
931 ArrayList pos_args = (ArrayList) Arguments [0];
\r
932 if (Arguments.Count > 1)
\r
933 named_args = (ArrayList) Arguments [1];
\r
936 string dll_name = null;
\r
938 Argument tmp = (Argument) pos_args [0];
\r
940 if (!tmp.Resolve (ec, Location))
\r
943 if (tmp.Expr is Constant)
\r
944 dll_name = (string) ((Constant) tmp.Expr).GetValue ();
\r
946 Error_AttributeArgumentNotValid (Location);
\r
950 // Now we process the named arguments
\r
951 CallingConvention cc = CallingConvention.Winapi;
\r
952 CharSet charset = CharSet.Ansi;
\r
953 bool preserve_sig = true;
\r
955 bool exact_spelling = false;
\r
957 bool set_last_err = false;
\r
958 string entry_point = null;
\r
960 for (int i = 0; i < named_args.Count; i++) {
\r
962 DictionaryEntry de = (DictionaryEntry) named_args [i];
\r
964 string member_name = (string) de.Key;
\r
965 Argument a = (Argument) de.Value;
\r
967 if (!a.Resolve (ec, Location))
\r
970 Expression member = Expression.MemberLookup (
\r
971 ec, Type, member_name,
\r
972 MemberTypes.Field | MemberTypes.Property,
\r
973 BindingFlags.Public | BindingFlags.Instance,
\r
976 if (member == null || !(member is FieldExpr)) {
\r
977 Error_InvalidNamedArgument (member_name);
\r
981 if (member is FieldExpr) {
\r
982 FieldExpr fe = (FieldExpr) member;
\r
983 FieldInfo fi = fe.FieldInfo;
\r
985 if (fi.IsInitOnly) {
\r
986 Error_InvalidNamedArgument (member_name);
\r
990 if (a.Expr is Constant) {
\r
991 Constant c = (Constant) a.Expr;
\r
994 if (member_name == "CallingConvention"){
\r
995 object val = GetValue (ec, c, typeof (CallingConvention));
\r
998 cc = (CallingConvention) val;
\r
999 } else if (member_name == "CharSet"){
\r
1000 charset = (CharSet) c.GetValue ();
\r
1001 } else if (member_name == "EntryPoint")
\r
1002 entry_point = (string) c.GetValue ();
\r
1003 else if (member_name == "SetLastError")
\r
1004 set_last_err = (bool) c.GetValue ();
\r
1006 else if (member_name == "ExactSpelling")
\r
1007 exact_spelling = (bool) c.GetValue ();
\r
1009 else if (member_name == "PreserveSig")
\r
1010 preserve_sig = (bool) c.GetValue ();
\r
1011 } catch (InvalidCastException){
\r
1012 Error_InvalidNamedArgument (member_name);
\r
1013 Error_AttributeArgumentNotValid (Location);
\r
1016 Error_AttributeArgumentNotValid (Location);
\r
1023 if (entry_point == null)
\r
1024 entry_point = name;
\r
1026 charset = (CharSet)((int)charset | 0x40);
\r
1028 MethodBuilder mb = builder.DefinePInvokeMethod (
\r
1029 name, dll_name, entry_point, flags | MethodAttributes.HideBySig,
\r
1030 CallingConventions.Standard,
\r
1037 mb.SetImplementationFlags (MethodImplAttributes.PreserveSig);
\r
1042 private Expression GetValue ()
\r
1044 if ((Arguments == null) || (Arguments.Count < 1))
\r
1046 ArrayList al = (ArrayList) Arguments [0];
\r
1047 if ((al == null) || (al.Count < 1))
\r
1049 Argument arg = (Argument) al [0];
\r
1050 if ((arg == null) || (arg.Expr == null))
\r
1055 public string GetString ()
\r
1057 Expression e = GetValue ();
\r
1058 if (e is StringLiteral)
\r
1059 return (e as StringLiteral).Value;
\r
1063 public bool GetBoolean ()
\r
1065 Expression e = GetValue ();
\r
1066 if (e is BoolLiteral)
\r
1067 return (e as BoolLiteral).Value;
\r
1072 /// For global attributes (assembly, module) we need special handling.
\r
1073 /// Attributes can be located in the several files
\r
1075 public class GlobalAttribute: Attribute
\r
1077 public readonly NamespaceEntry ns;
\r
1079 public GlobalAttribute (TypeContainer container, string target, string name, ArrayList args, Location loc):
\r
1080 base (target, name, args, loc)
\r
1082 ns = container.NamespaceEntry;
\r
1085 protected override Type CheckAttributeType (EmitContext ec, bool complain)
\r
1087 ec.DeclSpace.NamespaceEntry = ns;
\r
1088 return base.CheckAttributeType (ec, complain);
\r
1092 public class Attributes {
\r
1093 public ArrayList AttributeSections;
\r
1095 public Attributes (Attribute a)
\r
1097 AttributeSections = new ArrayList ();
\r
1098 AttributeSections.Add (a);
\r
1101 public Attributes (ArrayList attrs)
\r
1103 AttributeSections = attrs;
\r
1106 public void AddAttributes (ArrayList attrs)
\r
1108 AttributeSections.AddRange (attrs);
\r
1112 /// Checks whether attribute target is valid for the current element
\r
1114 public void CheckTargets (string[] possible_targets)
\r
1116 foreach (Attribute a in AttributeSections) {
\r
1117 if (a.Target == null)
\r
1120 bool valid_target = false;
\r
1121 for (int i = 0; i < possible_targets.Length; ++i) {
\r
1122 if (a.Target == possible_targets [i]) {
\r
1123 valid_target = true;
\r
1131 StringBuilder sb = new StringBuilder ();
\r
1132 foreach (string s in possible_targets) {
\r
1136 sb.Remove (sb.Length - 2, 2);
\r
1137 Report.Error_T (657, a.Location, a.Target, sb.ToString ());
\r
1141 public Attribute Search (Type t, EmitContext ec)
\r
1143 foreach (Attribute attr_section in AttributeSections) {
\r
1144 if (attr_section.ResolveType (ec, false) == t)
\r
1145 return attr_section;
\r
1150 public void Emit (EmitContext ec, Attributable ias)
\r
1152 ListDictionary ld = new ListDictionary ();
\r
1154 foreach (Attribute a in AttributeSections) {
\r
1155 a.Emit (ec, ias, ld);
\r
1159 public bool Contains (Type t, EmitContext ec)
\r
1161 return Search (t, ec) != null;
\r
1164 public Attribute GetClsCompliantAttribute (EmitContext ec)
\r
1166 return Search (TypeManager.cls_compliant_attribute_type, ec);
\r
1171 /// Helper class for attribute verification routine.
\r
1173 sealed class AttributeTester
\r
1175 static PtrHashtable analyzed_types = new PtrHashtable ();
\r
1177 private AttributeTester ()
\r
1182 /// Returns true if parameters of two compared methods are CLS-Compliant.
\r
1183 /// It tests differing only in ref or out, or in array rank.
\r
1185 public static bool AreOverloadedMethodParamsClsCompliant (Type[] types_a, Type[] types_b)
\r
1187 if (types_a == null || types_b == null)
\r
1190 if (types_a.Length != types_b.Length)
\r
1193 for (int i = 0; i < types_b.Length; ++i) {
\r
1194 Type aType = types_a [i];
\r
1195 Type bType = types_b [i];
\r
1197 if (aType.IsArray && bType.IsArray && aType.GetArrayRank () != bType.GetArrayRank () && aType.GetElementType () == bType.GetElementType ()) {
\r
1201 Type aBaseType = aType;
\r
1202 bool is_either_ref_or_out = false;
\r
1204 if (aType.IsByRef || aType.IsPointer) {
\r
1205 aBaseType = aType.GetElementType ();
\r
1206 is_either_ref_or_out = true;
\r
1209 Type bBaseType = bType;
\r
1210 if (bType.IsByRef || bType.IsPointer)
\r
1212 bBaseType = bType.GetElementType ();
\r
1213 is_either_ref_or_out = !is_either_ref_or_out;
\r
1216 if (aBaseType != bBaseType)
\r
1219 if (is_either_ref_or_out)
\r
1226 /// Goes through all parameters and test if they are CLS-Compliant.
\r
1228 public static bool AreParametersCompliant (Parameter[] fixedParameters, Location loc)
\r
1230 if (fixedParameters == null)
\r
1233 foreach (Parameter arg in fixedParameters) {
\r
1234 if (!AttributeTester.IsClsCompliant (arg.ParameterType)) {
\r
1235 Report.Error_T (3001, loc, arg.GetSignatureForError ());
\r
1244 /// This method tests the CLS compliance of external types. It doesn't test type visibility.
\r
1246 public static bool IsClsCompliant (Type type)
\r
1251 object type_compliance = analyzed_types[type];
\r
1252 if (type_compliance != null)
\r
1253 return type_compliance == TRUE;
\r
1255 if (type.IsPointer) {
\r
1256 analyzed_types.Add (type, null);
\r
1261 if (type.IsArray || type.IsByRef) {
\r
1262 result = IsClsCompliant (TypeManager.GetElementType (type));
\r
1264 result = AnalyzeTypeCompliance (type);
\r
1266 analyzed_types.Add (type, result ? TRUE : FALSE);
\r
1270 static object TRUE = new object ();
\r
1271 static object FALSE = new object ();
\r
1274 /// Non-hierarchical CLS Compliance analyzer
\r
1276 public static bool IsComplianceRequired (MemberInfo mi, DeclSpace ds)
\r
1278 DeclSpace temp_ds = TypeManager.LookupDeclSpace (mi.DeclaringType);
\r
1280 // Type is external, we can get attribute directly
\r
1281 if (temp_ds == null) {
\r
1282 object[] cls_attribute = mi.GetCustomAttributes (TypeManager.cls_compliant_attribute_type, false);
\r
1283 return (cls_attribute.Length == 1 && ((CLSCompliantAttribute)cls_attribute[0]).IsCompliant);
\r
1287 // Interface doesn't store full name
\r
1288 if (temp_ds is Interface)
\r
1289 tmp_name = mi.Name;
\r
1291 tmp_name = String.Concat (temp_ds.Name, ".", mi.Name);
\r
1293 MemberCore mc = temp_ds.GetDefinition (tmp_name) as MemberCore;
\r
1294 return mc.IsClsCompliaceRequired (ds);
\r
1297 public static void VerifyModulesClsCompliance ()
\r
1299 Module[] modules = TypeManager.Modules;
\r
1300 if (modules == null)
\r
1303 // The first module is generated assembly
\r
1304 for (int i = 1; i < modules.Length; ++i) {
\r
1305 Module module = modules [i];
\r
1306 if (!IsClsCompliant (module)) {
\r
1307 Report.Error_T (3013, module.Name);
\r
1313 static bool IsClsCompliant (ICustomAttributeProvider attribute_provider)
\r
1315 object[] CompliantAttribute = attribute_provider.GetCustomAttributes (TypeManager.cls_compliant_attribute_type, false);
\r
1316 if (CompliantAttribute.Length == 0)
\r
1319 return ((CLSCompliantAttribute)CompliantAttribute[0]).IsCompliant;
\r
1322 static bool AnalyzeTypeCompliance (Type type)
\r
1324 DeclSpace ds = TypeManager.LookupDeclSpace (type);
\r
1326 return ds.IsClsCompliaceRequired (ds.Parent);
\r
1329 if (type.IsGenericParameter)
\r
1332 object[] CompliantAttribute = type.GetCustomAttributes (TypeManager.cls_compliant_attribute_type, false);
\r
1333 if (CompliantAttribute.Length == 0)
\r
1334 return IsClsCompliant (type.Assembly);
\r
1336 return ((CLSCompliantAttribute)CompliantAttribute[0]).IsCompliant;
\r