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))
159 pos_values [i] = ((Constant) e).GetValue ();
160 } else if (e is TypeOf) {
161 pos_values [i] = ((TypeOf) e).TypeArg;
163 Error_AttributeArgumentNotValid ();
168 this.Targets = (AttributeTargets) pos_values [0];
171 this.ImplOptions = (MethodImplOptions) pos_values [0];
175 (System.Runtime.InteropServices.UnmanagedType) pos_values [0];
179 // Now process named arguments
182 ArrayList field_infos = new ArrayList ();
183 ArrayList prop_infos = new ArrayList ();
184 ArrayList field_values = new ArrayList ();
185 ArrayList prop_values = new ArrayList ();
187 for (i = 0; i < named_args.Count; i++) {
188 DictionaryEntry de = (DictionaryEntry) named_args [i];
189 string member_name = (string) de.Key;
190 Argument a = (Argument) de.Value;
193 if (!a.Resolve (ec, Location))
196 Expression member = Expression.MemberLookup (
197 ec, Type, member_name,
198 MemberTypes.Field | MemberTypes.Property,
199 BindingFlags.Public | BindingFlags.Instance,
202 if (member == null || !(member is PropertyExpr || member is FieldExpr)) {
203 Error_InvalidNamedArgument (member_name);
208 if (member is PropertyExpr) {
209 PropertyExpr pe = (PropertyExpr) member;
210 PropertyInfo pi = pe.PropertyInfo;
213 Error_InvalidNamedArgument (member_name);
218 object o = ((Constant) e).GetValue ();
222 if (member_name == "AllowMultiple")
223 this.AllowMultiple = (bool) o;
224 if (member_name == "Inherited")
225 this.Inherited = (bool) o;
229 Error_AttributeArgumentNotValid ();
235 } else if (member is FieldExpr) {
236 FieldExpr fe = (FieldExpr) member;
237 FieldInfo fi = fe.FieldInfo;
240 Error_InvalidNamedArgument (member_name);
245 // Handle charset here, and set the TypeAttributes
248 object value = ((Constant) e).GetValue ();
250 field_values.Add (value);
252 Error_AttributeArgumentNotValid ();
256 field_infos.Add (fi);
260 Expression mg = Expression.MemberLookup (
261 ec, Type, ".ctor", MemberTypes.Constructor,
262 BindingFlags.Public | BindingFlags.Instance, Location);
265 Error_AttributeConstructorMismatch (Location);
269 MethodBase constructor = Invocation.OverloadResolve (
270 ec, (MethodGroupExpr) mg, pos_args, Location);
272 if (constructor == null) {
273 Error_AttributeConstructorMismatch (Location);
277 PropertyInfo [] prop_info_arr = new PropertyInfo [prop_infos.Count];
278 FieldInfo [] field_info_arr = new FieldInfo [field_infos.Count];
279 object [] field_values_arr = new object [field_values.Count];
280 object [] prop_values_arr = new object [prop_values.Count];
282 field_infos.CopyTo (field_info_arr, 0);
283 field_values.CopyTo (field_values_arr, 0);
285 prop_values.CopyTo (prop_values_arr, 0);
286 prop_infos.CopyTo (prop_info_arr, 0);
289 cb = new CustomAttributeBuilder (
290 (ConstructorInfo) constructor, pos_values,
291 prop_info_arr, prop_values_arr,
292 field_info_arr, field_values_arr);
293 } catch (NullReferenceException) {
296 "The compiler can not encode this attribute in the Mono runtime\n" +
297 "\tdue to a known bug in it. We know about the problem and will\n" +
298 "\tfix it as soon as possible.");
302 // using System.ComponentModel;
303 // [DefaultValue (CollectionChangeAction.Add)]
304 // class X { static void Main () {} }
308 "The compiler can not encode this attribute in .NET due to\n" +
309 "\ta bug in the .NET runtime. Try the Mono runtime");
315 static string GetValidPlaces (Attribute attr)
317 StringBuilder sb = new StringBuilder ();
318 AttributeTargets targets = 0;
320 TypeContainer a = TypeManager.LookupAttr (attr.Type);
324 System.Attribute [] attrs = null;
327 attrs = System.Attribute.GetCustomAttributes (attr.Type);
330 Report.Error (-20, attr.Location, "Cannot find attribute type " + attr.Name +
331 " (maybe you forgot to set the usage using the" +
332 " AttributeUsage attribute ?).");
336 foreach (System.Attribute tmp in attrs)
337 if (tmp is AttributeUsageAttribute) {
338 targets = ((AttributeUsageAttribute) tmp).ValidOn;
345 if ((targets & AttributeTargets.Assembly) != 0)
346 sb.Append ("'assembly' ");
348 if ((targets & AttributeTargets.Class) != 0)
349 sb.Append ("'class' ");
351 if ((targets & AttributeTargets.Constructor) != 0)
352 sb.Append ("'constructor' ");
354 if ((targets & AttributeTargets.Delegate) != 0)
355 sb.Append ("'delegate' ");
357 if ((targets & AttributeTargets.Enum) != 0)
358 sb.Append ("'enum' ");
360 if ((targets & AttributeTargets.Event) != 0)
361 sb.Append ("'event' ");
363 if ((targets & AttributeTargets.Field) != 0)
364 sb.Append ("'field' ");
366 if ((targets & AttributeTargets.Interface) != 0)
367 sb.Append ("'interface' ");
369 if ((targets & AttributeTargets.Method) != 0)
370 sb.Append ("'method' ");
372 if ((targets & AttributeTargets.Module) != 0)
373 sb.Append ("'module' ");
375 if ((targets & AttributeTargets.Parameter) != 0)
376 sb.Append ("'parameter' ");
378 if ((targets & AttributeTargets.Property) != 0)
379 sb.Append ("'property' ");
381 if ((targets & AttributeTargets.ReturnValue) != 0)
382 sb.Append ("'return value' ");
384 if ((targets & AttributeTargets.Struct) != 0)
385 sb.Append ("'struct' ");
387 return sb.ToString ();
391 public static void Error_AttributeNotValidForElement (Attribute a, Location loc)
394 592, loc, "Attribute '" + a.Name +
395 "' is not valid on this declaration type. " +
396 "It is valid on " + GetValidPlaces (a) + "declarations only.");
399 public static bool CheckAttribute (Attribute a, object element)
401 TypeContainer attr = TypeManager.LookupAttr (a.Type);
402 AttributeTargets targets = 0;
407 System.Attribute [] attrs = null;
410 attrs = System.Attribute.GetCustomAttributes (a.Type);
413 Report.Error (-20, a.Location, "Cannot find attribute type " + a.Name +
414 " (maybe you forgot to set the usage using the" +
415 " AttributeUsage attribute ?).");
419 foreach (System.Attribute tmp in attrs)
420 if (tmp is AttributeUsageAttribute)
421 targets = ((AttributeUsageAttribute) tmp).ValidOn;
423 targets = attr.Targets;
425 if (element is Class) {
426 if ((targets & AttributeTargets.Class) != 0)
431 } else if (element is Struct) {
432 if ((targets & AttributeTargets.Struct) != 0)
436 } else if (element is Constructor) {
437 if ((targets & AttributeTargets.Constructor) != 0)
441 } else if (element is Delegate) {
442 if ((targets & AttributeTargets.Delegate) != 0)
446 } else if (element is Enum) {
447 if ((targets & AttributeTargets.Enum) != 0)
451 } else if (element is Event || element is InterfaceEvent) {
452 if ((targets & AttributeTargets.Event) != 0)
456 } else if (element is Field || element is FieldBuilder) {
457 if ((targets & AttributeTargets.Field) != 0)
461 } else if (element is Interface) {
462 if ((targets & AttributeTargets.Interface) != 0)
466 } else if (element is Method || element is Operator || element is InterfaceMethod || element is Accessor) {
467 if ((targets & AttributeTargets.Method) != 0)
471 } else if (element is ParameterBuilder) {
472 if ((targets & AttributeTargets.Parameter) != 0)
476 } else if (element is Property || element is Indexer ||
477 element is InterfaceProperty || element is InterfaceIndexer) {
478 if ((targets & AttributeTargets.Property) != 0)
482 } else if (element is AssemblyBuilder){
483 if ((targets & AttributeTargets.Assembly) != 0)
493 // This method should be invoked to pull the IndexerName attribute from an
494 // Indexer if it exists.
496 public static string ScanForIndexerName (EmitContext ec, Attributes opt_attrs)
498 if (opt_attrs == null)
500 if (opt_attrs.AttributeSections == null)
503 foreach (AttributeSection asec in opt_attrs.AttributeSections) {
504 if (asec.Attributes == null)
507 foreach (Attribute a in asec.Attributes){
508 if (a.ResolveType (ec) == null)
511 if (a.Type != TypeManager.indexer_name_type)
515 // So we have found an IndexerName, pull the data out.
517 if (a.Arguments == null || a.Arguments [0] == null){
518 Error_AttributeConstructorMismatch (a.Location);
521 ArrayList pos_args = (ArrayList) a.Arguments [0];
522 if (pos_args.Count == 0){
523 Error_AttributeConstructorMismatch (a.Location);
527 Argument arg = (Argument) pos_args [0];
528 if (!arg.Resolve (ec, a.Location))
531 Expression e = arg.Expr;
532 if (!(e is StringConstant)){
533 Error_AttributeConstructorMismatch (a.Location);
538 // Remove the attribute from the list
540 asec.Attributes.Remove (a);
542 return (((StringConstant) e).Value);
549 // This pulls the condition name out of a Conditional attribute
551 public string Conditional_GetConditionName ()
554 // So we have a Conditional, pull the data out.
556 if (Arguments == null || Arguments [0] == null){
557 Error_AttributeConstructorMismatch (Location);
561 ArrayList pos_args = (ArrayList) Arguments [0];
562 if (pos_args.Count != 1){
563 Error_AttributeConstructorMismatch (Location);
567 Argument arg = (Argument) pos_args [0];
568 if (!(arg.Expr is StringConstant)){
569 Error_AttributeConstructorMismatch (Location);
573 return ((StringConstant) arg.Expr).Value;
577 // This pulls the obsolete message and error flag out of an Obsolete attribute
579 public string Obsolete_GetObsoleteMessage (out bool is_error)
583 // So we have an Obsolete, pull the data out.
585 if (Arguments == null || Arguments [0] == null)
588 ArrayList pos_args = (ArrayList) Arguments [0];
589 if (pos_args.Count == 0)
591 else if (pos_args.Count > 2){
592 Error_AttributeConstructorMismatch (Location);
596 Argument arg = (Argument) pos_args [0];
597 if (!(arg.Expr is StringConstant)){
598 Error_AttributeConstructorMismatch (Location);
602 if (pos_args.Count == 2){
603 Argument arg2 = (Argument) pos_args [1];
604 if (!(arg2.Expr is BoolConstant)){
605 Error_AttributeConstructorMismatch (Location);
608 is_error = ((BoolConstant) arg2.Expr).Value;
611 return ((StringConstant) arg.Expr).Value;
615 // Applies the attributes to the `builder'.
617 public static void ApplyAttributes (EmitContext ec, object builder, object kind,
618 Attributes opt_attrs, Location loc)
620 if (opt_attrs == null)
622 if (opt_attrs.AttributeSections == null)
625 foreach (AttributeSection asec in opt_attrs.AttributeSections) {
626 if (asec.Attributes == null)
629 if (asec.Target == "assembly" && !(builder is AssemblyBuilder))
632 foreach (Attribute a in asec.Attributes) {
633 CustomAttributeBuilder cb = a.Resolve (ec);
638 if (!(kind is TypeContainer))
639 if (!CheckAttribute (a, kind)) {
640 Error_AttributeNotValidForElement (a, loc);
644 if (kind is Method || kind is Operator || kind is InterfaceMethod ||
646 if (a.Type == TypeManager.methodimpl_attr_type) {
647 if (a.ImplOptions == MethodImplOptions.InternalCall)
648 ((MethodBuilder) builder).
649 SetImplementationFlags (
650 MethodImplAttributes.InternalCall |
651 MethodImplAttributes.Runtime);
652 } else if (a.Type != TypeManager.dllimport_type){
653 ((MethodBuilder) builder).SetCustomAttribute (cb);
655 } else if (kind is Constructor) {
656 ((ConstructorBuilder) builder).SetCustomAttribute (cb);
657 } else if (kind is Field) {
658 ((FieldBuilder) builder).SetCustomAttribute (cb);
659 } else if (kind is Property || kind is Indexer ||
660 kind is InterfaceProperty || kind is InterfaceIndexer) {
661 ((PropertyBuilder) builder).SetCustomAttribute (cb);
662 } else if (kind is Event || kind is InterfaceEvent) {
663 ((MyEventBuilder) builder).SetCustomAttribute (cb);
664 } else if (kind is ParameterBuilder) {
666 if (a.Type == TypeManager.marshal_as_attr_type) {
667 UnmanagedMarshal marshal =
668 UnmanagedMarshal.DefineUnmanagedMarshal (a.UnmanagedType);
670 ((ParameterBuilder) builder).SetMarshal (marshal);
672 ((ParameterBuilder) builder).SetCustomAttribute (cb);
674 } else if (kind is Enum) {
675 ((TypeBuilder) builder).SetCustomAttribute (cb);
677 } else if (kind is TypeContainer) {
678 TypeContainer tc = (TypeContainer) kind;
681 tc.Targets = a.Targets;
682 tc.AllowMultiple = a.AllowMultiple;
683 tc.Inherited = a.Inherited;
685 } else if (a.Type == TypeManager.default_member_type) {
686 if (tc.Indexers != null) {
687 Report.Error (646, loc,
688 "Cannot specify the DefaultMember attribute on" +
689 " a type containing an indexer");
694 if (!CheckAttribute (a, kind)) {
695 Error_AttributeNotValidForElement (a, loc);
701 ((TypeBuilder) builder).SetCustomAttribute (cb);
702 } catch (System.ArgumentException) {
705 "The CharSet named property on StructLayout\n"+
706 "\tdoes not work correctly on Microsoft.NET\n"+
707 "\tYou might want to remove the CharSet declaration\n"+
708 "\tor compile using the Mono runtime instead of the\n"+
709 "\tMicrosoft .NET runtime");
712 } else if (kind is Interface) {
713 Interface iface = (Interface) kind;
715 if ((a.Type == TypeManager.default_member_type) &&
716 (iface.InterfaceIndexers != null)) {
719 "Cannot specify the DefaultMember attribute on" +
720 " a type containing an indexer");
724 if (!CheckAttribute (a, kind)) {
725 Error_AttributeNotValidForElement (a, loc);
729 ((TypeBuilder) builder).SetCustomAttribute (cb);
730 } else if (kind is AssemblyBuilder){
731 ((AssemblyBuilder) builder).SetCustomAttribute (cb);
732 } else if (kind is ModuleBuilder) {
733 ((ModuleBuilder) builder).SetCustomAttribute (cb);
734 } else if (kind is FieldBuilder) {
735 ((FieldBuilder) builder).SetCustomAttribute (cb);
737 throw new Exception ("Unknown kind: " + kind);
742 public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name,
743 MethodAttributes flags, Type ret_type, Type [] param_types)
746 // We extract from the attribute the information we need
749 if (Arguments == null) {
750 Console.WriteLine ("Internal error : this is not supposed to happen !");
754 Type = CheckAttributeType (ec);
758 ArrayList named_args = new ArrayList ();
760 ArrayList pos_args = (ArrayList) Arguments [0];
761 if (Arguments.Count > 1)
762 named_args = (ArrayList) Arguments [1];
765 string dll_name = null;
767 Argument tmp = (Argument) pos_args [0];
769 if (!tmp.Resolve (ec, Location))
772 if (tmp.Expr is Constant)
773 dll_name = (string) ((Constant) tmp.Expr).GetValue ();
775 Error_AttributeArgumentNotValid ();
779 // Now we process the named arguments
780 CallingConvention cc = CallingConvention.Winapi;
781 CharSet charset = CharSet.Ansi;
782 bool preserve_sig = true;
783 bool exact_spelling = false;
784 bool set_last_err = false;
785 string entry_point = null;
787 for (int i = 0; i < named_args.Count; i++) {
789 DictionaryEntry de = (DictionaryEntry) named_args [i];
791 string member_name = (string) de.Key;
792 Argument a = (Argument) de.Value;
794 if (!a.Resolve (ec, Location))
797 Expression member = Expression.MemberLookup (
798 ec, Type, member_name,
799 MemberTypes.Field | MemberTypes.Property,
800 BindingFlags.Public | BindingFlags.Instance,
803 if (member == null || !(member is FieldExpr)) {
804 Error_InvalidNamedArgument (member_name);
808 if (member is FieldExpr) {
809 FieldExpr fe = (FieldExpr) member;
810 FieldInfo fi = fe.FieldInfo;
813 Error_InvalidNamedArgument (member_name);
817 if (a.Expr is Constant) {
818 Constant c = (Constant) a.Expr;
820 if (member_name == "CallingConvention")
821 cc = (CallingConvention) c.GetValue ();
822 else if (member_name == "CharSet")
823 charset = (CharSet) c.GetValue ();
824 else if (member_name == "EntryPoint")
825 entry_point = (string) c.GetValue ();
826 else if (member_name == "SetLastError")
827 set_last_err = (bool) c.GetValue ();
828 else if (member_name == "ExactSpelling")
829 exact_spelling = (bool) c.GetValue ();
830 else if (member_name == "PreserveSig")
831 preserve_sig = (bool) c.GetValue ();
833 Error_AttributeArgumentNotValid ();
840 if (entry_point == null)
843 MethodBuilder mb = builder.DefinePInvokeMethod (
844 name, dll_name, entry_point, flags | MethodAttributes.HideBySig,
845 CallingConventions.Standard,
852 mb.SetImplementationFlags (MethodImplAttributes.PreserveSig);
859 public class AttributeSection {
861 public readonly string Target;
862 public readonly ArrayList Attributes;
864 public AttributeSection (string target, ArrayList attrs)
872 public class Attributes {
873 public ArrayList AttributeSections;
874 public Location Location;
876 public Attributes (AttributeSection a, Location loc)
878 AttributeSections = new ArrayList ();
879 AttributeSections.Add (a);
883 public void AddAttribute (AttributeSection a)
886 AttributeSections.Add (a);