2 // attribute.cs: Attribute Handler
4 // Author: Ravi Pratap (ravi@ximian.com)
6 // Licensed under the terms of the GNU GPL
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)
13 using System.Diagnostics;
14 using System.Collections;
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Runtime.InteropServices;
18 using System.Runtime.CompilerServices;
21 namespace Mono.CSharp {
23 public class Attribute {
24 public readonly string Name;
25 public readonly ArrayList Arguments;
32 // The following are only meaningful when the attribute
33 // being emitted is one of the builtin ones
35 AttributeTargets Targets;
39 bool UsageAttr = false;
41 MethodImplOptions ImplOptions;
42 UnmanagedType UnmanagedType;
43 CustomAttributeBuilder cb;
45 public Attribute (string name, ArrayList args, Location loc)
52 void Error_InvalidNamedArgument (string name)
54 Report.Error (617, Location, "'" + name + "' is not a valid named attribute " +
55 "argument. Named attribute arguments must be fields which are not " +
56 "readonly, static or const, or properties with a set accessor which "+
60 void Error_AttributeArgumentNotValid ()
62 Report.Error (182, Location,
63 "An attribute argument must be a constant expression, typeof " +
64 "expression or array creation expression");
67 static void Error_AttributeConstructorMismatch (Location loc)
71 "Could not find a constructor for this argument list.");
74 private Type CheckAttributeType (EmitContext ec) {
76 bool isattributeclass = true;
78 t = RootContext.LookupType (ec.DeclSpace, Name, true, Location);
80 isattributeclass = t.IsSubclassOf (TypeManager.attribute_type);
84 t = RootContext.LookupType (ec.DeclSpace, Name + "Attribute", true, Location);
86 if (t.IsSubclassOf (TypeManager.attribute_type))
89 if (!isattributeclass) {
90 Report.Error (616, Location, "'" + Name + "': is not an attribute class");
94 Report.Error (616, Location, "'" + Name + "Attribute': is not an attribute class");
98 246, Location, "Could not find attribute '" + Name + "' (are you" +
99 " missing a using directive or an assembly reference ?)");
103 public Type ResolveType (EmitContext ec)
105 Type = CheckAttributeType (ec);
110 public CustomAttributeBuilder Resolve (EmitContext ec)
113 Type = CheckAttributeType (ec);
117 bool MethodImplAttr = false;
118 bool MarshalAsAttr = false;
122 if (Type == TypeManager.attribute_usage_type)
124 if (Type == TypeManager.methodimpl_attr_type)
125 MethodImplAttr = true;
126 if (Type == TypeManager.marshal_as_attr_type)
127 MarshalAsAttr = true;
129 // Now we extract the positional and named arguments
131 ArrayList pos_args = new ArrayList ();
132 ArrayList named_args = new ArrayList ();
133 int pos_arg_count = 0;
135 if (Arguments != null) {
136 pos_args = (ArrayList) Arguments [0];
137 if (pos_args != null)
138 pos_arg_count = pos_args.Count;
139 if (Arguments.Count > 1)
140 named_args = (ArrayList) Arguments [1];
143 object [] pos_values = new object [pos_arg_count];
146 // First process positional arguments
150 for (i = 0; i < pos_arg_count; i++) {
151 Argument a = (Argument) pos_args [i];
154 if (!a.Resolve (ec, Location))
160 pos_values [i] = ((Constant) e).GetValue ();
161 } else if (e is TypeOf) {
162 pos_values [i] = ((TypeOf) e).TypeArg;
164 Error_AttributeArgumentNotValid ();
169 this.Targets = (AttributeTargets) pos_values [0];
172 this.ImplOptions = (MethodImplOptions) pos_values [0];
176 (System.Runtime.InteropServices.UnmanagedType) pos_values [0];
180 // Now process named arguments
183 ArrayList field_infos = new ArrayList ();
184 ArrayList prop_infos = new ArrayList ();
185 ArrayList field_values = new ArrayList ();
186 ArrayList prop_values = new ArrayList ();
188 for (i = 0; i < named_args.Count; i++) {
189 DictionaryEntry de = (DictionaryEntry) named_args [i];
190 string member_name = (string) de.Key;
191 Argument a = (Argument) de.Value;
194 if (!a.Resolve (ec, Location))
197 Expression member = Expression.MemberLookup (
198 ec, Type, member_name,
199 MemberTypes.Field | MemberTypes.Property,
200 BindingFlags.Public | BindingFlags.Instance,
203 if (member == null || !(member is PropertyExpr || member is FieldExpr)) {
204 Error_InvalidNamedArgument (member_name);
209 if (member is PropertyExpr) {
210 PropertyExpr pe = (PropertyExpr) member;
211 PropertyInfo pi = pe.PropertyInfo;
214 Error_InvalidNamedArgument (member_name);
219 object o = ((Constant) e).GetValue ();
223 if (member_name == "AllowMultiple")
224 this.AllowMultiple = (bool) o;
225 if (member_name == "Inherited")
226 this.Inherited = (bool) o;
229 } else if (e is TypeOf) {
230 prop_values.Add (((TypeOf) e).TypeArg);
232 Error_AttributeArgumentNotValid ();
238 } else if (member is FieldExpr) {
239 FieldExpr fe = (FieldExpr) member;
240 FieldInfo fi = fe.FieldInfo;
243 Error_InvalidNamedArgument (member_name);
248 // Handle charset here, and set the TypeAttributes
251 object value = ((Constant) e).GetValue ();
253 field_values.Add (value);
254 } else if (e is TypeOf) {
255 field_values.Add (((TypeOf) e).TypeArg);
257 Error_AttributeArgumentNotValid ();
261 field_infos.Add (fi);
265 Expression mg = Expression.MemberLookup (
266 ec, Type, ".ctor", MemberTypes.Constructor,
267 BindingFlags.Public | BindingFlags.Instance, Location);
270 Error_AttributeConstructorMismatch (Location);
274 MethodBase constructor = Invocation.OverloadResolve (
275 ec, (MethodGroupExpr) mg, pos_args, Location);
277 if (constructor == null) {
278 Error_AttributeConstructorMismatch (Location);
283 // Now we perform some checks on the positional args as they
284 // cannot be null for a constructor which expects a parameter
288 ParameterData pd = Invocation.GetParameterData (constructor);
290 for (int j = 0; j < pos_arg_count; ++j) {
291 Argument a = (Argument) pos_args [j];
293 if (a.Expr is NullLiteral && pd.ParameterType (j) == TypeManager.object_type) {
294 Error_AttributeArgumentNotValid ();
299 PropertyInfo [] prop_info_arr = new PropertyInfo [prop_infos.Count];
300 FieldInfo [] field_info_arr = new FieldInfo [field_infos.Count];
301 object [] field_values_arr = new object [field_values.Count];
302 object [] prop_values_arr = new object [prop_values.Count];
304 field_infos.CopyTo (field_info_arr, 0);
305 field_values.CopyTo (field_values_arr, 0);
307 prop_values.CopyTo (prop_values_arr, 0);
308 prop_infos.CopyTo (prop_info_arr, 0);
311 cb = new CustomAttributeBuilder (
312 (ConstructorInfo) constructor, pos_values,
313 prop_info_arr, prop_values_arr,
314 field_info_arr, field_values_arr);
316 } catch (NullReferenceException) {
318 // Don't know what to do here
323 // using System.ComponentModel;
324 // [DefaultValue (CollectionChangeAction.Add)]
325 // class X { static void Main () {} }
329 "The compiler can not encode this attribute in .NET due to\n" +
330 "\ta bug in the .NET runtime. Try the Mono runtime");
336 static string GetValidPlaces (Attribute attr)
338 StringBuilder sb = new StringBuilder ();
339 AttributeTargets targets = 0;
341 TypeContainer a = TypeManager.LookupAttr (attr.Type);
345 System.Attribute [] attrs = null;
348 attrs = System.Attribute.GetCustomAttributes (attr.Type);
351 Report.Error (-20, attr.Location, "Cannot find attribute type " + attr.Name +
352 " (maybe you forgot to set the usage using the" +
353 " AttributeUsage attribute ?).");
357 foreach (System.Attribute tmp in attrs)
358 if (tmp is AttributeUsageAttribute) {
359 targets = ((AttributeUsageAttribute) tmp).ValidOn;
366 if ((targets & AttributeTargets.Assembly) != 0)
367 sb.Append ("'assembly' ");
369 if ((targets & AttributeTargets.Class) != 0)
370 sb.Append ("'class' ");
372 if ((targets & AttributeTargets.Constructor) != 0)
373 sb.Append ("'constructor' ");
375 if ((targets & AttributeTargets.Delegate) != 0)
376 sb.Append ("'delegate' ");
378 if ((targets & AttributeTargets.Enum) != 0)
379 sb.Append ("'enum' ");
381 if ((targets & AttributeTargets.Event) != 0)
382 sb.Append ("'event' ");
384 if ((targets & AttributeTargets.Field) != 0)
385 sb.Append ("'field' ");
387 if ((targets & AttributeTargets.Interface) != 0)
388 sb.Append ("'interface' ");
390 if ((targets & AttributeTargets.Method) != 0)
391 sb.Append ("'method' ");
393 if ((targets & AttributeTargets.Module) != 0)
394 sb.Append ("'module' ");
396 if ((targets & AttributeTargets.Parameter) != 0)
397 sb.Append ("'parameter' ");
399 if ((targets & AttributeTargets.Property) != 0)
400 sb.Append ("'property' ");
402 if ((targets & AttributeTargets.ReturnValue) != 0)
403 sb.Append ("'return value' ");
405 if ((targets & AttributeTargets.Struct) != 0)
406 sb.Append ("'struct' ");
408 return sb.ToString ();
412 public static void Error_AttributeNotValidForElement (Attribute a, Location loc)
415 592, loc, "Attribute '" + a.Name +
416 "' is not valid on this declaration type. " +
417 "It is valid on " + GetValidPlaces (a) + "declarations only.");
420 public static bool CheckAttribute (Attribute a, object element)
422 TypeContainer attr = TypeManager.LookupAttr (a.Type);
423 AttributeTargets targets = 0;
428 System.Attribute [] attrs = null;
431 attrs = System.Attribute.GetCustomAttributes (a.Type);
434 Report.Error (-20, a.Location, "Cannot find attribute type " + a.Name +
435 " (maybe you forgot to set the usage using the" +
436 " AttributeUsage attribute ?).");
440 foreach (System.Attribute tmp in attrs)
441 if (tmp is AttributeUsageAttribute)
442 targets = ((AttributeUsageAttribute) tmp).ValidOn;
444 targets = attr.Targets;
446 if (element is Class) {
447 if ((targets & AttributeTargets.Class) != 0)
452 } else if (element is Struct) {
453 if ((targets & AttributeTargets.Struct) != 0)
457 } else if (element is Constructor) {
458 if ((targets & AttributeTargets.Constructor) != 0)
462 } else if (element is Delegate) {
463 if ((targets & AttributeTargets.Delegate) != 0)
467 } else if (element is Enum) {
468 if ((targets & AttributeTargets.Enum) != 0)
472 } else if (element is Event || element is InterfaceEvent) {
473 if ((targets & AttributeTargets.Event) != 0)
477 } else if (element is Field || element is FieldBuilder) {
478 if ((targets & AttributeTargets.Field) != 0)
482 } else if (element is Interface) {
483 if ((targets & AttributeTargets.Interface) != 0)
487 } else if (element is Method || element is Operator || element is InterfaceMethod || element is Accessor) {
488 if ((targets & AttributeTargets.Method) != 0)
492 } else if (element is ParameterBuilder) {
493 if ((targets & AttributeTargets.Parameter) != 0)
497 } else if (element is Property || element is Indexer ||
498 element is InterfaceProperty || element is InterfaceIndexer) {
499 if ((targets & AttributeTargets.Property) != 0)
503 } else if (element is AssemblyBuilder){
504 if ((targets & AttributeTargets.Assembly) != 0)
514 // This method should be invoked to pull the IndexerName attribute from an
515 // Indexer if it exists.
517 public static string ScanForIndexerName (EmitContext ec, Attributes opt_attrs)
519 if (opt_attrs == null)
521 if (opt_attrs.AttributeSections == null)
524 foreach (AttributeSection asec in opt_attrs.AttributeSections) {
525 if (asec.Attributes == null)
528 foreach (Attribute a in asec.Attributes){
529 if (a.ResolveType (ec) == null)
532 if (a.Type != TypeManager.indexer_name_type)
536 // So we have found an IndexerName, pull the data out.
538 if (a.Arguments == null || a.Arguments [0] == null){
539 Error_AttributeConstructorMismatch (a.Location);
542 ArrayList pos_args = (ArrayList) a.Arguments [0];
543 if (pos_args.Count == 0){
544 Error_AttributeConstructorMismatch (a.Location);
548 Argument arg = (Argument) pos_args [0];
549 if (!arg.Resolve (ec, a.Location))
552 Expression e = arg.Expr;
553 if (!(e is StringConstant)){
554 Error_AttributeConstructorMismatch (a.Location);
559 // Remove the attribute from the list
561 asec.Attributes.Remove (a);
563 return (((StringConstant) e).Value);
570 // This pulls the condition name out of a Conditional attribute
572 public string Conditional_GetConditionName ()
575 // So we have a Conditional, pull the data out.
577 if (Arguments == null || Arguments [0] == null){
578 Error_AttributeConstructorMismatch (Location);
582 ArrayList pos_args = (ArrayList) Arguments [0];
583 if (pos_args.Count != 1){
584 Error_AttributeConstructorMismatch (Location);
588 Argument arg = (Argument) pos_args [0];
589 if (!(arg.Expr is StringConstant)){
590 Error_AttributeConstructorMismatch (Location);
594 return ((StringConstant) arg.Expr).Value;
598 // This pulls the obsolete message and error flag out of an Obsolete attribute
600 public string Obsolete_GetObsoleteMessage (out bool is_error)
604 // So we have an Obsolete, pull the data out.
606 if (Arguments == null || Arguments [0] == null)
609 ArrayList pos_args = (ArrayList) Arguments [0];
610 if (pos_args.Count == 0)
612 else if (pos_args.Count > 2){
613 Error_AttributeConstructorMismatch (Location);
617 Argument arg = (Argument) pos_args [0];
618 if (!(arg.Expr is StringConstant)){
619 Error_AttributeConstructorMismatch (Location);
623 if (pos_args.Count == 2){
624 Argument arg2 = (Argument) pos_args [1];
625 if (!(arg2.Expr is BoolConstant)){
626 Error_AttributeConstructorMismatch (Location);
629 is_error = ((BoolConstant) arg2.Expr).Value;
632 return ((StringConstant) arg.Expr).Value;
636 // Applies the attributes to the `builder'.
638 public static void ApplyAttributes (EmitContext ec, object builder, object kind,
639 Attributes opt_attrs, Location loc)
641 if (opt_attrs == null)
643 if (opt_attrs.AttributeSections == null)
646 foreach (AttributeSection asec in opt_attrs.AttributeSections) {
647 if (asec.Attributes == null)
650 if (asec.Target == "assembly" && !(builder is AssemblyBuilder))
653 foreach (Attribute a in asec.Attributes) {
654 CustomAttributeBuilder cb = a.Resolve (ec);
659 if (!(kind is TypeContainer))
660 if (!CheckAttribute (a, kind)) {
661 Error_AttributeNotValidForElement (a, loc);
665 if (kind is Method || kind is Operator || kind is InterfaceMethod ||
667 if (a.Type == TypeManager.methodimpl_attr_type) {
668 if (a.ImplOptions == MethodImplOptions.InternalCall)
669 ((MethodBuilder) builder).
670 SetImplementationFlags (
671 MethodImplAttributes.InternalCall |
672 MethodImplAttributes.Runtime);
673 } else if (a.Type != TypeManager.dllimport_type){
674 ((MethodBuilder) builder).SetCustomAttribute (cb);
676 } else if (kind is Constructor) {
677 ((ConstructorBuilder) builder).SetCustomAttribute (cb);
678 } else if (kind is Field) {
679 ((FieldBuilder) builder).SetCustomAttribute (cb);
680 } else if (kind is Property || kind is Indexer ||
681 kind is InterfaceProperty || kind is InterfaceIndexer) {
682 ((PropertyBuilder) builder).SetCustomAttribute (cb);
683 } else if (kind is Event || kind is InterfaceEvent) {
684 ((MyEventBuilder) builder).SetCustomAttribute (cb);
685 } else if (kind is ParameterBuilder) {
687 if (a.Type == TypeManager.marshal_as_attr_type) {
688 UnmanagedMarshal marshal =
689 UnmanagedMarshal.DefineUnmanagedMarshal (a.UnmanagedType);
691 ((ParameterBuilder) builder).SetMarshal (marshal);
693 ((ParameterBuilder) builder).SetCustomAttribute (cb);
695 } else if (kind is Enum) {
696 ((TypeBuilder) builder).SetCustomAttribute (cb);
698 } else if (kind is TypeContainer) {
699 TypeContainer tc = (TypeContainer) kind;
702 tc.Targets = a.Targets;
703 tc.AllowMultiple = a.AllowMultiple;
704 tc.Inherited = a.Inherited;
706 } else if (a.Type == TypeManager.default_member_type) {
707 if (tc.Indexers != null) {
708 Report.Error (646, loc,
709 "Cannot specify the DefaultMember attribute on" +
710 " a type containing an indexer");
715 if (!CheckAttribute (a, kind)) {
716 Error_AttributeNotValidForElement (a, loc);
722 ((TypeBuilder) builder).SetCustomAttribute (cb);
723 } catch (System.ArgumentException) {
726 "The CharSet named property on StructLayout\n"+
727 "\tdoes not work correctly on Microsoft.NET\n"+
728 "\tYou might want to remove the CharSet declaration\n"+
729 "\tor compile using the Mono runtime instead of the\n"+
730 "\tMicrosoft .NET runtime");
733 } else if (kind is Interface) {
734 Interface iface = (Interface) kind;
736 if ((a.Type == TypeManager.default_member_type) &&
737 (iface.InterfaceIndexers != null)) {
740 "Cannot specify the DefaultMember attribute on" +
741 " a type containing an indexer");
745 if (!CheckAttribute (a, kind)) {
746 Error_AttributeNotValidForElement (a, loc);
750 ((TypeBuilder) builder).SetCustomAttribute (cb);
751 } else if (kind is AssemblyBuilder){
752 ((AssemblyBuilder) builder).SetCustomAttribute (cb);
753 } else if (kind is ModuleBuilder) {
754 ((ModuleBuilder) builder).SetCustomAttribute (cb);
755 } else if (kind is FieldBuilder) {
756 ((FieldBuilder) builder).SetCustomAttribute (cb);
758 throw new Exception ("Unknown kind: " + kind);
763 public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name,
764 MethodAttributes flags, Type ret_type, Type [] param_types)
767 // We extract from the attribute the information we need
770 if (Arguments == null) {
771 Console.WriteLine ("Internal error : this is not supposed to happen !");
775 Type = CheckAttributeType (ec);
779 ArrayList named_args = new ArrayList ();
781 ArrayList pos_args = (ArrayList) Arguments [0];
782 if (Arguments.Count > 1)
783 named_args = (ArrayList) Arguments [1];
786 string dll_name = null;
788 Argument tmp = (Argument) pos_args [0];
790 if (!tmp.Resolve (ec, Location))
793 if (tmp.Expr is Constant)
794 dll_name = (string) ((Constant) tmp.Expr).GetValue ();
796 Error_AttributeArgumentNotValid ();
800 // Now we process the named arguments
801 CallingConvention cc = CallingConvention.Winapi;
802 CharSet charset = CharSet.Ansi;
803 bool preserve_sig = true;
804 bool exact_spelling = false;
805 bool set_last_err = false;
806 string entry_point = null;
808 for (int i = 0; i < named_args.Count; i++) {
810 DictionaryEntry de = (DictionaryEntry) named_args [i];
812 string member_name = (string) de.Key;
813 Argument a = (Argument) de.Value;
815 if (!a.Resolve (ec, Location))
818 Expression member = Expression.MemberLookup (
819 ec, Type, member_name,
820 MemberTypes.Field | MemberTypes.Property,
821 BindingFlags.Public | BindingFlags.Instance,
824 if (member == null || !(member is FieldExpr)) {
825 Error_InvalidNamedArgument (member_name);
829 if (member is FieldExpr) {
830 FieldExpr fe = (FieldExpr) member;
831 FieldInfo fi = fe.FieldInfo;
834 Error_InvalidNamedArgument (member_name);
838 if (a.Expr is Constant) {
839 Constant c = (Constant) a.Expr;
841 if (member_name == "CallingConvention")
842 cc = (CallingConvention) c.GetValue ();
843 else if (member_name == "CharSet")
844 charset = (CharSet) c.GetValue ();
845 else if (member_name == "EntryPoint")
846 entry_point = (string) c.GetValue ();
847 else if (member_name == "SetLastError")
848 set_last_err = (bool) c.GetValue ();
849 else if (member_name == "ExactSpelling")
850 exact_spelling = (bool) c.GetValue ();
851 else if (member_name == "PreserveSig")
852 preserve_sig = (bool) c.GetValue ();
854 Error_AttributeArgumentNotValid ();
861 if (entry_point == null)
864 MethodBuilder mb = builder.DefinePInvokeMethod (
865 name, dll_name, entry_point, flags | MethodAttributes.HideBySig,
866 CallingConventions.Standard,
873 mb.SetImplementationFlags (MethodImplAttributes.PreserveSig);
879 public class AttributeSection {
881 public readonly string Target;
882 public readonly ArrayList Attributes;
884 public AttributeSection (string target, ArrayList attrs)
892 public class Attributes {
893 public ArrayList AttributeSections;
894 public Location Location;
896 public Attributes (AttributeSection a, Location loc)
898 AttributeSections = new ArrayList ();
899 AttributeSections.Add (a);
903 public void AddAttribute (AttributeSection a)
906 AttributeSections.Add (a);
909 public void AddAttributeSection (AttributeSection a)
911 if (a != null && !AttributeSections.Contains (a))
912 AttributeSections.Add (a);
915 public bool Contains (Type t)
917 foreach (AttributeSection attr_section in AttributeSections){
918 foreach (Attribute a in attr_section.Attributes){