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.Collections.Specialized;
16 using System.Reflection;
17 using System.Reflection.Emit;
18 using System.Runtime.InteropServices;
19 using System.Runtime.CompilerServices;
21 using System.Text.RegularExpressions;
22 namespace Mono.MonoBASIC {
25 /// Base class for objects that can have Attributes applied to them.
27 public abstract class Attributable {
29 /// Attributes for this type
31 Attributes attributes;
33 public Attributable(Attributes attrs)
38 public Attributes OptAttributes
49 /// Use member-specific procedure to apply attribute @a in @cb to the entity being built in @builder
51 public abstract void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb);
53 /// Returns one AttributeTarget for this element.
55 public abstract AttributeTargets AttributeTargets { get; }
57 public class Attribute {
58 public readonly string ExplicitTarget;
59 public readonly string Name;
61 public readonly ArrayList Arguments;
63 public Location Location;
67 // Is non-null if type is AttributeUsageAttribute
68 AttributeUsageAttribute usage_attribute;
70 public AttributeUsageAttribute UsageAttribute {
72 return usage_attribute;
76 bool usage_attr = false;
78 MethodImplOptions ImplOptions;
79 public UnmanagedType UnmanagedType;
80 CustomAttributeBuilder cb;
82 public Attribute (string name, ArrayList args, Location loc)
89 public Attribute (string target, string name, ArrayList args, Location loc)
91 ExplicitTarget = target;
93 if (name=="AssemblyVersion")
94 for (int i=0;i<args.Count;i++)
98 ArrayList arrList=(ArrayList)myObject;
99 for (int j=0;j<arrList.Count;j++)
102 Argument arg = (Argument)arrList[i];
103 string strVersion = Regex.Replace(arg.Expr.ToString(),"StringLiteral","");
104 strVersion = Regex.Replace(strVersion,@"\(|\)","");
106 //Version = new Version(strVersion); it does not work. but it should
107 //Making an ArrayList of integer to instatiate a new Version
108 for(int cont=0; cont<strVersion.Length;cont++)
109 if (Regex.IsMatch(strVersion[cont].ToString(),@"\d"))
112 CodeGen.ArrListVersion.Add(Convert.ToInt32(strVersion[cont].ToString()));
121 void Error_InvalidNamedArgument (string name)
123 Report.Error (617, Location, "'" + name + "' is not a valid named attribute " +
124 "argument. Named attribute arguments must be fields which are not " +
125 "readonly, static or const, or properties with a set accessor which "+
129 void Error_AttributeArgumentNotValid ()
131 Report.Error (182, Location,
132 "An attribute argument must be a constant expression, typeof " +
133 "expression or array creation expression");
136 static void Error_AttributeConstructorMismatch (Location loc)
140 "Could not find a constructor for this argument list.");
143 private Type CheckAttributeType (EmitContext ec) {
145 bool isattributeclass = true;
147 t = RootContext.LookupType (ec.DeclSpace, Name, true, Location);
149 isattributeclass = t.IsSubclassOf (TypeManager.attribute_type);
150 if (isattributeclass)
153 t = RootContext.LookupType (ec.DeclSpace, Name + "Attribute", true, Location);
155 if (t.IsSubclassOf (TypeManager.attribute_type))
158 if (!isattributeclass) {
159 Report.Error (616, Location, "'" + Name + "': is not an attribute class");
163 Report.Error (616, Location, "'" + Name + "Attribute': is not an attribute class");
167 246, Location, "Could not find attribute '" + Name + "' (are you" +
168 " missing a using directive or an assembly reference ?)");
173 public Type ResolveType (EmitContext ec)
175 Type = CheckAttributeType (ec);
180 public CustomAttributeBuilder Resolve (EmitContext ec)
183 Type = CheckAttributeType (ec);
187 bool MethodImplAttr = false;
188 bool MarshalAsAttr = false;
192 if (Type == TypeManager.attribute_usage_type)
194 if (Type == TypeManager.methodimpl_attr_type)
195 MethodImplAttr = true;
196 if (Type == TypeManager.marshal_as_attr_type)
197 MarshalAsAttr = true;
199 // Now we extract the positional and named arguments
201 ArrayList pos_args = new ArrayList ();
202 ArrayList named_args = new ArrayList ();
203 int pos_arg_count = 0;
205 if (Arguments != null) {
206 pos_args = (ArrayList) Arguments [0];
207 if (pos_args != null)
208 pos_arg_count = pos_args.Count;
209 if (Arguments.Count > 1)
210 named_args = (ArrayList) Arguments [1];
213 object [] pos_values = new object [pos_arg_count];
216 // First process positional arguments
220 for (i = 0; i < pos_arg_count; i++) {
221 Argument a = (Argument) pos_args [i];
224 if (!a.Resolve (ec, Location))
230 pos_values [i] = ((Constant) e).GetValue ();
231 } else if (e is TypeOf) {
232 pos_values [i] = ((TypeOf) e).TypeArg;
234 Error_AttributeArgumentNotValid ();
239 usage_attribute = new AttributeUsageAttribute ((AttributeTargets) pos_values [0]);
242 this.ImplOptions = (MethodImplOptions) pos_values [0];
246 (System.Runtime.InteropServices.UnmanagedType) pos_values [0];
250 // Now process named arguments
253 ArrayList field_infos = new ArrayList ();
254 ArrayList prop_infos = new ArrayList ();
255 ArrayList field_values = new ArrayList ();
256 ArrayList prop_values = new ArrayList ();
258 for (i = 0; i < named_args.Count; i++) {
259 DictionaryEntry de = (DictionaryEntry) named_args [i];
260 string member_name = (string) de.Key;
261 Argument a = (Argument) de.Value;
264 if (!a.Resolve (ec, Location))
267 Expression member = Expression.MemberLookup (
268 ec, Type, member_name,
269 MemberTypes.Field | MemberTypes.Property,
270 BindingFlags.Public | BindingFlags.Instance,
273 if (member == null || !(member is PropertyGroupExpr || member is FieldExpr)) {
274 Error_InvalidNamedArgument (member_name);
279 if (member is PropertyGroupExpr) {
280 PropertyGroupExpr pe = (PropertyGroupExpr) member;
281 PropertyInfo pi = null;
282 if (pe.Properties.Length == 1)
283 pi = pe.Properties [0];
285 // find a property which doesnt take any arguments
286 // Might be wrong, but this is just a temporary work-around
287 foreach (PropertyInfo p in pe.Properties) {
288 MethodInfo mi = p.GetGetMethod ();
289 if (mi.GetParameters().Length == 0) {
297 Error_InvalidNamedArgument (member_name);
302 object o = ((Constant) e).GetValue ();
306 if (member_name == "AllowMultiple")
307 usage_attribute.AllowMultiple = (bool) o;
308 if (member_name == "Inherited")
309 usage_attribute.Inherited = (bool) o;
312 } else if (e is TypeOf) {
313 prop_values.Add (((TypeOf) e).TypeArg);
315 Error_AttributeArgumentNotValid ();
321 } else if (member is FieldExpr) {
322 FieldExpr fe = (FieldExpr) member;
323 FieldInfo fi = fe.FieldInfo;
326 Error_InvalidNamedArgument (member_name);
331 // Handle charset here, and set the TypeAttributes
334 object value = ((Constant) e).GetValue ();
336 field_values.Add (value);
337 } else if (e is TypeOf) {
338 field_values.Add (((TypeOf) e).TypeArg);
340 Error_AttributeArgumentNotValid ();
344 field_infos.Add (fi);
348 Expression mg = Expression.MemberLookup (
349 ec, Type, ".ctor", MemberTypes.Constructor,
350 BindingFlags.Public | BindingFlags.Instance, Location);
353 Error_AttributeConstructorMismatch (Location);
357 MethodBase constructor = Invocation.OverloadResolve (
358 ec, (MethodGroupExpr) mg, pos_args, Location);
360 if (constructor == null) {
361 Error_AttributeConstructorMismatch (Location);
366 // Now we perform some checks on the positional args as they
367 // cannot be null for a constructor which expects a parameter
371 ParameterData pd = Invocation.GetParameterData (constructor);
373 for (int j = 0; j < pos_arg_count; ++j) {
374 Argument a = (Argument) pos_args [j];
376 if (a.Expr is NullLiteral && pd.ParameterType (j) == TypeManager.object_type) {
377 Error_AttributeArgumentNotValid ();
382 PropertyInfo [] prop_info_arr = new PropertyInfo [prop_infos.Count];
383 FieldInfo [] field_info_arr = new FieldInfo [field_infos.Count];
384 object [] field_values_arr = new object [field_values.Count];
385 object [] prop_values_arr = new object [prop_values.Count];
387 field_infos.CopyTo (field_info_arr, 0);
388 field_values.CopyTo (field_values_arr, 0);
390 prop_values.CopyTo (prop_values_arr, 0);
391 prop_infos.CopyTo (prop_info_arr, 0);
395 cb = new CustomAttributeBuilder (
396 (ConstructorInfo) constructor, pos_values,
397 prop_info_arr, prop_values_arr,
398 field_info_arr, field_values_arr);
400 } catch (NullReferenceException) {
402 // Don't know what to do here
407 // using System.ComponentModel;
408 // [DefaultValue (CollectionChangeAction.Add)]
409 // class X { static void Main () {} }
413 "The compiler can not encode this attribute in .NET due to\n" +
414 "\ta bug in the .NET runtime. Try the Mono runtime");
420 public string GetValidTargets ()
422 StringBuilder sb = new StringBuilder ();
423 AttributeTargets targets = GetAttributeUsage ().ValidOn;
425 if ((targets & AttributeTargets.Assembly) != 0)
426 sb.Append ("'assembly' ");
428 if ((targets & AttributeTargets.Class) != 0)
429 sb.Append ("'class' ");
431 if ((targets & AttributeTargets.Constructor) != 0)
432 sb.Append ("'constructor' ");
434 if ((targets & AttributeTargets.Delegate) != 0)
435 sb.Append ("'delegate' ");
437 if ((targets & AttributeTargets.Enum) != 0)
438 sb.Append ("'enum' ");
440 if ((targets & AttributeTargets.Event) != 0)
441 sb.Append ("'event' ");
443 if ((targets & AttributeTargets.Field) != 0)
444 sb.Append ("'field' ");
446 if ((targets & AttributeTargets.Interface) != 0)
447 sb.Append ("'interface' ");
449 if ((targets & AttributeTargets.Method) != 0)
450 sb.Append ("'method' ");
452 if ((targets & AttributeTargets.Module) != 0)
453 sb.Append ("'module' ");
455 if ((targets & AttributeTargets.Parameter) != 0)
456 sb.Append ("'parameter' ");
458 if ((targets & AttributeTargets.Property) != 0)
459 sb.Append ("'property' ");
461 if ((targets & AttributeTargets.ReturnValue) != 0)
462 sb.Append ("'return value' ");
464 if ((targets & AttributeTargets.Struct) != 0)
465 sb.Append ("'struct' ");
467 return sb.ToString ();
471 public static void Error_AttributeNotValidForElement (Attribute a, Location loc)
474 30662, loc, "Attribute '" + a.Name +
475 "' is not valid on this declaration type. " +
476 "It is valid on " + a.GetValidTargets () + "declarations only.");
479 public static bool CheckAttribute (Attribute a, object element)
481 //TypeContainer attr = TypeManager.LookupClass (a.Type);
482 AttributeTargets targets = a.GetAttributeUsage ().ValidOn;
485 if (element is Class) {
486 if ((targets & AttributeTargets.Class) != 0)
491 } else if (element is Struct) {
492 if ((targets & AttributeTargets.Struct) != 0)
496 } else if (element is Constructor) {
497 if ((targets & AttributeTargets.Constructor) != 0)
501 } else if (element is Delegate) {
502 if ((targets & AttributeTargets.Delegate) != 0)
506 } else if (element is Enum) {
507 if ((targets & AttributeTargets.Enum) != 0)
511 } else if (element is Event /*|| element is InterfaceEvent*/) {
512 if ((targets & AttributeTargets.Event) != 0)
516 } else if (element is Field || element is FieldBuilder) {
517 if ((targets & AttributeTargets.Field) != 0)
521 } else if (element is Interface) {
522 if ((targets & AttributeTargets.Interface) != 0)
526 } else if (element is Method || element is Accessor) {
527 if ((targets & AttributeTargets.Method) != 0)
531 } else if (element is ParameterBuilder) {
532 if ((targets & AttributeTargets.Parameter) != 0)
536 } else if (element is Property /* || element is Indexer ||
537 element is InterfaceProperty || element is InterfaceIndexer*/) {
538 if ((targets & AttributeTargets.Property) != 0)
542 } else if (element is AssemblyBuilder){
543 if ((targets & AttributeTargets.Assembly) != 0)
547 } else if (element is ModuleBuilder){
548 if ((targets & AttributeTargets.Module) != 0)
558 /// Returns AttributeUsage attribute for this type
560 public AttributeUsageAttribute GetAttributeUsage ()
562 Class attr_class = (Class) TypeManager.LookupClass (Type);
564 if (attr_class == null) {
565 object[] usage_attr = Type.GetCustomAttributes (TypeManager.attribute_usage_type, true);
566 return (AttributeUsageAttribute)usage_attr [0];
569 return attr_class.AttributeUsage;
573 // This method should be invoked to pull the DefaultPropName attribute from an
574 // Indexer if it exists.
576 public static string ScanForIndexerName (EmitContext ec, Attributes opt_attrs)
578 if (opt_attrs == null)
581 foreach (Attribute a in opt_attrs.Attrs) {
582 if (a.ResolveType (ec) == null)
585 if (a.Type != TypeManager.indexer_name_type)
589 // So we have found an DefaultPropName, pull the data out.
591 if (a.Arguments == null || a.Arguments [0] == null){
592 Error_AttributeConstructorMismatch (a.Location);
595 ArrayList pos_args = (ArrayList) a.Arguments [0];
596 if (pos_args.Count == 0){
597 Error_AttributeConstructorMismatch (a.Location);
601 Argument arg = (Argument) pos_args [0];
602 if (!arg.Resolve (ec, a.Location))
605 Expression e = arg.Expr;
606 if (!(e is StringConstant)){
607 Error_AttributeConstructorMismatch (a.Location);
612 // Remove the attribute from the list
614 opt_attrs.Attrs.Remove (a);
616 return (((StringConstant) e).Value);
622 // This pulls the condition name out of a Conditional attribute
624 public string Conditional_GetConditionName ()
627 // So we have a Conditional, pull the data out.
629 if (Arguments == null || Arguments [0] == null){
630 Error_AttributeConstructorMismatch (Location);
634 ArrayList pos_args = (ArrayList) Arguments [0];
635 if (pos_args.Count != 1){
636 Error_AttributeConstructorMismatch (Location);
640 Argument arg = (Argument) pos_args [0];
641 if (!(arg.Expr is StringConstant)){
642 Error_AttributeConstructorMismatch (Location);
646 return ((StringConstant) arg.Expr).Value;
650 // This pulls the obsolete message and error flag out of an Obsolete attribute
652 public string Obsolete_GetObsoleteMessage (out bool is_error)
656 // So we have an Obsolete, pull the data out.
658 if (Arguments == null || Arguments [0] == null)
661 ArrayList pos_args = (ArrayList) Arguments [0];
662 if (pos_args.Count == 0)
664 else if (pos_args.Count > 2){
665 Error_AttributeConstructorMismatch (Location);
669 Argument arg = (Argument) pos_args [0];
670 if (!(arg.Expr is StringConstant)){
671 Error_AttributeConstructorMismatch (Location);
675 if (pos_args.Count == 2){
676 Argument arg2 = (Argument) pos_args [1];
677 if (!(arg2.Expr is BoolConstant)){
678 Error_AttributeConstructorMismatch (Location);
681 is_error = ((BoolConstant) arg2.Expr).Value;
684 return ((StringConstant) arg.Expr).Value;
689 /// Emit attribute for Attributable symbol
691 public void Emit (EmitContext ec, Attributable ias, ListDictionary emitted_attr)
693 CustomAttributeBuilder cb = Resolve (ec);
697 AttributeUsageAttribute usage_attr = GetAttributeUsage ();
698 if ((usage_attr.ValidOn & ias.AttributeTargets) == 0) {
699 Report.Error (30662, Location, "Attribute" + Name + "is not valid on this declaration type. It is valid on " + GetValidTargets () + " declarations only.");
703 ias.ApplyAttributeBuilder (this, cb);
707 // Applies the attributes to the `builder'.
709 public static void ApplyAttributes (EmitContext ec, object builder, object kind,
710 Attributes opt_attrs, Location loc)
712 if (opt_attrs == null)
715 foreach (Attribute a in opt_attrs.Attrs) {
716 CustomAttributeBuilder cb = a.Resolve (ec);
721 if (!(kind is TypeContainer))
722 if (!CheckAttribute (a, kind)) {
723 Error_AttributeNotValidForElement (a, loc);
727 if (kind is Method || kind is Accessor) {
728 if (a.Type == TypeManager.methodimpl_attr_type) {
729 if (a.ImplOptions == MethodImplOptions.InternalCall)
730 ((MethodBuilder) builder).SetImplementationFlags (MethodImplAttributes.InternalCall | MethodImplAttributes.Runtime);
731 } else if (a.Type != TypeManager.dllimport_type){
732 ((MethodBuilder) builder).SetCustomAttribute (cb);
735 throw new Exception ("Unknown kind: " + kind);
739 public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name,
740 MethodAttributes flags, Type ret_type, Type [] param_types)
743 // We extract from the attribute the information we need
746 if (Arguments == null) {
747 Console.WriteLine ("Internal error : this is not supposed to happen !");
751 Type = CheckAttributeType (ec);
755 ArrayList named_args = new ArrayList ();
757 ArrayList pos_args = (ArrayList) Arguments [0];
758 if (Arguments.Count > 1)
759 named_args = (ArrayList) Arguments [1];
762 string dll_name = null;
764 Argument tmp = (Argument) pos_args [0];
766 if (!tmp.Resolve (ec, Location))
769 if (tmp.Expr is Constant)
770 dll_name = (string) ((Constant) tmp.Expr).GetValue ();
772 Error_AttributeArgumentNotValid ();
776 // Now we process the named arguments
777 CallingConvention cc = CallingConvention.Winapi;
778 CharSet charset = CharSet.Ansi;
779 bool preserve_sig = true;
780 /*bool exact_spelling = false;
781 bool set_last_err = false;*/
782 string entry_point = null;
784 for (int i = 0; i < named_args.Count; i++) {
786 DictionaryEntry de = (DictionaryEntry) named_args [i];
788 string member_name = (string) de.Key;
789 Argument a = (Argument) de.Value;
791 if (!a.Resolve (ec, Location))
794 Expression member = Expression.MemberLookup (
795 ec, Type, member_name,
796 MemberTypes.Field | MemberTypes.Property,
797 BindingFlags.Public | BindingFlags.Instance,
800 if (member == null || !(member is FieldExpr)) {
801 Error_InvalidNamedArgument (member_name);
805 if (member is FieldExpr) {
806 FieldExpr fe = (FieldExpr) member;
807 FieldInfo fi = fe.FieldInfo;
810 Error_InvalidNamedArgument (member_name);
814 if (a.Expr is Constant) {
815 Constant c = (Constant) a.Expr;
817 if (member_name == "CallingConvention")
818 cc = (CallingConvention) c.GetValue ();
819 else if (member_name == "CharSet")
820 charset = (CharSet) c.GetValue ();
821 else if (member_name == "EntryPoint")
822 entry_point = (string) c.GetValue ();
823 /*else if (member_name == "SetLastError")
824 set_last_err = (bool) c.GetValue ();
825 else if (member_name == "ExactSpelling")
826 exact_spelling = (bool) c.GetValue ();*/
827 else if (member_name == "PreserveSig")
828 preserve_sig = (bool) c.GetValue ();
830 Error_AttributeArgumentNotValid ();
837 if (entry_point == null)
840 MethodBuilder mb = builder.DefinePInvokeMethod (
841 name, dll_name, entry_point, flags | MethodAttributes.HideBySig,
842 CallingConventions.Standard,
849 mb.SetImplementationFlags (MethodImplAttributes.PreserveSig);
854 public bool IsAssemblyAttribute {
856 return ExplicitTarget == "assembly";
860 public bool IsModuleAttribute {
862 return ExplicitTarget == "module";
867 public class Attributes {
868 public ArrayList Attrs;
869 public Location Location;
871 public Attributes (Attribute a)
873 Attrs = new ArrayList ();
877 public Attributes (ArrayList attrs)
882 public void Add (Attribute attr)
887 public void Add (ArrayList attrs)
889 Attrs.AddRange (attrs);
892 public void Emit (EmitContext ec, Attributable ias)
894 ListDictionary ld = new ListDictionary ();
896 foreach (Attribute a in Attrs)
897 a.Emit (ec, ias, ld);