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
148 if (Type.Name.IndexOf ("DefaultValue") != -1){
149 Console.WriteLine ("HELLO");
153 for (i = 0; i < pos_arg_count; i++) {
154 Argument a = (Argument) pos_args [i];
157 if (!a.Resolve (ec, Location))
162 pos_values [i] = ((Constant) e).GetValue ();
163 } else if (e is TypeOf) {
164 pos_values [i] = ((TypeOf) e).TypeArg;
166 Error_AttributeArgumentNotValid ();
171 this.Targets = (AttributeTargets) pos_values [0];
174 this.ImplOptions = (MethodImplOptions) pos_values [0];
178 (System.Runtime.InteropServices.UnmanagedType) pos_values [0];
182 // Now process named arguments
185 ArrayList field_infos = new ArrayList ();
186 ArrayList prop_infos = new ArrayList ();
187 ArrayList field_values = new ArrayList ();
188 ArrayList prop_values = new ArrayList ();
190 for (i = 0; i < named_args.Count; i++) {
191 DictionaryEntry de = (DictionaryEntry) named_args [i];
192 string member_name = (string) de.Key;
193 Argument a = (Argument) de.Value;
196 if (!a.Resolve (ec, Location))
199 Expression member = Expression.MemberLookup (
200 ec, Type, member_name,
201 MemberTypes.Field | MemberTypes.Property,
202 BindingFlags.Public | BindingFlags.Instance,
205 if (member == null || !(member is PropertyExpr || member is FieldExpr)) {
206 Error_InvalidNamedArgument (member_name);
211 if (member is PropertyExpr) {
212 PropertyExpr pe = (PropertyExpr) member;
213 PropertyInfo pi = pe.PropertyInfo;
216 Error_InvalidNamedArgument (member_name);
221 object o = ((Constant) e).GetValue ();
225 if (member_name == "AllowMultiple")
226 this.AllowMultiple = (bool) o;
227 if (member_name == "Inherited")
228 this.Inherited = (bool) o;
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);
255 Error_AttributeArgumentNotValid ();
259 field_infos.Add (fi);
263 Expression mg = Expression.MemberLookup (
264 ec, Type, ".ctor", MemberTypes.Constructor,
265 BindingFlags.Public | BindingFlags.Instance, Location);
268 Error_AttributeConstructorMismatch (Location);
272 MethodBase constructor = Invocation.OverloadResolve (
273 ec, (MethodGroupExpr) mg, pos_args, Location);
275 if (constructor == null) {
276 Error_AttributeConstructorMismatch (Location);
280 PropertyInfo [] prop_info_arr = new PropertyInfo [prop_infos.Count];
281 FieldInfo [] field_info_arr = new FieldInfo [field_infos.Count];
282 object [] field_values_arr = new object [field_values.Count];
283 object [] prop_values_arr = new object [prop_values.Count];
285 field_infos.CopyTo (field_info_arr, 0);
286 field_values.CopyTo (field_values_arr, 0);
288 prop_values.CopyTo (prop_values_arr, 0);
289 prop_infos.CopyTo (prop_info_arr, 0);
292 cb = new CustomAttributeBuilder (
293 (ConstructorInfo) constructor, pos_values,
294 prop_info_arr, prop_values_arr,
295 field_info_arr, field_values_arr);
299 // using System.ComponentModel;
300 // [DefaultValue (CollectionChangeAction.Add)]
301 // class X { static void Main () {} }
305 "The compiler can not encode this attribute in .NET due to\n" +
306 "\ta bug in the .NET runtime. Try the Mono runtime");
312 static string GetValidPlaces (Attribute attr)
314 StringBuilder sb = new StringBuilder ();
315 AttributeTargets targets = 0;
317 TypeContainer a = TypeManager.LookupAttr (attr.Type);
321 System.Attribute [] attrs = null;
324 attrs = System.Attribute.GetCustomAttributes (attr.Type);
327 Report.Error (-20, attr.Location, "Cannot find attribute type " + attr.Name +
328 " (maybe you forgot to set the usage using the" +
329 " AttributeUsage attribute ?).");
333 foreach (System.Attribute tmp in attrs)
334 if (tmp is AttributeUsageAttribute)
335 targets = ((AttributeUsageAttribute) tmp).ValidOn;
340 if ((targets & AttributeTargets.Assembly) != 0)
341 sb.Append ("'assembly' ");
343 if ((targets & AttributeTargets.Class) != 0)
344 sb.Append ("'class' ");
346 if ((targets & AttributeTargets.Constructor) != 0)
347 sb.Append ("'constructor' ");
349 if ((targets & AttributeTargets.Delegate) != 0)
350 sb.Append ("'delegate' ");
352 if ((targets & AttributeTargets.Enum) != 0)
353 sb.Append ("'enum' ");
355 if ((targets & AttributeTargets.Event) != 0)
356 sb.Append ("'event' ");
358 if ((targets & AttributeTargets.Field) != 0)
359 sb.Append ("'field' ");
361 if ((targets & AttributeTargets.Interface) != 0)
362 sb.Append ("'interface' ");
364 if ((targets & AttributeTargets.Method) != 0)
365 sb.Append ("'method' ");
367 if ((targets & AttributeTargets.Module) != 0)
368 sb.Append ("'module' ");
370 if ((targets & AttributeTargets.Parameter) != 0)
371 sb.Append ("'parameter' ");
373 if ((targets & AttributeTargets.Property) != 0)
374 sb.Append ("'property' ");
376 if ((targets & AttributeTargets.ReturnValue) != 0)
377 sb.Append ("'return value' ");
379 if ((targets & AttributeTargets.Struct) != 0)
380 sb.Append ("'struct' ");
382 return sb.ToString ();
386 public static void Error_AttributeNotValidForElement (Attribute a, Location loc)
389 592, loc, "Attribute '" + a.Name +
390 "' is not valid on this declaration type. " +
391 "It is valid on " + GetValidPlaces (a) + "declarations only.");
394 public static bool CheckAttribute (Attribute a, object element)
396 TypeContainer attr = TypeManager.LookupAttr (a.Type);
397 AttributeTargets targets = 0;
402 System.Attribute [] attrs = null;
405 attrs = System.Attribute.GetCustomAttributes (a.Type);
408 Report.Error (-20, a.Location, "Cannot find attribute type " + a.Name +
409 " (maybe you forgot to set the usage using the" +
410 " AttributeUsage attribute ?).");
414 foreach (System.Attribute tmp in attrs)
415 if (tmp is AttributeUsageAttribute)
416 targets = ((AttributeUsageAttribute) tmp).ValidOn;
418 targets = attr.Targets;
420 if (element is Class) {
421 if ((targets & AttributeTargets.Class) != 0)
426 } else if (element is Struct) {
427 if ((targets & AttributeTargets.Struct) != 0)
431 } else if (element is Constructor) {
432 if ((targets & AttributeTargets.Constructor) != 0)
436 } else if (element is Delegate) {
437 if ((targets & AttributeTargets.Delegate) != 0)
441 } else if (element is Enum) {
442 if ((targets & AttributeTargets.Enum) != 0)
446 } else if (element is Event || element is InterfaceEvent) {
447 if ((targets & AttributeTargets.Event) != 0)
451 } else if (element is Field || element is FieldBuilder) {
452 if ((targets & AttributeTargets.Field) != 0)
456 } else if (element is Interface) {
457 if ((targets & AttributeTargets.Interface) != 0)
461 } else if (element is Method || element is Operator || element is InterfaceMethod || element is Accessor) {
462 if ((targets & AttributeTargets.Method) != 0)
466 } else if (element is ParameterBuilder) {
467 if ((targets & AttributeTargets.Parameter) != 0)
471 } else if (element is Property || element is Indexer ||
472 element is InterfaceProperty || element is InterfaceIndexer) {
473 if ((targets & AttributeTargets.Property) != 0)
477 } else if (element is AssemblyBuilder){
478 if ((targets & AttributeTargets.Assembly) != 0)
488 // This method should be invoked to pull the IndexerName attribute from an
489 // Indexer if it exists.
491 public static string ScanForIndexerName (EmitContext ec, Attributes opt_attrs)
493 if (opt_attrs == null)
495 if (opt_attrs.AttributeSections == null)
498 foreach (AttributeSection asec in opt_attrs.AttributeSections) {
499 if (asec.Attributes == null)
502 foreach (Attribute a in asec.Attributes){
503 if (a.ResolveType (ec) == null)
506 if (a.Type != TypeManager.indexer_name_type)
510 // So we have found an IndexerName, pull the data out.
512 if (a.Arguments == null || a.Arguments [0] == null){
513 Error_AttributeConstructorMismatch (a.Location);
516 ArrayList pos_args = (ArrayList) a.Arguments [0];
517 if (pos_args.Count == 0){
518 Error_AttributeConstructorMismatch (a.Location);
522 Argument arg = (Argument) pos_args [0];
523 if (!arg.Resolve (ec, a.Location))
526 Expression e = arg.Expr;
527 if (!(e is StringConstant)){
528 Error_AttributeConstructorMismatch (a.Location);
533 // Remove the attribute from the list
535 asec.Attributes.Remove (a);
537 return (((StringConstant) e).Value);
544 // Applies the attributes to the `builder'.
546 public static void ApplyAttributes (EmitContext ec, object builder, object kind,
547 Attributes opt_attrs, Location loc)
549 if (opt_attrs == null)
551 if (opt_attrs.AttributeSections == null)
554 foreach (AttributeSection asec in opt_attrs.AttributeSections) {
555 if (asec.Attributes == null)
558 if (asec.Target == "assembly" && !(builder is AssemblyBuilder))
561 foreach (Attribute a in asec.Attributes) {
562 CustomAttributeBuilder cb = a.Resolve (ec);
567 if (!(kind is TypeContainer))
568 if (!CheckAttribute (a, kind)) {
569 Error_AttributeNotValidForElement (a, loc);
573 if (kind is Method || kind is Operator || kind is InterfaceMethod) {
575 if (a.Type == TypeManager.methodimpl_attr_type) {
576 if (a.ImplOptions == MethodImplOptions.InternalCall)
577 ((MethodBuilder) builder).SetImplementationFlags (
578 MethodImplAttributes.InternalCall |
579 MethodImplAttributes.Runtime);
580 } else if (a.Type != TypeManager.dllimport_type)
581 ((MethodBuilder) builder).SetCustomAttribute (cb);
583 } else if (kind is Constructor) {
584 ((ConstructorBuilder) builder).SetCustomAttribute (cb);
585 } else if (kind is Field) {
586 ((FieldBuilder) builder).SetCustomAttribute (cb);
587 } else if (kind is Property || kind is Indexer ||
588 kind is InterfaceProperty || kind is InterfaceIndexer) {
589 ((PropertyBuilder) builder).SetCustomAttribute (cb);
590 } else if (kind is Event || kind is InterfaceEvent) {
591 ((MyEventBuilder) builder).SetCustomAttribute (cb);
592 } else if (kind is ParameterBuilder) {
594 if (a.Type == TypeManager.marshal_as_attr_type) {
595 UnmanagedMarshal marshal =
596 UnmanagedMarshal.DefineUnmanagedMarshal (a.UnmanagedType);
598 ((ParameterBuilder) builder).SetMarshal (marshal);
600 ((ParameterBuilder) builder).SetCustomAttribute (cb);
602 } else if (kind is Enum) {
603 ((TypeBuilder) builder).SetCustomAttribute (cb);
605 } else if (kind is TypeContainer) {
606 TypeContainer tc = (TypeContainer) kind;
609 tc.Targets = a.Targets;
610 tc.AllowMultiple = a.AllowMultiple;
611 tc.Inherited = a.Inherited;
613 } else if (a.Type == TypeManager.default_member_type) {
614 if (tc.Indexers != null) {
615 Report.Error (646, loc,
616 "Cannot specify the DefaultMember attribute on" +
617 " a type containing an indexer");
622 if (!CheckAttribute (a, kind)) {
623 Error_AttributeNotValidForElement (a, loc);
629 ((TypeBuilder) builder).SetCustomAttribute (cb);
630 } catch (System.ArgumentException) {
633 "The CharSet named property on StructLayout\n"+
634 "\tdoes not work correctly on Microsoft.NET\n"+
635 "\tYou might want to remove the CharSet declaration\n"+
636 "\tor compile using the Mono runtime instead of the\n"+
637 "\tMicrosoft .NET runtime");
640 } else if (kind is AssemblyBuilder){
641 ((AssemblyBuilder) builder).SetCustomAttribute (cb);
642 } else if (kind is ModuleBuilder) {
643 ((ModuleBuilder) builder).SetCustomAttribute (cb);
644 } else if (kind is FieldBuilder) {
645 ((FieldBuilder) builder).SetCustomAttribute (cb);
646 } else if (kind is Accessor) {
647 ((MethodBuilder) builder).SetCustomAttribute (cb);
649 throw new Exception ("Unknown kind: " + kind);
654 public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name,
655 MethodAttributes flags, Type ret_type, Type [] param_types)
658 // We extract from the attribute the information we need
661 if (Arguments == null) {
662 Console.WriteLine ("Internal error : this is not supposed to happen !");
666 Type = CheckAttributeType (ec);
670 ArrayList named_args = new ArrayList ();
672 ArrayList pos_args = (ArrayList) Arguments [0];
673 if (Arguments.Count > 1)
674 named_args = (ArrayList) Arguments [1];
677 string dll_name = null;
679 Argument tmp = (Argument) pos_args [0];
681 if (!tmp.Resolve (ec, Location))
684 if (tmp.Expr is Constant)
685 dll_name = (string) ((Constant) tmp.Expr).GetValue ();
687 Error_AttributeArgumentNotValid ();
691 // Now we process the named arguments
692 CallingConvention cc = CallingConvention.Winapi;
693 CharSet charset = CharSet.Ansi;
694 bool preserve_sig = true;
695 bool exact_spelling = false;
696 bool set_last_err = false;
697 string entry_point = null;
699 for (int i = 0; i < named_args.Count; i++) {
701 DictionaryEntry de = (DictionaryEntry) named_args [i];
703 string member_name = (string) de.Key;
704 Argument a = (Argument) de.Value;
706 if (!a.Resolve (ec, Location))
709 Expression member = Expression.MemberLookup (
710 ec, Type, member_name,
711 MemberTypes.Field | MemberTypes.Property,
712 BindingFlags.Public | BindingFlags.Instance,
715 if (member == null || !(member is FieldExpr)) {
716 Error_InvalidNamedArgument (member_name);
720 if (member is FieldExpr) {
721 FieldExpr fe = (FieldExpr) member;
722 FieldInfo fi = fe.FieldInfo;
725 Error_InvalidNamedArgument (member_name);
729 if (a.Expr is Constant) {
730 Constant c = (Constant) a.Expr;
732 if (member_name == "CallingConvention")
733 cc = (CallingConvention) c.GetValue ();
734 else if (member_name == "CharSet")
735 charset = (CharSet) c.GetValue ();
736 else if (member_name == "EntryPoint")
737 entry_point = (string) c.GetValue ();
738 else if (member_name == "SetLastError")
739 set_last_err = (bool) c.GetValue ();
740 else if (member_name == "ExactSpelling")
741 exact_spelling = (bool) c.GetValue ();
742 else if (member_name == "PreserveSig")
743 preserve_sig = (bool) c.GetValue ();
745 Error_AttributeArgumentNotValid ();
752 MethodBuilder mb = builder.DefinePInvokeMethod (
753 name, dll_name, flags | MethodAttributes.HideBySig,
754 CallingConventions.Standard,
761 mb.SetImplementationFlags (MethodImplAttributes.PreserveSig);
768 public class AttributeSection {
770 public readonly string Target;
771 public readonly ArrayList Attributes;
773 public AttributeSection (string target, ArrayList attrs)
781 public class Attributes {
782 public ArrayList AttributeSections;
783 public Location Location;
785 public Attributes (AttributeSection a, Location loc)
787 AttributeSections = new ArrayList ();
788 AttributeSections.Add (a);
792 public void AddAttribute (AttributeSection a)
795 AttributeSections.Add (a);