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.Collections;
14 using System.Reflection;
15 using System.Reflection.Emit;
16 using System.Runtime.InteropServices;
17 using System.Runtime.CompilerServices;
20 namespace Mono.CSharp {
22 public class Attribute {
23 public readonly string Name;
24 public readonly ArrayList Arguments;
31 // The following are only meaningful when the attribute
32 // being emitted is one of the builtin ones
34 AttributeTargets Targets;
38 bool UsageAttr = false;
40 MethodImplOptions ImplOptions;
41 UnmanagedType UnmanagedType;
42 CustomAttributeBuilder cb;
44 public Attribute (string name, ArrayList args, Location loc)
51 void Error_InvalidNamedArgument (string name)
53 Report.Error (617, Location, "'" + name + "' is not a valid named attribute " +
54 "argument. Named attribute arguments must be fields which are not " +
55 "readonly, static or const, or properties with a set accessor which "+
59 void Error_AttributeArgumentNotValid ()
61 Report.Error (182, Location,
62 "An attribute argument must be a constant expression, typeof " +
63 "expression or array creation expression");
66 static void Error_AttributeConstructorMismatch (Location loc)
70 "Could not find a constructor for this argument list.");
73 private Type CheckAttributeType (EmitContext ec) {
75 bool isattributeclass = true;
77 t = RootContext.LookupType (ec.DeclSpace, Name, true, Location);
79 isattributeclass = t.IsSubclassOf (TypeManager.attribute_type);
83 t = RootContext.LookupType (ec.DeclSpace, Name + "Attribute", true, Location);
85 if (t.IsSubclassOf (TypeManager.attribute_type))
88 if (!isattributeclass) {
89 Report.Error (616, Location, "'" + Name + "': is not an attribute class");
93 Report.Error (616, Location, "'" + Name + "Attribute': is not an attribute class");
97 246, Location, "Could not find attribute '" + Name + "' (are you" +
98 " missing a using directive or an assembly reference ?)");
102 public Type ResolveType (EmitContext ec)
104 Type = CheckAttributeType (ec);
109 public CustomAttributeBuilder Resolve (EmitContext ec)
112 Type = CheckAttributeType (ec);
116 bool MethodImplAttr = false;
117 bool MarshalAsAttr = false;
121 if (Type == TypeManager.attribute_usage_type)
123 if (Type == TypeManager.methodimpl_attr_type)
124 MethodImplAttr = true;
125 if (Type == TypeManager.marshal_as_attr_type)
126 MarshalAsAttr = true;
128 // Now we extract the positional and named arguments
130 ArrayList pos_args = new ArrayList ();
131 ArrayList named_args = new ArrayList ();
132 int pos_arg_count = 0;
134 if (Arguments != null) {
135 pos_args = (ArrayList) Arguments [0];
136 if (pos_args != null)
137 pos_arg_count = pos_args.Count;
138 if (Arguments.Count > 1)
139 named_args = (ArrayList) Arguments [1];
142 object [] pos_values = new object [pos_arg_count];
145 // First process positional arguments
149 for (i = 0; i < pos_arg_count; i++) {
150 Argument a = (Argument) pos_args [i];
153 if (!a.Resolve (ec, Location))
158 pos_values [i] = ((Constant) e).GetValue ();
159 } else if (e is TypeOf) {
160 pos_values [i] = ((TypeOf) e).TypeArg;
162 Error_AttributeArgumentNotValid ();
167 this.Targets = (AttributeTargets) pos_values [0];
170 this.ImplOptions = (MethodImplOptions) pos_values [0];
174 (System.Runtime.InteropServices.UnmanagedType) pos_values [0];
178 // Now process named arguments
181 ArrayList field_infos = new ArrayList ();
182 ArrayList prop_infos = new ArrayList ();
183 ArrayList field_values = new ArrayList ();
184 ArrayList prop_values = new ArrayList ();
186 for (i = 0; i < named_args.Count; i++) {
187 DictionaryEntry de = (DictionaryEntry) named_args [i];
188 string member_name = (string) de.Key;
189 Argument a = (Argument) de.Value;
192 if (!a.Resolve (ec, Location))
195 Expression member = Expression.MemberLookup (
196 ec, Type, member_name,
197 MemberTypes.Field | MemberTypes.Property,
198 BindingFlags.Public | BindingFlags.Instance,
201 if (member == null || !(member is PropertyExpr || member is FieldExpr)) {
202 Error_InvalidNamedArgument (member_name);
207 if (member is PropertyExpr) {
208 PropertyExpr pe = (PropertyExpr) member;
209 PropertyInfo pi = pe.PropertyInfo;
212 Error_InvalidNamedArgument (member_name);
217 object o = ((Constant) e).GetValue ();
221 if (member_name == "AllowMultiple")
222 this.AllowMultiple = (bool) o;
223 if (member_name == "Inherited")
224 this.Inherited = (bool) o;
228 Error_AttributeArgumentNotValid ();
234 } else if (member is FieldExpr) {
235 FieldExpr fe = (FieldExpr) member;
236 FieldInfo fi = fe.FieldInfo;
239 Error_InvalidNamedArgument (member_name);
244 // Handle charset here, and set the TypeAttributes
247 object value = ((Constant) e).GetValue ();
249 field_values.Add (value);
251 Error_AttributeArgumentNotValid ();
255 field_infos.Add (fi);
259 Expression mg = Expression.MemberLookup (
260 ec, Type, ".ctor", MemberTypes.Constructor,
261 BindingFlags.Public | BindingFlags.Instance, Location);
264 Error_AttributeConstructorMismatch (Location);
268 MethodBase constructor = Invocation.OverloadResolve (
269 ec, (MethodGroupExpr) mg, pos_args, Location);
271 if (constructor == null) {
272 Error_AttributeConstructorMismatch (Location);
276 PropertyInfo [] prop_info_arr = new PropertyInfo [prop_infos.Count];
277 FieldInfo [] field_info_arr = new FieldInfo [field_infos.Count];
278 object [] field_values_arr = new object [field_values.Count];
279 object [] prop_values_arr = new object [prop_values.Count];
281 field_infos.CopyTo (field_info_arr, 0);
282 field_values.CopyTo (field_values_arr, 0);
284 prop_values.CopyTo (prop_values_arr, 0);
285 prop_infos.CopyTo (prop_info_arr, 0);
288 cb = new CustomAttributeBuilder (
289 (ConstructorInfo) constructor, pos_values,
290 prop_info_arr, prop_values_arr,
291 field_info_arr, field_values_arr);
295 // using System.ComponentModel;
296 // [DefaultValue (CollectionChangeAction.Add)]
297 // class X { static void Main () {} }
301 "The compiler can not encode this attribute in .NET due to\n" +
302 "\ta bug in the .NET runtime. Try the Mono runtime");
308 static string GetValidPlaces (Attribute attr)
310 StringBuilder sb = new StringBuilder ();
311 AttributeTargets targets = 0;
313 TypeContainer a = TypeManager.LookupAttr (attr.Type);
317 System.Attribute [] attrs = null;
320 attrs = System.Attribute.GetCustomAttributes (attr.Type);
323 Report.Error (-20, attr.Location, "Cannot find attribute type " + attr.Name +
324 " (maybe you forgot to set the usage using the" +
325 " AttributeUsage attribute ?).");
329 foreach (System.Attribute tmp in attrs)
330 if (tmp is AttributeUsageAttribute)
331 targets = ((AttributeUsageAttribute) tmp).ValidOn;
336 if ((targets & AttributeTargets.Assembly) != 0)
337 sb.Append ("'assembly' ");
339 if ((targets & AttributeTargets.Class) != 0)
340 sb.Append ("'class' ");
342 if ((targets & AttributeTargets.Constructor) != 0)
343 sb.Append ("'constructor' ");
345 if ((targets & AttributeTargets.Delegate) != 0)
346 sb.Append ("'delegate' ");
348 if ((targets & AttributeTargets.Enum) != 0)
349 sb.Append ("'enum' ");
351 if ((targets & AttributeTargets.Event) != 0)
352 sb.Append ("'event' ");
354 if ((targets & AttributeTargets.Field) != 0)
355 sb.Append ("'field' ");
357 if ((targets & AttributeTargets.Interface) != 0)
358 sb.Append ("'interface' ");
360 if ((targets & AttributeTargets.Method) != 0)
361 sb.Append ("'method' ");
363 if ((targets & AttributeTargets.Module) != 0)
364 sb.Append ("'module' ");
366 if ((targets & AttributeTargets.Parameter) != 0)
367 sb.Append ("'parameter' ");
369 if ((targets & AttributeTargets.Property) != 0)
370 sb.Append ("'property' ");
372 if ((targets & AttributeTargets.ReturnValue) != 0)
373 sb.Append ("'return value' ");
375 if ((targets & AttributeTargets.Struct) != 0)
376 sb.Append ("'struct' ");
378 return sb.ToString ();
382 public static void Error_AttributeNotValidForElement (Attribute a, Location loc)
385 592, loc, "Attribute '" + a.Name +
386 "' is not valid on this declaration type. " +
387 "It is valid on " + GetValidPlaces (a) + "declarations only.");
390 public static bool CheckAttribute (Attribute a, object element)
392 TypeContainer attr = TypeManager.LookupAttr (a.Type);
393 AttributeTargets targets = 0;
398 System.Attribute [] attrs = null;
401 attrs = System.Attribute.GetCustomAttributes (a.Type);
404 Report.Error (-20, a.Location, "Cannot find attribute type " + a.Name +
405 " (maybe you forgot to set the usage using the" +
406 " AttributeUsage attribute ?).");
410 foreach (System.Attribute tmp in attrs)
411 if (tmp is AttributeUsageAttribute)
412 targets = ((AttributeUsageAttribute) tmp).ValidOn;
414 targets = attr.Targets;
416 if (element is Class) {
417 if ((targets & AttributeTargets.Class) != 0)
422 } else if (element is Struct) {
423 if ((targets & AttributeTargets.Struct) != 0)
427 } else if (element is Constructor) {
428 if ((targets & AttributeTargets.Constructor) != 0)
432 } else if (element is Delegate) {
433 if ((targets & AttributeTargets.Delegate) != 0)
437 } else if (element is Enum) {
438 if ((targets & AttributeTargets.Enum) != 0)
442 } else if (element is Event || element is InterfaceEvent) {
443 if ((targets & AttributeTargets.Event) != 0)
447 } else if (element is Field || element is FieldBuilder) {
448 if ((targets & AttributeTargets.Field) != 0)
452 } else if (element is Interface) {
453 if ((targets & AttributeTargets.Interface) != 0)
457 } else if (element is Method || element is Operator || element is InterfaceMethod || element is Accessor) {
458 if ((targets & AttributeTargets.Method) != 0)
462 } else if (element is ParameterBuilder) {
463 if ((targets & AttributeTargets.Parameter) != 0)
467 } else if (element is Property || element is Indexer ||
468 element is InterfaceProperty || element is InterfaceIndexer) {
469 if ((targets & AttributeTargets.Property) != 0)
473 } else if (element is AssemblyBuilder){
474 if ((targets & AttributeTargets.Assembly) != 0)
484 // This method should be invoked to pull the IndexerName attribute from an
485 // Indexer if it exists.
487 public static string ScanForIndexerName (EmitContext ec, Attributes opt_attrs)
489 if (opt_attrs == null)
491 if (opt_attrs.AttributeSections == null)
494 foreach (AttributeSection asec in opt_attrs.AttributeSections) {
495 if (asec.Attributes == null)
498 foreach (Attribute a in asec.Attributes){
499 if (a.ResolveType (ec) == null)
502 if (a.Type != TypeManager.indexer_name_type)
506 // So we have found an IndexerName, pull the data out.
508 if (a.Arguments == null || a.Arguments [0] == null){
509 Error_AttributeConstructorMismatch (a.Location);
512 ArrayList pos_args = (ArrayList) a.Arguments [0];
513 if (pos_args.Count == 0){
514 Error_AttributeConstructorMismatch (a.Location);
518 Argument arg = (Argument) pos_args [0];
519 if (!arg.Resolve (ec, a.Location))
522 Expression e = arg.Expr;
523 if (!(e is StringConstant)){
524 Error_AttributeConstructorMismatch (a.Location);
529 // Remove the attribute from the list
531 asec.Attributes.Remove (a);
533 return (((StringConstant) e).Value);
540 // Applies the attributes to the `builder'.
542 public static void ApplyAttributes (EmitContext ec, object builder, object kind,
543 Attributes opt_attrs, Location loc)
545 if (opt_attrs == null)
547 if (opt_attrs.AttributeSections == null)
550 foreach (AttributeSection asec in opt_attrs.AttributeSections) {
551 if (asec.Attributes == null)
554 if (asec.Target == "assembly" && !(builder is AssemblyBuilder))
557 foreach (Attribute a in asec.Attributes) {
558 CustomAttributeBuilder cb = a.Resolve (ec);
563 if (!(kind is TypeContainer))
564 if (!CheckAttribute (a, kind)) {
565 Error_AttributeNotValidForElement (a, loc);
569 if (kind is Method || kind is Operator || kind is InterfaceMethod) {
570 if (a.Type == TypeManager.methodimpl_attr_type) {
571 if (a.ImplOptions == MethodImplOptions.InternalCall)
572 ((MethodBuilder) builder).
573 SetImplementationFlags (
574 MethodImplAttributes.InternalCall |
575 MethodImplAttributes.Runtime);
576 } else if (a.Type != TypeManager.dllimport_type){
577 ((MethodBuilder) builder).SetCustomAttribute (cb);
579 } else if (kind is Constructor) {
580 ((ConstructorBuilder) builder).SetCustomAttribute (cb);
581 } else if (kind is Field) {
582 ((FieldBuilder) builder).SetCustomAttribute (cb);
583 } else if (kind is Property || kind is Indexer ||
584 kind is InterfaceProperty || kind is InterfaceIndexer) {
585 ((PropertyBuilder) builder).SetCustomAttribute (cb);
586 } else if (kind is Event || kind is InterfaceEvent) {
587 ((MyEventBuilder) builder).SetCustomAttribute (cb);
588 } else if (kind is ParameterBuilder) {
590 if (a.Type == TypeManager.marshal_as_attr_type) {
591 UnmanagedMarshal marshal =
592 UnmanagedMarshal.DefineUnmanagedMarshal (a.UnmanagedType);
594 ((ParameterBuilder) builder).SetMarshal (marshal);
596 ((ParameterBuilder) builder).SetCustomAttribute (cb);
598 } else if (kind is Enum) {
599 ((TypeBuilder) builder).SetCustomAttribute (cb);
601 } else if (kind is TypeContainer) {
602 TypeContainer tc = (TypeContainer) kind;
605 tc.Targets = a.Targets;
606 tc.AllowMultiple = a.AllowMultiple;
607 tc.Inherited = a.Inherited;
609 } else if (a.Type == TypeManager.default_member_type) {
610 if (tc.Indexers != null) {
611 Report.Error (646, loc,
612 "Cannot specify the DefaultMember attribute on" +
613 " a type containing an indexer");
618 if (!CheckAttribute (a, kind)) {
619 Error_AttributeNotValidForElement (a, loc);
625 ((TypeBuilder) builder).SetCustomAttribute (cb);
626 } catch (System.ArgumentException) {
629 "The CharSet named property on StructLayout\n"+
630 "\tdoes not work correctly on Microsoft.NET\n"+
631 "\tYou might want to remove the CharSet declaration\n"+
632 "\tor compile using the Mono runtime instead of the\n"+
633 "\tMicrosoft .NET runtime");
636 } else if (kind is AssemblyBuilder){
637 ((AssemblyBuilder) builder).SetCustomAttribute (cb);
638 } else if (kind is ModuleBuilder) {
639 ((ModuleBuilder) builder).SetCustomAttribute (cb);
640 } else if (kind is FieldBuilder) {
641 ((FieldBuilder) builder).SetCustomAttribute (cb);
642 } else if (kind is Accessor) {
643 ((MethodBuilder) builder).SetCustomAttribute (cb);
645 throw new Exception ("Unknown kind: " + kind);
650 public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name,
651 MethodAttributes flags, Type ret_type, Type [] param_types)
654 // We extract from the attribute the information we need
657 if (Arguments == null) {
658 Console.WriteLine ("Internal error : this is not supposed to happen !");
662 Type = CheckAttributeType (ec);
666 ArrayList named_args = new ArrayList ();
668 ArrayList pos_args = (ArrayList) Arguments [0];
669 if (Arguments.Count > 1)
670 named_args = (ArrayList) Arguments [1];
673 string dll_name = null;
675 Argument tmp = (Argument) pos_args [0];
677 if (!tmp.Resolve (ec, Location))
680 if (tmp.Expr is Constant)
681 dll_name = (string) ((Constant) tmp.Expr).GetValue ();
683 Error_AttributeArgumentNotValid ();
687 // Now we process the named arguments
688 CallingConvention cc = CallingConvention.Winapi;
689 CharSet charset = CharSet.Ansi;
690 bool preserve_sig = true;
691 bool exact_spelling = false;
692 bool set_last_err = false;
693 string entry_point = null;
695 for (int i = 0; i < named_args.Count; i++) {
697 DictionaryEntry de = (DictionaryEntry) named_args [i];
699 string member_name = (string) de.Key;
700 Argument a = (Argument) de.Value;
702 if (!a.Resolve (ec, Location))
705 Expression member = Expression.MemberLookup (
706 ec, Type, member_name,
707 MemberTypes.Field | MemberTypes.Property,
708 BindingFlags.Public | BindingFlags.Instance,
711 if (member == null || !(member is FieldExpr)) {
712 Error_InvalidNamedArgument (member_name);
716 if (member is FieldExpr) {
717 FieldExpr fe = (FieldExpr) member;
718 FieldInfo fi = fe.FieldInfo;
721 Error_InvalidNamedArgument (member_name);
725 if (a.Expr is Constant) {
726 Constant c = (Constant) a.Expr;
728 if (member_name == "CallingConvention")
729 cc = (CallingConvention) c.GetValue ();
730 else if (member_name == "CharSet")
731 charset = (CharSet) c.GetValue ();
732 else if (member_name == "EntryPoint")
733 entry_point = (string) c.GetValue ();
734 else if (member_name == "SetLastError")
735 set_last_err = (bool) c.GetValue ();
736 else if (member_name == "ExactSpelling")
737 exact_spelling = (bool) c.GetValue ();
738 else if (member_name == "PreserveSig")
739 preserve_sig = (bool) c.GetValue ();
741 Error_AttributeArgumentNotValid ();
748 MethodBuilder mb = builder.DefinePInvokeMethod (
749 name, dll_name, flags | MethodAttributes.HideBySig,
750 CallingConventions.Standard,
757 mb.SetImplementationFlags (MethodImplAttributes.PreserveSig);
764 public class AttributeSection {
766 public readonly string Target;
767 public readonly ArrayList Attributes;
769 public AttributeSection (string target, ArrayList attrs)
777 public class Attributes {
778 public ArrayList AttributeSections;
779 public Location Location;
781 public Attributes (AttributeSection a, Location loc)
783 AttributeSections = new ArrayList ();
784 AttributeSections.Add (a);
788 public void AddAttribute (AttributeSection a)
791 AttributeSections.Add (a);