**** Merged from MCS ****
[mono-project.git] / mcs / mbas / attribute.cs
blob53533cb48306f3ee93948bb6236cc043a9f07b4c
1 //
2 // attribute.cs: Attribute Handler
3 //
4 // Author: Ravi Pratap (ravi@ximian.com)
5 //
6 // Licensed under the terms of the GNU GPL
7 //
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)
9 //
12 using System;
13 using System.Diagnostics;
14 using System.Collections;
15 using System.Collections.Specialized;
16 using System.Reflection;
17 using System.Reflection.Emit;
18 using System.Runtime.InteropServices;
19 using System.Runtime.CompilerServices;
20 using System.Text;
22 namespace Mono.MonoBASIC {
24 /// <summary>
25 /// Base class for objects that can have Attributes applied to them.
26 /// </summary>
27 public abstract class Attributable {
28 /// <summary>
29 /// Attributes for this type
30 /// </summary>
31 Attributes attributes;
33 public Attributable(Attributes attrs)
35 attributes = attrs;
38 public Attributes OptAttributes
40 get {
41 return attributes;
43 set {
44 attributes = value;
48 /// <summary>
49 /// Use member-specific procedure to apply attribute @a in @cb to the entity being built in @builder
50 /// </summary>
51 public abstract void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb);
52 /// <summary>
53 /// Returns one AttributeTarget for this element.
54 /// </summary>
55 public abstract AttributeTargets AttributeTargets { get; }
57 public class Attribute {
58 public readonly string ExplicitTarget;
59 public readonly string Name;
60 public readonly ArrayList Arguments;
62 public Location Location;
64 public Type Type;
66 // Is non-null if type is AttributeUsageAttribute
67 AttributeUsageAttribute usage_attribute;
69 public AttributeUsageAttribute UsageAttribute {
70 get {
71 return usage_attribute;
75 bool usage_attr = false;
77 MethodImplOptions ImplOptions;
78 UnmanagedType UnmanagedType;
79 CustomAttributeBuilder cb;
81 public Attribute (string name, ArrayList args, Location loc)
83 Name = name;
84 Arguments = args;
85 Location = loc;
88 public Attribute (string target, string name, ArrayList args, Location loc)
90 ExplicitTarget = target;
93 void Error_InvalidNamedArgument (string name)
95 Report.Error (617, Location, "'" + name + "' is not a valid named attribute " +
96 "argument. Named attribute arguments must be fields which are not " +
97 "readonly, static or const, or properties with a set accessor which "+
98 "are not static.");
101 void Error_AttributeArgumentNotValid ()
103 Report.Error (182, Location,
104 "An attribute argument must be a constant expression, typeof " +
105 "expression or array creation expression");
108 static void Error_AttributeConstructorMismatch (Location loc)
110 Report.Error (
111 -6, loc,
112 "Could not find a constructor for this argument list.");
115 private Type CheckAttributeType (EmitContext ec) {
116 Type t;
117 bool isattributeclass = true;
119 t = RootContext.LookupType (ec.DeclSpace, Name, true, Location);
120 if (t != null) {
121 isattributeclass = t.IsSubclassOf (TypeManager.attribute_type);
122 if (isattributeclass)
123 return t;
125 t = RootContext.LookupType (ec.DeclSpace, Name + "Attribute", true, Location);
126 if (t != null) {
127 if (t.IsSubclassOf (TypeManager.attribute_type))
128 return t;
130 if (!isattributeclass) {
131 Report.Error (616, Location, "'" + Name + "': is not an attribute class");
132 return null;
134 if (t != null) {
135 Report.Error (616, Location, "'" + Name + "Attribute': is not an attribute class");
136 return null;
138 Report.Error (
139 246, Location, "Could not find attribute '" + Name + "' (are you" +
140 " missing a using directive or an assembly reference ?)");
141 return null;
145 public Type ResolveType (EmitContext ec)
147 Type = CheckAttributeType (ec);
148 return Type;
152 public CustomAttributeBuilder Resolve (EmitContext ec)
154 if (Type == null)
155 Type = CheckAttributeType (ec);
156 if (Type == null)
157 return null;
159 bool MethodImplAttr = false;
160 bool MarshalAsAttr = false;
162 usage_attr = false;
164 if (Type == TypeManager.attribute_usage_type)
165 usage_attr = true;
166 if (Type == TypeManager.methodimpl_attr_type)
167 MethodImplAttr = true;
168 if (Type == TypeManager.marshal_as_attr_type)
169 MarshalAsAttr = true;
171 // Now we extract the positional and named arguments
173 ArrayList pos_args = new ArrayList ();
174 ArrayList named_args = new ArrayList ();
175 int pos_arg_count = 0;
177 if (Arguments != null) {
178 pos_args = (ArrayList) Arguments [0];
179 if (pos_args != null)
180 pos_arg_count = pos_args.Count;
181 if (Arguments.Count > 1)
182 named_args = (ArrayList) Arguments [1];
185 object [] pos_values = new object [pos_arg_count];
188 // First process positional arguments
191 int i;
192 for (i = 0; i < pos_arg_count; i++) {
193 Argument a = (Argument) pos_args [i];
194 Expression e;
196 if (!a.Resolve (ec, Location))
197 return null;
199 e = a.Expr;
201 if (e is Constant) {
202 pos_values [i] = ((Constant) e).GetValue ();
203 } else if (e is TypeOf) {
204 pos_values [i] = ((TypeOf) e).TypeArg;
205 } else {
206 Error_AttributeArgumentNotValid ();
207 return null;
210 if (usage_attr)
211 usage_attribute = new AttributeUsageAttribute ((AttributeTargets) pos_values [0]);
213 if (MethodImplAttr)
214 this.ImplOptions = (MethodImplOptions) pos_values [0];
216 if (MarshalAsAttr)
217 this.UnmanagedType =
218 (System.Runtime.InteropServices.UnmanagedType) pos_values [0];
222 // Now process named arguments
225 ArrayList field_infos = new ArrayList ();
226 ArrayList prop_infos = new ArrayList ();
227 ArrayList field_values = new ArrayList ();
228 ArrayList prop_values = new ArrayList ();
230 for (i = 0; i < named_args.Count; i++) {
231 DictionaryEntry de = (DictionaryEntry) named_args [i];
232 string member_name = (string) de.Key;
233 Argument a = (Argument) de.Value;
234 Expression e;
236 if (!a.Resolve (ec, Location))
237 return null;
239 Expression member = Expression.MemberLookup (
240 ec, Type, member_name,
241 MemberTypes.Field | MemberTypes.Property,
242 BindingFlags.Public | BindingFlags.Instance,
243 Location);
245 if (member == null || !(member is PropertyExpr || member is FieldExpr)) {
246 Error_InvalidNamedArgument (member_name);
247 return null;
250 e = a.Expr;
251 if (member is PropertyExpr) {
252 PropertyExpr pe = (PropertyExpr) member;
253 PropertyInfo pi = pe.PropertyInfo;
255 if (!pi.CanWrite) {
256 Error_InvalidNamedArgument (member_name);
257 return null;
260 if (e is Constant) {
261 object o = ((Constant) e).GetValue ();
262 prop_values.Add (o);
264 if (usage_attr) {
265 if (member_name == "AllowMultiple")
266 usage_attribute.AllowMultiple = (bool) o;
267 if (member_name == "Inherited")
268 usage_attribute.Inherited = (bool) o;
271 } else if (e is TypeOf) {
272 prop_values.Add (((TypeOf) e).TypeArg);
273 } else {
274 Error_AttributeArgumentNotValid ();
275 return null;
278 prop_infos.Add (pi);
280 } else if (member is FieldExpr) {
281 FieldExpr fe = (FieldExpr) member;
282 FieldInfo fi = fe.FieldInfo;
284 if (fi.IsInitOnly) {
285 Error_InvalidNamedArgument (member_name);
286 return null;
290 // Handle charset here, and set the TypeAttributes
292 if (e is Constant){
293 object value = ((Constant) e).GetValue ();
295 field_values.Add (value);
296 } else if (e is TypeOf) {
297 field_values.Add (((TypeOf) e).TypeArg);
298 } else {
299 Error_AttributeArgumentNotValid ();
300 return null;
303 field_infos.Add (fi);
307 Expression mg = Expression.MemberLookup (
308 ec, Type, ".ctor", MemberTypes.Constructor,
309 BindingFlags.Public | BindingFlags.Instance, Location);
311 if (mg == null) {
312 Error_AttributeConstructorMismatch (Location);
313 return null;
316 MethodBase constructor = Invocation.OverloadResolve (
317 ec, (MethodGroupExpr) mg, pos_args, Location);
319 if (constructor == null) {
320 Error_AttributeConstructorMismatch (Location);
321 return null;
325 // Now we perform some checks on the positional args as they
326 // cannot be null for a constructor which expects a parameter
327 // of type object
330 ParameterData pd = Invocation.GetParameterData (constructor);
332 for (int j = 0; j < pos_arg_count; ++j) {
333 Argument a = (Argument) pos_args [j];
335 if (a.Expr is NullLiteral && pd.ParameterType (j) == TypeManager.object_type) {
336 Error_AttributeArgumentNotValid ();
337 return null;
341 PropertyInfo [] prop_info_arr = new PropertyInfo [prop_infos.Count];
342 FieldInfo [] field_info_arr = new FieldInfo [field_infos.Count];
343 object [] field_values_arr = new object [field_values.Count];
344 object [] prop_values_arr = new object [prop_values.Count];
346 field_infos.CopyTo (field_info_arr, 0);
347 field_values.CopyTo (field_values_arr, 0);
349 prop_values.CopyTo (prop_values_arr, 0);
350 prop_infos.CopyTo (prop_info_arr, 0);
352 try {
353 cb = new CustomAttributeBuilder (
354 (ConstructorInfo) constructor, pos_values,
355 prop_info_arr, prop_values_arr,
356 field_info_arr, field_values_arr);
358 } catch (NullReferenceException) {
360 // Don't know what to do here
362 } catch {
364 // Sample:
365 // using System.ComponentModel;
366 // [DefaultValue (CollectionChangeAction.Add)]
367 // class X { static void Main () {} }
369 Report.Warning (
370 -23, Location,
371 "The compiler can not encode this attribute in .NET due to\n" +
372 "\ta bug in the .NET runtime. Try the Mono runtime");
375 return cb;
378 public string GetValidTargets ()
380 StringBuilder sb = new StringBuilder ();
381 AttributeTargets targets = GetAttributeUsage ().ValidOn;
383 if ((targets & AttributeTargets.Assembly) != 0)
384 sb.Append ("'assembly' ");
386 if ((targets & AttributeTargets.Class) != 0)
387 sb.Append ("'class' ");
389 if ((targets & AttributeTargets.Constructor) != 0)
390 sb.Append ("'constructor' ");
392 if ((targets & AttributeTargets.Delegate) != 0)
393 sb.Append ("'delegate' ");
395 if ((targets & AttributeTargets.Enum) != 0)
396 sb.Append ("'enum' ");
398 if ((targets & AttributeTargets.Event) != 0)
399 sb.Append ("'event' ");
401 if ((targets & AttributeTargets.Field) != 0)
402 sb.Append ("'field' ");
404 if ((targets & AttributeTargets.Interface) != 0)
405 sb.Append ("'interface' ");
407 if ((targets & AttributeTargets.Method) != 0)
408 sb.Append ("'method' ");
410 if ((targets & AttributeTargets.Module) != 0)
411 sb.Append ("'module' ");
413 if ((targets & AttributeTargets.Parameter) != 0)
414 sb.Append ("'parameter' ");
416 if ((targets & AttributeTargets.Property) != 0)
417 sb.Append ("'property' ");
419 if ((targets & AttributeTargets.ReturnValue) != 0)
420 sb.Append ("'return value' ");
422 if ((targets & AttributeTargets.Struct) != 0)
423 sb.Append ("'struct' ");
425 return sb.ToString ();
429 public static void Error_AttributeNotValidForElement (Attribute a, Location loc)
431 Report.Error (
432 592, loc, "Attribute '" + a.Name +
433 "' is not valid on this declaration type. " +
434 "It is valid on " + a.GetValidTargets () + "declarations only.");
437 public static bool CheckAttribute (Attribute a, object element)
439 TypeContainer attr = TypeManager.LookupClass (a.Type);
440 AttributeTargets targets = a.GetAttributeUsage ().ValidOn;
443 if (element is Class) {
444 if ((targets & AttributeTargets.Class) != 0)
445 return true;
446 else
447 return false;
449 } else if (element is Struct) {
450 if ((targets & AttributeTargets.Struct) != 0)
451 return true;
452 else
453 return false;
454 } else if (element is Constructor) {
455 if ((targets & AttributeTargets.Constructor) != 0)
456 return true;
457 else
458 return false;
459 } else if (element is Delegate) {
460 if ((targets & AttributeTargets.Delegate) != 0)
461 return true;
462 else
463 return false;
464 } else if (element is Enum) {
465 if ((targets & AttributeTargets.Enum) != 0)
466 return true;
467 else
468 return false;
469 } else if (element is Event /*|| element is InterfaceEvent*/) {
470 if ((targets & AttributeTargets.Event) != 0)
471 return true;
472 else
473 return false;
474 } else if (element is Field || element is FieldBuilder) {
475 if ((targets & AttributeTargets.Field) != 0)
476 return true;
477 else
478 return false;
479 } else if (element is Interface) {
480 if ((targets & AttributeTargets.Interface) != 0)
481 return true;
482 else
483 return false;
484 } else if (element is Method || element is Accessor) {
485 if ((targets & AttributeTargets.Method) != 0)
486 return true;
487 else
488 return false;
489 } else if (element is ParameterBuilder) {
490 if ((targets & AttributeTargets.Parameter) != 0)
491 return true;
492 else
493 return false;
494 } else if (element is Property /* || element is Indexer ||
495 element is InterfaceProperty || element is InterfaceIndexer*/) {
496 if ((targets & AttributeTargets.Property) != 0)
497 return true;
498 else
499 return false;
500 } else if (element is AssemblyBuilder){
501 if ((targets & AttributeTargets.Assembly) != 0)
502 return true;
503 else
504 return false;
505 } else if (element is ModuleBuilder){
506 if ((targets & AttributeTargets.Module) != 0)
507 return true;
508 else
509 return false;
512 return false;
515 /// <summary>
516 /// Returns AttributeUsage attribute for this type
517 /// </summary>
518 public AttributeUsageAttribute GetAttributeUsage ()
520 Class attr_class = (Class) TypeManager.LookupClass (Type);
522 if (attr_class == null) {
523 object[] usage_attr = Type.GetCustomAttributes (TypeManager.attribute_usage_type, true);
524 return (AttributeUsageAttribute)usage_attr [0];
527 return attr_class.AttributeUsage;
531 // This method should be invoked to pull the IndexerName attribute from an
532 // Indexer if it exists.
534 public static string ScanForIndexerName (EmitContext ec, Attributes opt_attrs)
536 if (opt_attrs == null)
537 return null;
539 foreach (Attribute a in opt_attrs.Attrs) {
540 if (a.ResolveType (ec) == null)
541 return null;
543 if (a.Type != TypeManager.indexer_name_type)
544 continue;
547 // So we have found an IndexerName, pull the data out.
549 if (a.Arguments == null || a.Arguments [0] == null){
550 Error_AttributeConstructorMismatch (a.Location);
551 return null;
553 ArrayList pos_args = (ArrayList) a.Arguments [0];
554 if (pos_args.Count == 0){
555 Error_AttributeConstructorMismatch (a.Location);
556 return null;
559 Argument arg = (Argument) pos_args [0];
560 if (!arg.Resolve (ec, a.Location))
561 return null;
563 Expression e = arg.Expr;
564 if (!(e is StringConstant)){
565 Error_AttributeConstructorMismatch (a.Location);
566 return null;
570 // Remove the attribute from the list
572 opt_attrs.Attrs.Remove (a);
574 return (((StringConstant) e).Value);
576 return null;
580 // This pulls the condition name out of a Conditional attribute
582 public string Conditional_GetConditionName ()
585 // So we have a Conditional, pull the data out.
587 if (Arguments == null || Arguments [0] == null){
588 Error_AttributeConstructorMismatch (Location);
589 return null;
592 ArrayList pos_args = (ArrayList) Arguments [0];
593 if (pos_args.Count != 1){
594 Error_AttributeConstructorMismatch (Location);
595 return null;
598 Argument arg = (Argument) pos_args [0];
599 if (!(arg.Expr is StringConstant)){
600 Error_AttributeConstructorMismatch (Location);
601 return null;
604 return ((StringConstant) arg.Expr).Value;
608 // This pulls the obsolete message and error flag out of an Obsolete attribute
610 public string Obsolete_GetObsoleteMessage (out bool is_error)
612 is_error = false;
614 // So we have an Obsolete, pull the data out.
616 if (Arguments == null || Arguments [0] == null)
617 return "";
619 ArrayList pos_args = (ArrayList) Arguments [0];
620 if (pos_args.Count == 0)
621 return "";
622 else if (pos_args.Count > 2){
623 Error_AttributeConstructorMismatch (Location);
624 return null;
627 Argument arg = (Argument) pos_args [0];
628 if (!(arg.Expr is StringConstant)){
629 Error_AttributeConstructorMismatch (Location);
630 return null;
633 if (pos_args.Count == 2){
634 Argument arg2 = (Argument) pos_args [1];
635 if (!(arg2.Expr is BoolConstant)){
636 Error_AttributeConstructorMismatch (Location);
637 return null;
639 is_error = ((BoolConstant) arg2.Expr).Value;
642 return ((StringConstant) arg.Expr).Value;
646 /// <summary>
647 /// Emit attribute for Attributable symbol
648 /// </summary>
649 public void Emit (EmitContext ec, Attributable ias, ListDictionary emitted_attr)
651 CustomAttributeBuilder cb = Resolve (ec);
652 if (cb == null)
653 return;
655 AttributeUsageAttribute usage_attr = GetAttributeUsage ();
656 if ((usage_attr.ValidOn & ias.AttributeTargets) == 0) {
657 Report.Error (592, Location, "Attribute" + Name + "is not valid on this declaration type. It is valid on " + GetValidTargets () + " declarations only.");
658 return;
661 ias.ApplyAttributeBuilder (this, cb);
665 // Applies the attributes to the `builder'.
667 public static void ApplyAttributes (EmitContext ec, object builder, object kind,
668 Attributes opt_attrs, Location loc)
670 if (opt_attrs == null)
671 return;
673 foreach (Attribute a in opt_attrs.Attrs) {
674 CustomAttributeBuilder cb = a.Resolve (ec);
676 if (cb == null)
677 continue;
679 if (!(kind is TypeContainer))
680 if (!CheckAttribute (a, kind)) {
681 Error_AttributeNotValidForElement (a, loc);
682 return;
685 if (kind is Method || kind is Accessor) {
686 if (a.Type == TypeManager.methodimpl_attr_type) {
687 if (a.ImplOptions == MethodImplOptions.InternalCall)
688 ((MethodBuilder) builder).SetImplementationFlags (MethodImplAttributes.InternalCall | MethodImplAttributes.Runtime);
689 } else if (a.Type != TypeManager.dllimport_type){
690 ((MethodBuilder) builder).SetCustomAttribute (cb);
692 } else if (kind is ParameterBuilder) {
693 if (a.Type == TypeManager.marshal_as_attr_type) {
694 UnmanagedMarshal marshal = UnmanagedMarshal.DefineUnmanagedMarshal (a.UnmanagedType);
695 ((ParameterBuilder) builder).SetMarshal (marshal);
696 } else
697 ((ParameterBuilder) builder).SetCustomAttribute (cb);
698 } else if (kind is FieldBuilder) {
699 ((FieldBuilder) builder).SetCustomAttribute (cb);
700 } else
701 throw new Exception ("Unknown kind: " + kind);
705 public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name,
706 MethodAttributes flags, Type ret_type, Type [] param_types)
709 // We extract from the attribute the information we need
712 if (Arguments == null) {
713 Console.WriteLine ("Internal error : this is not supposed to happen !");
714 return null;
717 Type = CheckAttributeType (ec);
718 if (Type == null)
719 return null;
721 ArrayList named_args = new ArrayList ();
723 ArrayList pos_args = (ArrayList) Arguments [0];
724 if (Arguments.Count > 1)
725 named_args = (ArrayList) Arguments [1];
728 string dll_name = null;
730 Argument tmp = (Argument) pos_args [0];
732 if (!tmp.Resolve (ec, Location))
733 return null;
735 if (tmp.Expr is Constant)
736 dll_name = (string) ((Constant) tmp.Expr).GetValue ();
737 else {
738 Error_AttributeArgumentNotValid ();
739 return null;
742 // Now we process the named arguments
743 CallingConvention cc = CallingConvention.Winapi;
744 CharSet charset = CharSet.Ansi;
745 bool preserve_sig = true;
746 bool exact_spelling = false;
747 bool set_last_err = false;
748 string entry_point = null;
750 for (int i = 0; i < named_args.Count; i++) {
752 DictionaryEntry de = (DictionaryEntry) named_args [i];
754 string member_name = (string) de.Key;
755 Argument a = (Argument) de.Value;
757 if (!a.Resolve (ec, Location))
758 return null;
760 Expression member = Expression.MemberLookup (
761 ec, Type, member_name,
762 MemberTypes.Field | MemberTypes.Property,
763 BindingFlags.Public | BindingFlags.Instance,
764 Location);
766 if (member == null || !(member is FieldExpr)) {
767 Error_InvalidNamedArgument (member_name);
768 return null;
771 if (member is FieldExpr) {
772 FieldExpr fe = (FieldExpr) member;
773 FieldInfo fi = fe.FieldInfo;
775 if (fi.IsInitOnly) {
776 Error_InvalidNamedArgument (member_name);
777 return null;
780 if (a.Expr is Constant) {
781 Constant c = (Constant) a.Expr;
783 if (member_name == "CallingConvention")
784 cc = (CallingConvention) c.GetValue ();
785 else if (member_name == "CharSet")
786 charset = (CharSet) c.GetValue ();
787 else if (member_name == "EntryPoint")
788 entry_point = (string) c.GetValue ();
789 else if (member_name == "SetLastError")
790 set_last_err = (bool) c.GetValue ();
791 else if (member_name == "ExactSpelling")
792 exact_spelling = (bool) c.GetValue ();
793 else if (member_name == "PreserveSig")
794 preserve_sig = (bool) c.GetValue ();
795 } else {
796 Error_AttributeArgumentNotValid ();
797 return null;
803 if (entry_point == null)
804 entry_point = name;
806 MethodBuilder mb = builder.DefinePInvokeMethod (
807 name, dll_name, entry_point, flags | MethodAttributes.HideBySig,
808 CallingConventions.Standard,
809 ret_type,
810 param_types,
812 charset);
814 if (preserve_sig)
815 mb.SetImplementationFlags (MethodImplAttributes.PreserveSig);
817 return mb;
820 public bool IsAssemblyAttribute {
821 get {
822 return ExplicitTarget == "assembly";
826 public bool IsModuleAttribute {
827 get {
828 return ExplicitTarget == "module";
833 public class Attributes {
834 public ArrayList Attrs;
835 public Location Location;
837 public Attributes (Attribute a)
839 Attrs = new ArrayList ();
840 Attrs.Add (a);
843 public Attributes (ArrayList attrs)
845 Attrs = attrs;
848 public void Add (Attribute attr)
850 Attrs.Add (attr);
853 public void Add (ArrayList attrs)
855 Attrs.AddRange (attrs);
858 public void Emit (EmitContext ec, Attributable ias)
860 ListDictionary ld = new ListDictionary ();
862 foreach (Attribute a in Attrs)
863 a.Emit (ec, ias, ld);