2 // attribute.cs: Attribute Handler
4 // Author: Ravi Pratap (ravi@ximian.com)
5 // Marek Safar (marek.safar@seznam.cz)
7 // Licensed under the terms of the GNU GPL
9 // (C) 2001 Ximian, Inc (http://www.ximian.com)
14 using System.Diagnostics;
15 using System.Collections;
16 using System.Collections.Specialized;
17 using System.Reflection;
18 using System.Reflection.Emit;
19 using System.Runtime.InteropServices;
20 using System.Runtime.CompilerServices;
21 using System.Security;
22 using System.Security.Permissions;
25 namespace Mono.CSharp {
28 /// Base class for objects that can have Attributes applied to them.
30 public abstract class Attributable {
32 /// Attributes for this type
34 Attributes attributes;
36 public Attributable (Attributes attrs)
41 public Attributes OptAttributes
52 /// Use member-specific procedure to apply attribute @a in @cb to the entity being built in @builder
54 public abstract void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb);
57 /// Returns one AttributeTarget for this element.
59 public abstract AttributeTargets AttributeTargets { get; }
61 public abstract bool IsClsCompliaceRequired (DeclSpace ds);
64 /// Gets list of valid attribute targets for explicit target declaration.
65 /// The first array item is default target. Don't break this rule.
67 public abstract string[] ValidAttributeTargets { get; }
70 public class Attribute {
71 public readonly string ExplicitTarget;
72 public AttributeTargets Target;
74 public readonly string Name;
75 public readonly ArrayList Arguments;
77 public readonly Location Location;
81 // Is non-null if type is AttributeUsageAttribute
82 AttributeUsageAttribute usage_attribute;
84 public AttributeUsageAttribute UsageAttribute {
86 return usage_attribute;
90 MethodImplOptions ImplOptions;
91 UnmanagedType UnmanagedType;
92 CustomAttributeBuilder cb;
94 // non-null if named args present after Resolve () is called
95 PropertyInfo [] prop_info_arr;
96 FieldInfo [] field_info_arr;
97 object [] field_values_arr;
98 object [] prop_values_arr;
101 static PtrHashtable usage_attr_cache = new PtrHashtable ();
103 public Attribute (string target, string name, ArrayList args, Location loc)
108 ExplicitTarget = target;
111 void Error_InvalidNamedArgument (string name)
113 Report.Error (617, Location, "Invalid attribute argument: '{0}'. Argument must be fields " +
114 "fields which are not readonly, static or const; or read-write instance properties.",
118 static void Error_AttributeArgumentNotValid (Location loc)
120 Report.Error (182, loc,
121 "An attribute argument must be a constant expression, typeof " +
122 "expression or array creation expression");
126 /// This is rather hack. We report many emit attribute error with same error to be compatible with
127 /// csc. But because csc has to report them this way because error came from ilasm we needn't.
129 public void Error_AttributeEmitError (string inner)
131 Report.Error (647, Location, "Error emitting '{0}' attribute because '{1}'", Name, inner);
134 public void Error_InvalidSecurityParent ()
136 Error_AttributeEmitError ("it is attached to invalid parent");
139 void Error_AttributeConstructorMismatch ()
141 Report.Error (-6, Location,
142 "Could not find a constructor for this argument list.");
146 /// Tries to resolve the type of the attribute. Flags an error if it can't, and complain is true.
148 protected virtual Type CheckAttributeType (EmitContext ec)
150 string NameAttribute = Name + "Attribute";
152 Type t1 = ec.ResolvingTypeTree
153 ? ec.DeclSpace.FindType (Location, Name)
154 : RootContext.LookupType (ec.DeclSpace, Name, true, Location);
156 // FIXME: Shouldn't do this for quoted attributes: [@A]
157 Type t2 = ec.ResolvingTypeTree
158 ? ec.DeclSpace.FindType (Location, NameAttribute)
159 : RootContext.LookupType (ec.DeclSpace, NameAttribute, true, Location);
161 String err0616 = null;
163 if (t1 != null && ! t1.IsSubclassOf (TypeManager.attribute_type)) {
165 err0616 = "'" + Name + "': is not an attribute class";
167 if (t2 != null && ! t2.IsSubclassOf (TypeManager.attribute_type)) {
169 err0616 = (err0616 != null)
170 ? "Neither '" + Name + "' nor '" + NameAttribute +"' is an attribute class"
171 : "'" + Name + "Attribute': is not an attribute class";
174 if (t1 != null && t2 != null) {
175 Report.Error(1614, Location, "'" + Name + "': is ambiguous; "
176 + " use either '@" + Name + "' or '" + NameAttribute + "'");
184 if (err0616 != null) {
185 Report.Error (616, Location, err0616);
189 Report.Error (246, Location,
190 "Could not find attribute '" + Name
191 + "' (are you missing a using directive or an assembly reference ?)");
196 public Type ResolveType (EmitContext ec)
199 Type = CheckAttributeType (ec);
204 /// Validates the guid string
206 bool ValidateGuid (string guid)
212 Report.Error (647, Location, "Format of GUID is invalid: " + guid);
217 string GetFullMemberName (string member)
219 return Type.FullName + '.' + member;
223 // Given an expression, if the expression is a valid attribute-argument-expression
224 // returns an object that can be used to encode it, or null on failure.
226 public static bool GetAttributeArgumentExpression (Expression e, Location loc, Type arg_type, out object result)
228 if (e is EnumConstant) {
229 if (RootContext.StdLib)
230 result = ((EnumConstant)e).GetValueAsEnumType ();
232 result = ((EnumConstant)e).GetValue ();
237 Constant constant = e as Constant;
238 if (constant != null) {
239 if (e.Type != arg_type) {
240 constant = Const.ChangeType (loc, constant, arg_type);
241 if (constant == null) {
243 Error_AttributeArgumentNotValid (loc);
247 result = constant.GetValue ();
249 } else if (e is TypeOf) {
250 result = ((TypeOf) e).TypeArg;
252 } else if (e is ArrayCreation){
253 result = ((ArrayCreation) e).EncodeAsAttribute ();
256 } else if (e is EmptyCast) {
257 Expression child = ((EmptyCast)e).Child;
258 return GetAttributeArgumentExpression (child, loc, child.Type, out result);
262 Error_AttributeArgumentNotValid (loc);
266 public CustomAttributeBuilder Resolve (EmitContext ec)
271 Type = CheckAttributeType (ec);
273 if (oldType == null && Type == null)
275 if (oldType != null && oldType != Type){
276 Report.Error (-27, Location,
277 "Attribute {0} resolved to different types at different times: {1} vs. {2}",
278 Name, oldType, Type);
282 if (Type.IsAbstract) {
283 Report.Error (653, Location, "Cannot apply attribute class '{0}' because it is abstract", Name);
287 bool MethodImplAttr = false;
288 bool MarshalAsAttr = false;
289 bool GuidAttr = false;
290 bool usage_attr = false;
292 bool DoCompares = true;
295 // If we are a certain special attribute, we
296 // set the information accordingly
299 if (Type == TypeManager.attribute_usage_type)
301 else if (Type == TypeManager.methodimpl_attr_type)
302 MethodImplAttr = true;
303 else if (Type == TypeManager.marshal_as_attr_type)
304 MarshalAsAttr = true;
305 else if (Type == TypeManager.guid_attr_type)
310 // Now we extract the positional and named arguments
312 ArrayList pos_args = new ArrayList ();
313 ArrayList named_args = new ArrayList ();
314 int pos_arg_count = 0;
316 if (Arguments != null) {
317 pos_args = (ArrayList) Arguments [0];
318 if (pos_args != null)
319 pos_arg_count = pos_args.Count;
320 if (Arguments.Count > 1)
321 named_args = (ArrayList) Arguments [1];
324 pos_values = new object [pos_arg_count];
327 // First process positional arguments
331 for (i = 0; i < pos_arg_count; i++) {
332 Argument a = (Argument) pos_args [i];
335 if (!a.Resolve (ec, Location))
341 if (!GetAttributeArgumentExpression (e, Location, a.Type, out val))
344 pos_values [i] = val;
349 Report.Error (591, Location, "Invalid value for argument to 'System.AttributeUsage' attribute");
352 usage_attribute = new AttributeUsageAttribute ((AttributeTargets)val);
353 } else if (MethodImplAttr) {
354 this.ImplOptions = (MethodImplOptions) val;
355 } else if (GuidAttr){
357 // we will later check the validity of the type
360 if (!ValidateGuid ((string) val))
364 } else if (MarshalAsAttr)
366 (System.Runtime.InteropServices.UnmanagedType) val;
371 // Now process named arguments
374 ArrayList field_infos = null;
375 ArrayList prop_infos = null;
376 ArrayList field_values = null;
377 ArrayList prop_values = null;
379 if (named_args.Count > 0) {
380 field_infos = new ArrayList ();
381 prop_infos = new ArrayList ();
382 field_values = new ArrayList ();
383 prop_values = new ArrayList ();
386 Hashtable seen_names = new Hashtable();
388 for (i = 0; i < named_args.Count; i++) {
389 DictionaryEntry de = (DictionaryEntry) named_args [i];
390 string member_name = (string) de.Key;
391 Argument a = (Argument) de.Value;
394 if (seen_names.Contains(member_name)) {
395 Report.Error(643, Location, "'" + member_name + "' duplicate named attribute argument");
398 seen_names.Add(member_name, 1);
400 if (!a.Resolve (ec, Location))
403 Expression member = Expression.MemberLookup (
404 ec, Type, member_name,
405 MemberTypes.Field | MemberTypes.Property,
406 BindingFlags.Public | BindingFlags.Instance,
409 if (member == null) {
410 member = Expression.MemberLookup (ec, Type, member_name,
411 MemberTypes.Field | MemberTypes.Property, BindingFlags.NonPublic | BindingFlags.Instance,
414 if (member != null) {
415 Report.Error (122, Location, "'{0}' is inaccessible due to its protection level", GetFullMemberName (member_name));
421 Report.Error (117, Location, "Attribute `{0}' does not contain a definition for `{1}'",
426 if (!(member is PropertyExpr || member is FieldExpr)) {
427 Error_InvalidNamedArgument (member_name);
432 if (member is PropertyExpr) {
433 PropertyExpr pe = (PropertyExpr) member;
434 PropertyInfo pi = pe.PropertyInfo;
436 if (!pi.CanWrite || !pi.CanRead) {
437 Report.SymbolRelatedToPreviousError (pi);
438 Error_InvalidNamedArgument (member_name);
443 if (!GetAttributeArgumentExpression (e, Location, pi.PropertyType, out value))
446 if (usage_attribute != null) {
447 if (member_name == "AllowMultiple")
448 usage_attribute.AllowMultiple = (bool) value;
449 if (member_name == "Inherited")
450 usage_attribute.Inherited = (bool) value;
453 prop_values.Add (value);
456 } else if (member is FieldExpr) {
457 FieldExpr fe = (FieldExpr) member;
458 FieldInfo fi = fe.FieldInfo;
461 Error_InvalidNamedArgument (member_name);
466 if (!GetAttributeArgumentExpression (e, Location, fi.FieldType, out value))
469 field_values.Add (value);
470 field_infos.Add (fi);
474 Expression mg = Expression.MemberLookup (
475 ec, Type, ".ctor", MemberTypes.Constructor,
476 BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly,
480 Error_AttributeConstructorMismatch ();
484 MethodBase constructor = Invocation.OverloadResolve (
485 ec, (MethodGroupExpr) mg, pos_args, false, Location);
487 if (constructor == null) {
492 // Now we perform some checks on the positional args as they
493 // cannot be null for a constructor which expects a parameter
497 ParameterData pd = Invocation.GetParameterData (constructor);
499 int last_real_param = pd.Count;
501 // When the params is not filled we need to put one
502 if (last_real_param > pos_arg_count) {
503 object [] new_pos_values = new object [pos_arg_count + 1];
504 pos_values.CopyTo (new_pos_values, 0);
505 new_pos_values [pos_arg_count] = new object [] {} ;
506 pos_values = new_pos_values;
511 for (int j = 0; j < pos_arg_count; ++j) {
512 Argument a = (Argument) pos_args [j];
514 if (a.Expr is NullLiteral && pd.ParameterType (j) == TypeManager.object_type) {
515 Error_AttributeArgumentNotValid (Location);
519 if (j < last_real_param)
522 if (j == last_real_param) {
523 object [] array = new object [pos_arg_count - last_real_param];
524 array [0] = pos_values [j];
525 pos_values [j] = array;
529 object [] params_array = (object []) pos_values [last_real_param];
530 params_array [j - last_real_param] = pos_values [j];
533 // Adjust the size of the pos_values if it had params
534 if (last_real_param != pos_arg_count) {
535 object [] new_pos_values = new object [last_real_param + 1];
536 Array.Copy (pos_values, new_pos_values, last_real_param + 1);
537 pos_values = new_pos_values;
541 if (named_args.Count > 0) {
542 prop_info_arr = new PropertyInfo [prop_infos.Count];
543 field_info_arr = new FieldInfo [field_infos.Count];
544 field_values_arr = new object [field_values.Count];
545 prop_values_arr = new object [prop_values.Count];
547 field_infos.CopyTo (field_info_arr, 0);
548 field_values.CopyTo (field_values_arr, 0);
550 prop_values.CopyTo (prop_values_arr, 0);
551 prop_infos.CopyTo (prop_info_arr, 0);
553 cb = new CustomAttributeBuilder (
554 (ConstructorInfo) constructor, pos_values,
555 prop_info_arr, prop_values_arr,
556 field_info_arr, field_values_arr);
559 cb = new CustomAttributeBuilder (
560 (ConstructorInfo) constructor, pos_values);
561 } catch (NullReferenceException) {
563 // Don't know what to do here
566 -101, Location, "NullReferenceException while trying to create attribute." +
567 "Something's wrong!");
568 } catch (Exception e) {
571 // using System.ComponentModel;
572 // [DefaultValue (CollectionChangeAction.Add)]
573 // class X { static void Main () {} }
576 -23, Location, "The compiler can not encode this attribute in .NET due to a bug in the .NET runtime. Try the Mono runtime. The exception was: " + e.Message);
583 /// Get a string containing a list of valid targets for the attribute 'attr'
585 public string GetValidTargets ()
587 StringBuilder sb = new StringBuilder ();
588 AttributeTargets targets = GetAttributeUsage ().ValidOn;
590 if ((targets & AttributeTargets.Assembly) != 0)
591 sb.Append ("'assembly' ");
593 if ((targets & AttributeTargets.Class) != 0)
594 sb.Append ("'class' ");
596 if ((targets & AttributeTargets.Constructor) != 0)
597 sb.Append ("'constructor' ");
599 if ((targets & AttributeTargets.Delegate) != 0)
600 sb.Append ("'delegate' ");
602 if ((targets & AttributeTargets.Enum) != 0)
603 sb.Append ("'enum' ");
605 if ((targets & AttributeTargets.Event) != 0)
606 sb.Append ("'event' ");
608 if ((targets & AttributeTargets.Field) != 0)
609 sb.Append ("'field' ");
611 if ((targets & AttributeTargets.Interface) != 0)
612 sb.Append ("'interface' ");
614 if ((targets & AttributeTargets.Method) != 0)
615 sb.Append ("'method' ");
617 if ((targets & AttributeTargets.Module) != 0)
618 sb.Append ("'module' ");
620 if ((targets & AttributeTargets.Parameter) != 0)
621 sb.Append ("'parameter' ");
623 if ((targets & AttributeTargets.Property) != 0)
624 sb.Append ("'property' ");
626 if ((targets & AttributeTargets.ReturnValue) != 0)
627 sb.Append ("'return' ");
629 if ((targets & AttributeTargets.Struct) != 0)
630 sb.Append ("'struct' ");
632 return sb.ToString ();
637 /// Returns AttributeUsage attribute for this type
639 public AttributeUsageAttribute GetAttributeUsage ()
641 AttributeUsageAttribute ua = usage_attr_cache [Type] as AttributeUsageAttribute;
645 Class attr_class = TypeManager.LookupClass (Type);
647 if (attr_class == null) {
648 object[] usage_attr = Type.GetCustomAttributes (TypeManager.attribute_usage_type, true);
649 ua = (AttributeUsageAttribute)usage_attr [0];
650 usage_attr_cache.Add (Type, ua);
654 return attr_class.AttributeUsage;
658 /// Returns custom name of indexer
660 public string GetIndexerAttributeValue (EmitContext ec)
662 if (pos_values == null) {
663 // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args.
664 // But because a lot of attribute class code must be rewritten will be better to wait...
668 return pos_values [0] as string;
672 /// Returns condition of ConditionalAttribute
674 public string GetConditionalAttributeValue (DeclSpace ds)
676 if (pos_values == null) {
677 EmitContext ec = new EmitContext (ds, ds, Location, null, null, 0, false);
679 // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args.
680 // But because a lot of attribute class code must be rewritten will be better to wait...
684 // Some error occurred
685 if (pos_values [0] == null)
688 return (string)pos_values [0];
692 /// Creates the instance of ObsoleteAttribute from this attribute instance
694 public ObsoleteAttribute GetObsoleteAttribute (DeclSpace ds)
696 if (pos_values == null) {
697 EmitContext ec = new EmitContext (ds, ds, Location, null, null, 0, false);
699 // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args.
700 // But because a lot of attribute class code must be rewritten will be better to wait...
704 // Some error occurred
705 if (pos_values == null)
708 if (pos_values.Length == 0)
709 return new ObsoleteAttribute ();
711 if (pos_values.Length == 1)
712 return new ObsoleteAttribute ((string)pos_values [0]);
714 return new ObsoleteAttribute ((string)pos_values [0], (bool)pos_values [1]);
718 /// Returns value of CLSCompliantAttribute contructor parameter but because the method can be called
719 /// before ApplyAttribute. We need to resolve the arguments.
720 /// This situation occurs when class deps is differs from Emit order.
722 public bool GetClsCompliantAttributeValue (DeclSpace ds)
724 if (pos_values == null) {
725 EmitContext ec = new EmitContext (ds, ds, Location, null, null, 0, false);
727 // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args.
728 // But because a lot of attribute class code must be rewritten will be better to wait...
732 // Some error occurred
733 if (pos_values [0] == null)
736 return (bool)pos_values [0];
740 /// Tests permitted SecurityAction for assembly or other types
742 public bool CheckSecurityActionValidity (bool for_assembly)
744 SecurityAction action = GetSecurityActionValue ();
746 if ((action == SecurityAction.RequestMinimum || action == SecurityAction.RequestOptional || action == SecurityAction.RequestRefuse) && for_assembly)
750 if (action < SecurityAction.Demand || action > SecurityAction.InheritanceDemand) {
751 Error_AttributeEmitError ("SecurityAction is out of range");
755 if ((action != SecurityAction.RequestMinimum && action != SecurityAction.RequestOptional && action != SecurityAction.RequestRefuse) && !for_assembly)
759 Error_AttributeEmitError (String.Concat ("SecurityAction '", action, "' is not valid for this declaration"));
763 System.Security.Permissions.SecurityAction GetSecurityActionValue ()
765 return (SecurityAction)pos_values [0];
769 /// Creates instance of SecurityAttribute class and add result of CreatePermission method to permission table.
771 /// <returns></returns>
772 public void ExtractSecurityPermissionSet (ListDictionary permissions)
774 if (TypeManager.LookupDeclSpace (Type) != null && RootContext.StdLib) {
775 Error_AttributeEmitError ("security custom attributes can not be referenced from defining assembly");
779 SecurityAttribute sa;
780 // For all assemblies except corlib we can avoid all hacks
781 if (RootContext.StdLib) {
782 sa = (SecurityAttribute) Activator.CreateInstance (Type, pos_values);
784 if (prop_info_arr != null) {
785 for (int i = 0; i < prop_info_arr.Length; ++i) {
786 PropertyInfo pi = prop_info_arr [i];
787 pi.SetValue (sa, prop_values_arr [i], null);
791 Type temp_type = Type.GetType (Type.FullName);
792 // HACK: All mscorlib attributes have same ctor syntax
793 sa = (SecurityAttribute) Activator.CreateInstance (temp_type, new object[] { GetSecurityActionValue () } );
795 // All types are from newly created corlib but for invocation with old we need to convert them
796 if (prop_info_arr != null) {
797 for (int i = 0; i < prop_info_arr.Length; ++i) {
798 PropertyInfo emited_pi = prop_info_arr [i];
799 PropertyInfo pi = temp_type.GetProperty (emited_pi.Name, emited_pi.PropertyType);
801 object old_instance = pi.PropertyType.IsEnum ?
802 System.Enum.ToObject (pi.PropertyType, prop_values_arr [i]) :
805 pi.SetValue (sa, old_instance, null);
810 IPermission perm = sa.CreatePermission ();
811 SecurityAction action;
813 // IS is correct because for corlib we are using an instance from old corlib
814 if (perm is System.Security.CodeAccessPermission) {
815 action = GetSecurityActionValue ();
817 switch (GetSecurityActionValue ()) {
818 case SecurityAction.Demand:
819 action = (SecurityAction)13;
821 case SecurityAction.LinkDemand:
822 action = (SecurityAction)14;
824 case SecurityAction.InheritanceDemand:
825 action = (SecurityAction)15;
828 Error_AttributeEmitError ("Invalid SecurityAction for non-Code Access Security permission");
833 PermissionSet ps = (PermissionSet)permissions [action];
835 ps = new PermissionSet (PermissionState.None);
836 permissions.Add (action, ps);
838 ps.AddPermission (sa.CreatePermission ());
841 object GetValue (object value)
843 if (value is EnumConstant)
844 return ((EnumConstant) value).GetValue ();
849 public object GetPositionalValue (int i)
851 return (pos_values == null) ? null : pos_values[i];
854 object GetFieldValue (string name)
857 if (field_info_arr == null)
860 foreach (FieldInfo fi in field_info_arr) {
862 return GetValue (field_values_arr [i]);
868 public UnmanagedMarshal GetMarshal (Attributable attr)
870 object value = GetFieldValue ("SizeParamIndex");
871 if (value != null && UnmanagedType != UnmanagedType.LPArray) {
872 Error_AttributeEmitError ("SizeParamIndex field is not valid for the specified unmanaged type");
876 object o = GetFieldValue ("ArraySubType");
877 UnmanagedType array_sub_type = o == null ? UnmanagedType.I4 : (UnmanagedType) o;
879 switch (UnmanagedType) {
880 case UnmanagedType.CustomMarshaler:
881 MethodInfo define_custom = typeof (UnmanagedMarshal).GetMethod ("DefineCustom",
882 BindingFlags.Static | BindingFlags.Public);
883 if (define_custom == null) {
884 Report.RuntimeMissingSupport (Location, "set marshal info");
888 object [] args = new object [4];
889 args [0] = GetFieldValue ("MarshalTypeRef");
890 args [1] = GetFieldValue ("MarshalCookie");
891 args [2] = GetFieldValue ("MarshalType");
892 args [3] = Guid.Empty;
893 return (UnmanagedMarshal) define_custom.Invoke (null, args);
895 case UnmanagedType.LPArray:
896 return UnmanagedMarshal.DefineLPArray (array_sub_type);
898 case UnmanagedType.SafeArray:
899 return UnmanagedMarshal.DefineSafeArray (array_sub_type);
901 case UnmanagedType.ByValArray:
902 FieldMember fm = attr as FieldMember;
904 Error_AttributeEmitError ("Specified unmanaged type is only valid on fields");
907 return UnmanagedMarshal.DefineByValArray ((int) GetFieldValue ("SizeConst"));
909 case UnmanagedType.ByValTStr:
910 return UnmanagedMarshal.DefineByValTStr ((int) GetFieldValue ("SizeConst"));
913 return UnmanagedMarshal.DefineUnmanagedMarshal (UnmanagedType);
917 public bool IsInternalCall
919 get { return ImplOptions == MethodImplOptions.InternalCall; }
923 /// Emit attribute for Attributable symbol
925 public void Emit (EmitContext ec, Attributable ias, ListDictionary emitted_attr)
927 CustomAttributeBuilder cb = Resolve (ec);
931 AttributeUsageAttribute usage_attr = GetAttributeUsage ();
932 if ((usage_attr.ValidOn & Target) == 0) {
933 Report.Error (592, Location, "Attribute '{0}' is not valid on this declaration type. It is valid on {1} declarations only.", Name, GetValidTargets ());
937 ias.ApplyAttributeBuilder (this, cb);
939 if (!usage_attr.AllowMultiple) {
940 ArrayList emitted_targets = (ArrayList)emitted_attr [Type];
941 if (emitted_targets == null) {
942 emitted_targets = new ArrayList ();
943 emitted_attr.Add (Type, emitted_targets);
944 } else if (emitted_targets.Contains (Target)) {
945 Report.Error (579, Location, "Duplicate '" + Name + "' attribute");
948 emitted_targets.Add (Target);
951 if (!RootContext.VerifyClsCompliance)
954 // Here we are testing attribute arguments for array usage (error 3016)
955 if (ias.IsClsCompliaceRequired (ec.DeclSpace)) {
956 if (Arguments == null)
959 ArrayList pos_args = (ArrayList) Arguments [0];
960 if (pos_args != null) {
961 foreach (Argument arg in pos_args) {
962 // Type is undefined (was error 246)
963 if (arg.Type == null)
966 if (arg.Type.IsArray) {
967 Report.Error (3016, Location, "Arrays as attribute arguments are not CLS-compliant");
973 if (Arguments.Count < 2)
976 ArrayList named_args = (ArrayList) Arguments [1];
977 foreach (DictionaryEntry de in named_args) {
978 Argument arg = (Argument) de.Value;
980 // Type is undefined (was error 246)
981 if (arg.Type == null)
984 if (arg.Type.IsArray) {
985 Report.Error (3016, Location, "Arrays as attribute arguments are not CLS-compliant");
992 public object GetValue (EmitContext ec, Constant c, Type target)
994 if (Convert.ImplicitConversionExists (ec, c, target))
995 return c.GetValue ();
997 Convert.Error_CannotImplicitConversion (Location, c.Type, target);
1001 public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name,
1002 MethodAttributes flags, Type ret_type, Type [] param_types)
1005 // We extract from the attribute the information we need
1008 if (Arguments == null) {
1009 Console.WriteLine ("Internal error : this is not supposed to happen !");
1017 ArrayList named_args = new ArrayList ();
1019 ArrayList pos_args = (ArrayList) Arguments [0];
1020 if (Arguments.Count > 1)
1021 named_args = (ArrayList) Arguments [1];
1024 string dll_name = null;
1026 Argument tmp = (Argument) pos_args [0];
1028 if (!tmp.Resolve (ec, Location))
1031 if (tmp.Expr is Constant)
1032 dll_name = (string) ((Constant) tmp.Expr).GetValue ();
1034 Error_AttributeArgumentNotValid (Location);
1038 // Now we process the named arguments
1039 CallingConvention cc = CallingConvention.Winapi;
1040 CharSet charset = CharSet.Ansi;
1041 bool preserve_sig = true;
1043 bool exact_spelling = false;
1045 bool set_last_err = false;
1046 string entry_point = null;
1048 for (int i = 0; i < named_args.Count; i++) {
1050 DictionaryEntry de = (DictionaryEntry) named_args [i];
1052 string member_name = (string) de.Key;
1053 Argument a = (Argument) de.Value;
1055 if (!a.Resolve (ec, Location))
1058 Expression member = Expression.MemberLookup (
1059 ec, Type, member_name,
1060 MemberTypes.Field | MemberTypes.Property,
1061 BindingFlags.Public | BindingFlags.Instance,
1064 if (member == null || !(member is FieldExpr)) {
1065 Error_InvalidNamedArgument (member_name);
1069 if (member is FieldExpr) {
1070 FieldExpr fe = (FieldExpr) member;
1071 FieldInfo fi = fe.FieldInfo;
1073 if (fi.IsInitOnly) {
1074 Error_InvalidNamedArgument (member_name);
1078 if (a.Expr is Constant) {
1079 Constant c = (Constant) a.Expr;
1082 if (member_name == "CallingConvention"){
1083 object val = GetValue (ec, c, typeof (CallingConvention));
1086 cc = (CallingConvention) val;
1087 } else if (member_name == "CharSet"){
1088 charset = (CharSet) c.GetValue ();
1089 } else if (member_name == "EntryPoint")
1090 entry_point = (string) c.GetValue ();
1091 else if (member_name == "SetLastError")
1092 set_last_err = (bool) c.GetValue ();
1094 else if (member_name == "ExactSpelling")
1095 exact_spelling = (bool) c.GetValue ();
1097 else if (member_name == "PreserveSig")
1098 preserve_sig = (bool) c.GetValue ();
1099 } catch (InvalidCastException){
1100 Error_InvalidNamedArgument (member_name);
1101 Error_AttributeArgumentNotValid (Location);
1104 Error_AttributeArgumentNotValid (Location);
1111 if (entry_point == null)
1114 charset = (CharSet)((int)charset | 0x40);
1116 MethodBuilder mb = builder.DefinePInvokeMethod (
1117 name, dll_name, entry_point, flags | MethodAttributes.HideBySig,
1118 CallingConventions.Standard,
1125 mb.SetImplementationFlags (MethodImplAttributes.PreserveSig);
1130 private Expression GetValue ()
1132 if ((Arguments == null) || (Arguments.Count < 1))
1134 ArrayList al = (ArrayList) Arguments [0];
1135 if ((al == null) || (al.Count < 1))
1137 Argument arg = (Argument) al [0];
1138 if ((arg == null) || (arg.Expr == null))
1143 public string GetString ()
1145 Expression e = GetValue ();
1146 if (e is StringLiteral)
1147 return (e as StringLiteral).Value;
1151 public bool GetBoolean ()
1153 Expression e = GetValue ();
1154 if (e is BoolLiteral)
1155 return (e as BoolLiteral).Value;
1162 /// For global attributes (assembly, module) we need special handling.
1163 /// Attributes can be located in the several files
1165 public class GlobalAttribute: Attribute
1167 public readonly NamespaceEntry ns;
1169 public GlobalAttribute (TypeContainer container, string target, string name, ArrayList args, Location loc):
1170 base (target, name, args, loc)
1172 ns = container.NamespaceEntry;
1175 protected override Type CheckAttributeType (EmitContext ec)
1177 // RootContext.Tree.Types has a single NamespaceEntry which gets overwritten
1178 // each time a new file is parsed. However, we need to use the NamespaceEntry
1179 // in effect where the attribute was used. Since code elsewhere cannot assume
1180 // that the NamespaceEntry is right, just overwrite it.
1182 // FIXME: Check every place the NamespaceEntry of RootContext.Tree.Types is used
1183 // to ensure the right one is used.
1184 if (ec.DeclSpace == RootContext.Tree.Types)
1185 ec.DeclSpace.NamespaceEntry = ns;
1187 return base.CheckAttributeType (ec);
1191 public class Attributes {
1192 public ArrayList Attrs;
1194 public Attributes (Attribute a)
1196 Attrs = new ArrayList ();
1200 public Attributes (ArrayList attrs)
1205 public void AddAttributes (ArrayList attrs)
1207 Attrs.AddRange (attrs);
1211 /// Checks whether attribute target is valid for the current element
1213 public bool CheckTargets (Attributable member)
1215 string[] valid_targets = member.ValidAttributeTargets;
1216 foreach (Attribute a in Attrs) {
1217 if (a.ExplicitTarget == null || a.ExplicitTarget == valid_targets [0]) {
1218 a.Target = member.AttributeTargets;
1222 // TODO: we can skip the first item
1223 if (((IList) valid_targets).Contains (a.ExplicitTarget)) {
1224 switch (a.ExplicitTarget) {
1225 case "return": a.Target = AttributeTargets.ReturnValue; continue;
1226 case "param": a.Target = AttributeTargets.Parameter; continue;
1227 case "field": a.Target = AttributeTargets.Field; continue;
1228 case "method": a.Target = AttributeTargets.Method; continue;
1229 case "property": a.Target = AttributeTargets.Property; continue;
1231 throw new InternalErrorException ("Unknown explicit target: " + a.ExplicitTarget);
1234 StringBuilder sb = new StringBuilder ();
1235 foreach (string s in valid_targets) {
1239 sb.Remove (sb.Length - 2, 2);
1240 Report.Error (657, a.Location, "'{0}' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are '{1}'", a.ExplicitTarget, sb.ToString ());
1246 public Attribute Search (Type t, EmitContext ec)
1248 foreach (Attribute a in Attrs) {
1249 if (a.ResolveType (ec) == t)
1256 /// Returns all attributes of type 't'. Use it when attribute is AllowMultiple = true
1258 public Attribute[] SearchMulti (Type t, EmitContext ec)
1260 ArrayList ar = null;
1262 foreach (Attribute a in Attrs) {
1263 if (a.ResolveType (ec) == t) {
1265 ar = new ArrayList ();
1270 return ar == null ? null : ar.ToArray (typeof (Attribute)) as Attribute[];
1273 public void Emit (EmitContext ec, Attributable ias)
1275 if (!CheckTargets (ias))
1278 ListDictionary ld = new ListDictionary ();
1280 foreach (Attribute a in Attrs)
1281 a.Emit (ec, ias, ld);
1284 public bool Contains (Type t, EmitContext ec)
1286 return Search (t, ec) != null;
1291 /// Helper class for attribute verification routine.
1293 sealed class AttributeTester
1295 static PtrHashtable analyzed_types = new PtrHashtable ();
1296 static PtrHashtable analyzed_types_obsolete = new PtrHashtable ();
1297 static PtrHashtable analyzed_member_obsolete = new PtrHashtable ();
1298 static PtrHashtable analyzed_method_excluded = new PtrHashtable ();
1300 private AttributeTester ()
1305 /// Returns true if parameters of two compared methods are CLS-Compliant.
1306 /// It tests differing only in ref or out, or in array rank.
1308 public static bool AreOverloadedMethodParamsClsCompliant (Type[] types_a, Type[] types_b)
1310 if (types_a == null || types_b == null)
1313 if (types_a.Length != types_b.Length)
1316 for (int i = 0; i < types_b.Length; ++i) {
1317 Type aType = types_a [i];
1318 Type bType = types_b [i];
1320 if (aType.IsArray && bType.IsArray && aType.GetArrayRank () != bType.GetArrayRank () && aType.GetElementType () == bType.GetElementType ()) {
1324 Type aBaseType = aType;
1325 bool is_either_ref_or_out = false;
1327 if (aType.IsByRef || aType.IsPointer) {
1328 aBaseType = aType.GetElementType ();
1329 is_either_ref_or_out = true;
1332 Type bBaseType = bType;
1333 if (bType.IsByRef || bType.IsPointer)
1335 bBaseType = bType.GetElementType ();
1336 is_either_ref_or_out = !is_either_ref_or_out;
1339 if (aBaseType != bBaseType)
1342 if (is_either_ref_or_out)
1349 /// Goes through all parameters and test if they are CLS-Compliant.
1351 public static bool AreParametersCompliant (Parameter[] fixedParameters, Location loc)
1353 if (fixedParameters == null)
1356 foreach (Parameter arg in fixedParameters) {
1357 if (!AttributeTester.IsClsCompliant (arg.ParameterType)) {
1358 Report.Error (3001, loc, "Argument type '{0}' is not CLS-compliant", arg.GetSignatureForError ());
1367 /// This method tests the CLS compliance of external types. It doesn't test type visibility.
1369 public static bool IsClsCompliant (Type type)
1374 object type_compliance = analyzed_types[type];
1375 if (type_compliance != null)
1376 return type_compliance == TRUE;
1378 if (type.IsPointer) {
1379 analyzed_types.Add (type, null);
1384 if (type.IsArray || type.IsByRef) {
1385 result = IsClsCompliant (TypeManager.GetElementType (type));
1387 result = AnalyzeTypeCompliance (type);
1389 analyzed_types.Add (type, result ? TRUE : FALSE);
1393 static object TRUE = new object ();
1394 static object FALSE = new object ();
1396 public static void VerifyModulesClsCompliance ()
1398 Module[] modules = TypeManager.Modules;
1399 if (modules == null)
1402 // The first module is generated assembly
1403 for (int i = 1; i < modules.Length; ++i) {
1404 Module module = modules [i];
1405 if (!IsClsCompliant (module)) {
1406 Report.Error (3013, "Added modules must be marked with the CLSCompliant attribute to match the assembly", module.Name);
1413 /// Tests container name for CLS-Compliant name (differing only in case)
1415 public static void VerifyTopLevelNameClsCompliance ()
1417 Hashtable locase_table = new Hashtable ();
1419 // Convert imported type names to lower case and ignore not cls compliant
1420 foreach (DictionaryEntry de in TypeManager.all_imported_types) {
1421 Type t = (Type)de.Value;
1422 if (!AttributeTester.IsClsCompliant (t))
1425 locase_table.Add (((string)de.Key).ToLower (System.Globalization.CultureInfo.InvariantCulture), t);
1428 foreach (DictionaryEntry de in RootContext.Tree.Decls) {
1429 DeclSpace decl = (DeclSpace)de.Value;
1430 if (!decl.IsClsCompliaceRequired (decl))
1433 string lcase = decl.Name.ToLower (System.Globalization.CultureInfo.InvariantCulture);
1434 if (!locase_table.Contains (lcase)) {
1435 locase_table.Add (lcase, decl);
1439 object conflict = locase_table [lcase];
1440 if (conflict is Type)
1441 Report.SymbolRelatedToPreviousError ((Type)conflict);
1443 Report.SymbolRelatedToPreviousError ((MemberCore)conflict);
1445 Report.Error (3005, decl.Location, "Identifier '{0}' differing only in case is not CLS-compliant", decl.GetSignatureForError ());
1449 static bool IsClsCompliant (ICustomAttributeProvider attribute_provider)
1451 object[] CompliantAttribute = attribute_provider.GetCustomAttributes (TypeManager.cls_compliant_attribute_type, false);
1452 if (CompliantAttribute.Length == 0)
1455 return ((CLSCompliantAttribute)CompliantAttribute[0]).IsCompliant;
1458 static bool AnalyzeTypeCompliance (Type type)
1460 DeclSpace ds = TypeManager.LookupDeclSpace (type);
1462 return ds.IsClsCompliaceRequired (ds.Parent);
1465 object[] CompliantAttribute = type.GetCustomAttributes (TypeManager.cls_compliant_attribute_type, false);
1466 if (CompliantAttribute.Length == 0)
1467 return IsClsCompliant (type.Assembly);
1469 return ((CLSCompliantAttribute)CompliantAttribute[0]).IsCompliant;
1473 /// Returns instance of ObsoleteAttribute when type is obsolete
1475 public static ObsoleteAttribute GetObsoleteAttribute (Type type)
1477 object type_obsolete = analyzed_types_obsolete [type];
1478 if (type_obsolete == FALSE)
1481 if (type_obsolete != null)
1482 return (ObsoleteAttribute)type_obsolete;
1484 ObsoleteAttribute result = null;
1485 if (type.IsByRef || type.IsArray || type.IsPointer) {
1486 result = GetObsoleteAttribute (TypeManager.GetElementType (type));
1488 DeclSpace type_ds = TypeManager.LookupDeclSpace (type);
1490 // Type is external, we can get attribute directly
1491 if (type_ds == null) {
1492 object[] attribute = type.GetCustomAttributes (TypeManager.obsolete_attribute_type, false);
1493 if (attribute.Length == 1)
1494 result = (ObsoleteAttribute)attribute [0];
1496 result = type_ds.GetObsoleteAttribute (type_ds);
1500 analyzed_types_obsolete.Add (type, result == null ? FALSE : result);
1505 /// Returns instance of ObsoleteAttribute when method is obsolete
1507 public static ObsoleteAttribute GetMethodObsoleteAttribute (MethodBase mb)
1509 IMethodData mc = TypeManager.GetMethod (mb);
1511 return mc.GetObsoleteAttribute ();
1513 // compiler generated methods are not registered by AddMethod
1514 if (mb.DeclaringType is TypeBuilder)
1517 return GetMemberObsoleteAttribute (mb);
1521 /// Returns instance of ObsoleteAttribute when member is obsolete
1523 public static ObsoleteAttribute GetMemberObsoleteAttribute (MemberInfo mi)
1525 object type_obsolete = analyzed_member_obsolete [mi];
1526 if (type_obsolete == FALSE)
1529 if (type_obsolete != null)
1530 return (ObsoleteAttribute)type_obsolete;
1532 ObsoleteAttribute oa = System.Attribute.GetCustomAttribute (mi, TypeManager.obsolete_attribute_type, false) as ObsoleteAttribute;
1533 analyzed_member_obsolete.Add (mi, oa == null ? FALSE : oa);
1538 /// Common method for Obsolete error/warning reporting.
1540 public static void Report_ObsoleteMessage (ObsoleteAttribute oa, string member, Location loc)
1543 Report.Error (619, loc, "'{0}' is obsolete: '{1}'", member, oa.Message);
1547 if (oa.Message == null) {
1548 Report.Warning (612, loc, "'{0}' is obsolete", member);
1551 if (RootContext.WarningLevel >= 2)
1552 Report.Warning (618, loc, "'{0}' is obsolete: '{1}'", member, oa.Message);
1555 public static bool IsConditionalMethodExcluded (MethodBase mb)
1557 object excluded = analyzed_method_excluded [mb];
1558 if (excluded != null)
1559 return excluded == TRUE ? true : false;
1561 ConditionalAttribute[] attrs = mb.GetCustomAttributes (TypeManager.conditional_attribute_type, true) as ConditionalAttribute[];
1562 if (attrs.Length == 0) {
1563 analyzed_method_excluded.Add (mb, FALSE);
1567 foreach (ConditionalAttribute a in attrs) {
1568 if (RootContext.AllDefines.Contains (a.ConditionString)) {
1569 analyzed_method_excluded.Add (mb, FALSE);
1573 analyzed_method_excluded.Add (mb, TRUE);