2 // attribute.cs: Attribute Handler
\r
4 // Author: Ravi Pratap (ravi@ximian.com)
\r
6 // Licensed under the terms of the GNU GPL
\r
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)
\r
13 using System.Collections;
\r
14 using System.Reflection;
\r
15 using System.Reflection.Emit;
\r
16 using System.Runtime.InteropServices;
\r
17 using System.Runtime.CompilerServices;
\r
20 namespace Mono.CSharp {
\r
22 public class Attribute {
\r
23 public readonly string Name;
\r
24 public readonly ArrayList Arguments;
\r
31 // The following are only meaningful when the attribute
\r
32 // being emitted is one of the builtin ones
\r
34 AttributeTargets Targets;
\r
38 bool UsageAttr = false;
\r
40 MethodImplOptions ImplOptions;
\r
41 UnmanagedType UnmanagedType;
\r
42 CustomAttributeBuilder cb;
\r
44 public Attribute (string name, ArrayList args, Location loc)
\r
51 void Error_InvalidNamedArgument (string name)
\r
53 Report.Error (617, Location, "'" + name + "' is not a valid named attribute " +
\r
54 "argument. Named attribute arguments must be fields which are not " +
\r
55 "readonly, static or const, or properties with a set accessor which "+
\r
59 void Error_AttributeArgumentNotValid ()
\r
61 Report.Error (182, Location,
\r
62 "An attribute argument must be a constant expression, typeof " +
\r
63 "expression or array creation expression");
\r
66 static void Error_AttributeConstructorMismatch (Location loc)
\r
70 "Could not find a constructor for this argument list.");
\r
73 private Type CheckAttributeType (EmitContext ec) {
\r
75 bool isattributeclass = true;
\r
77 t = RootContext.LookupType (ec.DeclSpace, Name, true, Location);
\r
79 isattributeclass = t.IsSubclassOf (TypeManager.attribute_type);
\r
80 if (isattributeclass)
\r
83 t = RootContext.LookupType (ec.DeclSpace, Name + "Attribute", true, Location);
\r
85 if (t.IsSubclassOf (TypeManager.attribute_type))
\r
88 if (!isattributeclass) {
\r
89 Report.Error (616, Location, "'" + Name + "': is not an attribute class");
\r
93 Report.Error (616, Location, "'" + Name + "Attribute': is not an attribute class");
\r
97 246, Location, "Could not find attribute '" + Name + "' (are you" +
\r
98 " missing a using directive or an assembly reference ?)");
\r
102 public Type ResolveType (EmitContext ec)
\r
104 Type = CheckAttributeType (ec);
\r
109 public CustomAttributeBuilder Resolve (EmitContext ec)
\r
112 Type = CheckAttributeType (ec);
\r
114 bool MethodImplAttr = false;
\r
115 bool MarshalAsAttr = false;
\r
119 if (Type == TypeManager.attribute_usage_type)
\r
121 if (Type == TypeManager.methodimpl_attr_type)
\r
122 MethodImplAttr = true;
\r
123 if (Type == TypeManager.marshal_as_attr_type)
\r
124 MarshalAsAttr = true;
\r
126 // Now we extract the positional and named arguments
\r
128 ArrayList pos_args = new ArrayList ();
\r
129 ArrayList named_args = new ArrayList ();
\r
130 int pos_arg_count = 0;
\r
132 if (Arguments != null) {
\r
133 pos_args = (ArrayList) Arguments [0];
\r
134 if (pos_args != null)
\r
135 pos_arg_count = pos_args.Count;
\r
136 if (Arguments.Count > 1)
\r
137 named_args = (ArrayList) Arguments [1];
\r
140 object [] pos_values = new object [pos_arg_count];
\r
143 // First process positional arguments
\r
147 for (i = 0; i < pos_arg_count; i++) {
\r
148 Argument a = (Argument) pos_args [i];
\r
151 if (!a.Resolve (ec, Location))
\r
155 if (e is Constant) {
\r
156 pos_values [i] = ((Constant) e).GetValue ();
\r
157 } else if (e is TypeOf) {
\r
158 pos_values [i] = ((TypeOf) e).TypeArg;
\r
160 Error_AttributeArgumentNotValid ();
\r
165 this.Targets = (AttributeTargets) pos_values [0];
\r
167 if (MethodImplAttr)
\r
168 this.ImplOptions = (MethodImplOptions) pos_values [0];
\r
171 this.UnmanagedType =
\r
172 (System.Runtime.InteropServices.UnmanagedType) pos_values [0];
\r
176 // Now process named arguments
\r
179 ArrayList field_infos = new ArrayList ();
\r
180 ArrayList prop_infos = new ArrayList ();
\r
181 ArrayList field_values = new ArrayList ();
\r
182 ArrayList prop_values = new ArrayList ();
\r
184 for (i = 0; i < named_args.Count; i++) {
\r
185 DictionaryEntry de = (DictionaryEntry) named_args [i];
\r
186 string member_name = (string) de.Key;
\r
187 Argument a = (Argument) de.Value;
\r
190 if (!a.Resolve (ec, Location))
\r
193 Expression member = Expression.MemberLookup (
\r
194 ec, Type, member_name,
\r
195 MemberTypes.Field | MemberTypes.Property,
\r
196 BindingFlags.Public | BindingFlags.Instance,
\r
199 if (member == null || !(member is PropertyExpr || member is FieldExpr)) {
\r
200 Error_InvalidNamedArgument (member_name);
\r
205 if (member is PropertyExpr) {
\r
206 PropertyExpr pe = (PropertyExpr) member;
\r
207 PropertyInfo pi = pe.PropertyInfo;
\r
209 if (!pi.CanWrite) {
\r
210 Error_InvalidNamedArgument (member_name);
\r
214 if (e is Constant) {
\r
215 object o = ((Constant) e).GetValue ();
\r
216 prop_values.Add (o);
\r
219 if (member_name == "AllowMultiple")
\r
220 this.AllowMultiple = (bool) o;
\r
221 if (member_name == "Inherited")
\r
222 this.Inherited = (bool) o;
\r
226 Error_AttributeArgumentNotValid ();
\r
230 prop_infos.Add (pi);
\r
232 } else if (member is FieldExpr) {
\r
233 FieldExpr fe = (FieldExpr) member;
\r
234 FieldInfo fi = fe.FieldInfo;
\r
236 if (fi.IsInitOnly) {
\r
237 Error_InvalidNamedArgument (member_name);
\r
242 // Handle charset here, and set the TypeAttributes
\r
244 if (e is Constant){
\r
245 object value = ((Constant) e).GetValue ();
\r
247 field_values.Add (value);
\r
249 Error_AttributeArgumentNotValid ();
\r
253 field_infos.Add (fi);
\r
257 Expression mg = Expression.MemberLookup (
\r
258 ec, Type, ".ctor", MemberTypes.Constructor,
\r
259 BindingFlags.Public | BindingFlags.Instance, Location);
\r
262 Error_AttributeConstructorMismatch (Location);
\r
266 MethodBase constructor = Invocation.OverloadResolve (
\r
267 ec, (MethodGroupExpr) mg, pos_args, Location);
\r
269 if (constructor == null) {
\r
270 Error_AttributeConstructorMismatch (Location);
\r
274 PropertyInfo [] prop_info_arr = new PropertyInfo [prop_infos.Count];
\r
275 FieldInfo [] field_info_arr = new FieldInfo [field_infos.Count];
\r
276 object [] field_values_arr = new object [field_values.Count];
\r
277 object [] prop_values_arr = new object [prop_values.Count];
\r
279 field_infos.CopyTo (field_info_arr, 0);
\r
280 field_values.CopyTo (field_values_arr, 0);
\r
282 prop_values.CopyTo (prop_values_arr, 0);
\r
283 prop_infos.CopyTo (prop_info_arr, 0);
\r
286 cb = new CustomAttributeBuilder (
\r
287 (ConstructorInfo) constructor, pos_values,
\r
288 prop_info_arr, prop_values_arr,
\r
289 field_info_arr, field_values_arr);
\r
293 // using System.ComponentModel;
\r
294 // [DefaultValue (CollectionChangeAction.Add)]
\r
295 // class X { static void Main () {} }
\r
299 "The compiler can not encode this attribute in .NET due to\n" +
\r
300 "\ta bug in the .NET runtime. Try the Mono runtime");
\r
306 static string GetValidPlaces (Attribute attr)
\r
308 StringBuilder sb = new StringBuilder ();
\r
309 AttributeTargets targets = 0;
\r
311 TypeContainer a = TypeManager.LookupAttr (attr.Type);
\r
315 System.Attribute [] attrs = null;
\r
318 attrs = System.Attribute.GetCustomAttributes (attr.Type);
\r
321 Report.Error (-20, attr.Location, "Cannot find attribute type " + attr.Name +
\r
322 " (maybe you forgot to set the usage using the" +
\r
323 " AttributeUsage attribute ?).");
\r
327 foreach (System.Attribute tmp in attrs)
\r
328 if (tmp is AttributeUsageAttribute)
\r
329 targets = ((AttributeUsageAttribute) tmp).ValidOn;
\r
331 targets = a.Targets;
\r
334 if ((targets & AttributeTargets.Assembly) != 0)
\r
335 sb.Append ("'assembly' ");
\r
337 if ((targets & AttributeTargets.Class) != 0)
\r
338 sb.Append ("'class' ");
\r
340 if ((targets & AttributeTargets.Constructor) != 0)
\r
341 sb.Append ("'constructor' ");
\r
343 if ((targets & AttributeTargets.Delegate) != 0)
\r
344 sb.Append ("'delegate' ");
\r
346 if ((targets & AttributeTargets.Enum) != 0)
\r
347 sb.Append ("'enum' ");
\r
349 if ((targets & AttributeTargets.Event) != 0)
\r
350 sb.Append ("'event' ");
\r
352 if ((targets & AttributeTargets.Field) != 0)
\r
353 sb.Append ("'field' ");
\r
355 if ((targets & AttributeTargets.Interface) != 0)
\r
356 sb.Append ("'interface' ");
\r
358 if ((targets & AttributeTargets.Method) != 0)
\r
359 sb.Append ("'method' ");
\r
361 if ((targets & AttributeTargets.Module) != 0)
\r
362 sb.Append ("'module' ");
\r
364 if ((targets & AttributeTargets.Parameter) != 0)
\r
365 sb.Append ("'parameter' ");
\r
367 if ((targets & AttributeTargets.Property) != 0)
\r
368 sb.Append ("'property' ");
\r
370 if ((targets & AttributeTargets.ReturnValue) != 0)
\r
371 sb.Append ("'return value' ");
\r
373 if ((targets & AttributeTargets.Struct) != 0)
\r
374 sb.Append ("'struct' ");
\r
376 return sb.ToString ();
\r
380 public static void Error_AttributeNotValidForElement (Attribute a, Location loc)
\r
383 592, loc, "Attribute '" + a.Name +
\r
384 "' is not valid on this declaration type. " +
\r
385 "It is valid on " + GetValidPlaces (a) + "declarations only.");
\r
388 public static bool CheckAttribute (Attribute a, object element)
\r
390 TypeContainer attr = TypeManager.LookupAttr (a.Type);
\r
391 AttributeTargets targets = 0;
\r
394 if (attr == null) {
\r
396 System.Attribute [] attrs = null;
\r
399 attrs = System.Attribute.GetCustomAttributes (a.Type);
\r
402 Report.Error (-20, a.Location, "Cannot find attribute type " + a.Name +
\r
403 " (maybe you forgot to set the usage using the" +
\r
404 " AttributeUsage attribute ?).");
\r
408 foreach (System.Attribute tmp in attrs)
\r
409 if (tmp is AttributeUsageAttribute)
\r
410 targets = ((AttributeUsageAttribute) tmp).ValidOn;
\r
412 targets = attr.Targets;
\r
414 if (element is Class) {
\r
415 if ((targets & AttributeTargets.Class) != 0)
\r
420 } else if (element is Struct) {
\r
421 if ((targets & AttributeTargets.Struct) != 0)
\r
425 } else if (element is Constructor) {
\r
426 if ((targets & AttributeTargets.Constructor) != 0)
\r
430 } else if (element is Delegate) {
\r
431 if ((targets & AttributeTargets.Delegate) != 0)
\r
435 } else if (element is Enum) {
\r
436 if ((targets & AttributeTargets.Enum) != 0)
\r
440 } else if (element is Event || element is InterfaceEvent) {
\r
441 if ((targets & AttributeTargets.Event) != 0)
\r
445 } else if (element is Field) {
\r
446 if ((targets & AttributeTargets.Field) != 0)
\r
450 } else if (element is Interface) {
\r
451 if ((targets & AttributeTargets.Interface) != 0)
\r
455 } else if (element is Method || element is Operator || element is InterfaceMethod) {
\r
456 if ((targets & AttributeTargets.Method) != 0)
\r
460 } else if (element is ParameterBuilder) {
\r
461 if ((targets & AttributeTargets.Parameter) != 0)
\r
465 } else if (element is Property || element is Indexer ||
\r
466 element is InterfaceProperty || element is InterfaceIndexer || element is Accessor) {
\r
467 if ((targets & AttributeTargets.Property) != 0)
\r
471 } else if (element is AssemblyBuilder){
\r
472 if ((targets & AttributeTargets.Assembly) != 0)
\r
482 // This method should be invoked to pull the IndexerName attribute from an
\r
483 // Indexer if it exists.
\r
485 public static string ScanForIndexerName (EmitContext ec, Attributes opt_attrs)
\r
487 if (opt_attrs == null)
\r
489 if (opt_attrs.AttributeSections == null)
\r
492 foreach (AttributeSection asec in opt_attrs.AttributeSections) {
\r
493 if (asec.Attributes == null)
\r
496 foreach (Attribute a in asec.Attributes){
\r
497 if (a.ResolveType (ec) == null)
\r
500 if (a.Type != TypeManager.indexer_name_type)
\r
504 // So we have found an IndexerName, pull the data out.
\r
506 if (a.Arguments == null || a.Arguments [0] == null){
\r
507 Error_AttributeConstructorMismatch (a.Location);
\r
510 ArrayList pos_args = (ArrayList) a.Arguments [0];
\r
511 if (pos_args.Count == 0){
\r
512 Error_AttributeConstructorMismatch (a.Location);
\r
516 Argument arg = (Argument) pos_args [0];
\r
517 if (!arg.Resolve (ec, a.Location))
\r
520 Expression e = arg.Expr;
\r
521 if (!(e is StringConstant)){
\r
522 Error_AttributeConstructorMismatch (a.Location);
\r
527 // Remove the attribute from the list
\r
529 asec.Attributes.Remove (a);
\r
531 return (((StringConstant) e).Value);
\r
538 // Applies the attributes to the `builder'.
\r
540 public static void ApplyAttributes (EmitContext ec, object builder, object kind,
\r
541 Attributes opt_attrs, Location loc)
\r
543 if (opt_attrs == null)
\r
545 if (opt_attrs.AttributeSections == null)
\r
548 foreach (AttributeSection asec in opt_attrs.AttributeSections) {
\r
549 if (asec.Attributes == null)
\r
552 if (asec.Target == "assembly" && !(builder is AssemblyBuilder))
\r
555 foreach (Attribute a in asec.Attributes) {
\r
556 CustomAttributeBuilder cb = a.Resolve (ec);
\r
561 if (!(kind is TypeContainer))
\r
562 if (!CheckAttribute (a, kind)) {
\r
563 Error_AttributeNotValidForElement (a, loc);
\r
567 if (kind is Method || kind is Operator || kind is InterfaceMethod) {
\r
569 if (a.Type == TypeManager.methodimpl_attr_type) {
\r
570 if (a.ImplOptions == MethodImplOptions.InternalCall)
\r
571 ((MethodBuilder) builder).SetImplementationFlags (
\r
572 MethodImplAttributes.InternalCall |
\r
573 MethodImplAttributes.Runtime);
\r
574 } else if (a.Type != TypeManager.dllimport_type)
\r
575 ((MethodBuilder) builder).SetCustomAttribute (cb);
\r
577 } else if (kind is Constructor) {
\r
578 ((ConstructorBuilder) builder).SetCustomAttribute (cb);
\r
579 } else if (kind is Field) {
\r
580 ((FieldBuilder) builder).SetCustomAttribute (cb);
\r
581 } else if (kind is Property || kind is Indexer ||
\r
582 kind is InterfaceProperty || kind is InterfaceIndexer) {
\r
583 ((PropertyBuilder) builder).SetCustomAttribute (cb);
\r
584 } else if (kind is Event || kind is InterfaceEvent) {
\r
585 ((MyEventBuilder) builder).SetCustomAttribute (cb);
\r
586 } else if (kind is ParameterBuilder) {
\r
588 if (a.Type == TypeManager.marshal_as_attr_type) {
\r
589 UnmanagedMarshal marshal =
\r
590 UnmanagedMarshal.DefineUnmanagedMarshal (a.UnmanagedType);
\r
592 ((ParameterBuilder) builder).SetMarshal (marshal);
\r
594 ((ParameterBuilder) builder).SetCustomAttribute (cb);
\r
596 } else if (kind is Enum) {
\r
597 ((TypeBuilder) builder).SetCustomAttribute (cb);
\r
599 } else if (kind is TypeContainer) {
\r
600 TypeContainer tc = (TypeContainer) kind;
\r
603 tc.Targets = a.Targets;
\r
604 tc.AllowMultiple = a.AllowMultiple;
\r
605 tc.Inherited = a.Inherited;
\r
607 } else if (a.Type == TypeManager.default_member_type) {
\r
608 if (tc.Indexers != null) {
\r
609 Report.Error (646, loc,
\r
610 "Cannot specify the DefaultMember attribute on" +
\r
611 " a type containing an indexer");
\r
616 if (!CheckAttribute (a, kind)) {
\r
617 Error_AttributeNotValidForElement (a, loc);
\r
623 ((TypeBuilder) builder).SetCustomAttribute (cb);
\r
624 } catch (System.ArgumentException) {
\r
627 "The CharSet named property on StructLayout\n"+
\r
628 "\tdoes not work correctly on Microsoft.NET\n"+
\r
629 "\tYou might want to remove the CharSet declaration\n"+
\r
630 "\tor compile using the Mono runtime instead of the\n"+
\r
631 "\tMicrosoft .NET runtime");
\r
634 } else if (kind is AssemblyBuilder){
\r
635 ((AssemblyBuilder) builder).SetCustomAttribute (cb);
\r
636 } else if (kind is ModuleBuilder) {
\r
637 ((ModuleBuilder) builder).SetCustomAttribute (cb);
\r
638 } else if (kind is Accessor) {
\r
639 ((MethodBuilder) builder).SetCustomAttribute (cb);
\r
641 throw new Exception ("Unknown kind: " + kind);
\r
646 public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name,
\r
647 MethodAttributes flags, Type ret_type, Type [] param_types)
\r
650 // We extract from the attribute the information we need
\r
653 if (Arguments == null) {
\r
654 Console.WriteLine ("Internal error : this is not supposed to happen !");
\r
658 Type = CheckAttributeType (ec);
\r
662 ArrayList named_args = new ArrayList ();
\r
664 ArrayList pos_args = (ArrayList) Arguments [0];
\r
665 if (Arguments.Count > 1)
\r
666 named_args = (ArrayList) Arguments [1];
\r
669 string dll_name = null;
\r
671 Argument tmp = (Argument) pos_args [0];
\r
673 if (!tmp.Resolve (ec, Location))
\r
676 if (tmp.Expr is Constant)
\r
677 dll_name = (string) ((Constant) tmp.Expr).GetValue ();
\r
679 Error_AttributeArgumentNotValid ();
\r
683 // Now we process the named arguments
\r
684 CallingConvention cc = CallingConvention.Winapi;
\r
685 CharSet charset = CharSet.Ansi;
\r
686 bool preserve_sig = true;
\r
687 bool exact_spelling = false;
\r
688 bool set_last_err = false;
\r
689 string entry_point = null;
\r
691 for (int i = 0; i < named_args.Count; i++) {
\r
693 DictionaryEntry de = (DictionaryEntry) named_args [i];
\r
695 string member_name = (string) de.Key;
\r
696 Argument a = (Argument) de.Value;
\r
698 if (!a.Resolve (ec, Location))
\r
701 Expression member = Expression.MemberLookup (
\r
702 ec, Type, member_name,
\r
703 MemberTypes.Field | MemberTypes.Property,
\r
704 BindingFlags.Public | BindingFlags.Instance,
\r
707 if (member == null || !(member is FieldExpr)) {
\r
708 Error_InvalidNamedArgument (member_name);
\r
712 if (member is FieldExpr) {
\r
713 FieldExpr fe = (FieldExpr) member;
\r
714 FieldInfo fi = fe.FieldInfo;
\r
716 if (fi.IsInitOnly) {
\r
717 Error_InvalidNamedArgument (member_name);
\r
721 if (a.Expr is Constant) {
\r
722 Constant c = (Constant) a.Expr;
\r
724 if (member_name == "CallingConvention")
\r
725 cc = (CallingConvention) c.GetValue ();
\r
726 else if (member_name == "CharSet")
\r
727 charset = (CharSet) c.GetValue ();
\r
728 else if (member_name == "EntryPoint")
\r
729 entry_point = (string) c.GetValue ();
\r
730 else if (member_name == "SetLastError")
\r
731 set_last_err = (bool) c.GetValue ();
\r
732 else if (member_name == "ExactSpelling")
\r
733 exact_spelling = (bool) c.GetValue ();
\r
734 else if (member_name == "PreserveSig")
\r
735 preserve_sig = (bool) c.GetValue ();
\r
737 Error_AttributeArgumentNotValid ();
\r
744 MethodBuilder mb = builder.DefinePInvokeMethod (
\r
745 name, dll_name, flags | MethodAttributes.HideBySig,
\r
746 CallingConventions.Standard,
\r
753 mb.SetImplementationFlags (MethodImplAttributes.PreserveSig);
\r
760 public class AttributeSection {
\r
762 public readonly string Target;
\r
763 public readonly ArrayList Attributes;
\r
765 public AttributeSection (string target, ArrayList attrs)
\r
768 Attributes = attrs;
\r
773 public class Attributes {
\r
774 public ArrayList AttributeSections;
\r
775 public Location Location;
\r
777 public Attributes (AttributeSection a, Location loc)
\r
779 AttributeSections = new ArrayList ();
\r
780 AttributeSections.Add (a);
\r
784 public void AddAttribute (AttributeSection a)
\r
787 AttributeSections.Add (a);
\r