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
;
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
;
60 public readonly ArrayList Arguments
;
62 public Location Location
;
66 // Is non-null if type is AttributeUsageAttribute
67 AttributeUsageAttribute usage_attribute
;
69 public AttributeUsageAttribute UsageAttribute
{
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
)
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 "+
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
)
112 "Could not find a constructor for this argument list.");
115 private Type
CheckAttributeType (EmitContext ec
) {
117 bool isattributeclass
= true;
119 t
= RootContext
.LookupType (ec
.DeclSpace
, Name
, true, Location
);
121 isattributeclass
= t
.IsSubclassOf (TypeManager
.attribute_type
);
122 if (isattributeclass
)
125 t
= RootContext
.LookupType (ec
.DeclSpace
, Name
+ "Attribute", true, Location
);
127 if (t
.IsSubclassOf (TypeManager
.attribute_type
))
130 if (!isattributeclass
) {
131 Report
.Error (616, Location
, "'" + Name
+ "': is not an attribute class");
135 Report
.Error (616, Location
, "'" + Name
+ "Attribute': is not an attribute class");
139 246, Location
, "Could not find attribute '" + Name
+ "' (are you" +
140 " missing a using directive or an assembly reference ?)");
145 public Type
ResolveType (EmitContext ec
)
147 Type
= CheckAttributeType (ec
);
152 public CustomAttributeBuilder
Resolve (EmitContext ec
)
155 Type
= CheckAttributeType (ec
);
159 bool MethodImplAttr
= false;
160 bool MarshalAsAttr
= false;
164 if (Type
== TypeManager
.attribute_usage_type
)
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
192 for (i
= 0; i
< pos_arg_count
; i
++) {
193 Argument a
= (Argument
) pos_args
[i
];
196 if (!a
.Resolve (ec
, Location
))
202 pos_values
[i
] = ((Constant
) e
).GetValue ();
203 } else if (e
is TypeOf
) {
204 pos_values
[i
] = ((TypeOf
) e
).TypeArg
;
206 Error_AttributeArgumentNotValid ();
211 usage_attribute
= new AttributeUsageAttribute ((AttributeTargets
) pos_values
[0]);
214 this.ImplOptions
= (MethodImplOptions
) pos_values
[0];
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
;
236 if (!a
.Resolve (ec
, Location
))
239 Expression member
= Expression
.MemberLookup (
240 ec
, Type
, member_name
,
241 MemberTypes
.Field
| MemberTypes
.Property
,
242 BindingFlags
.Public
| BindingFlags
.Instance
,
245 if (member
== null || !(member
is PropertyExpr
|| member
is FieldExpr
)) {
246 Error_InvalidNamedArgument (member_name
);
251 if (member
is PropertyExpr
) {
252 PropertyExpr pe
= (PropertyExpr
) member
;
253 PropertyInfo pi
= pe
.PropertyInfo
;
256 Error_InvalidNamedArgument (member_name
);
261 object o
= ((Constant
) e
).GetValue ();
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
);
274 Error_AttributeArgumentNotValid ();
280 } else if (member
is FieldExpr
) {
281 FieldExpr fe
= (FieldExpr
) member
;
282 FieldInfo fi
= fe
.FieldInfo
;
285 Error_InvalidNamedArgument (member_name
);
290 // Handle charset here, and set the TypeAttributes
293 object value = ((Constant
) e
).GetValue ();
295 field_values
.Add (value);
296 } else if (e
is TypeOf
) {
297 field_values
.Add (((TypeOf
) e
).TypeArg
);
299 Error_AttributeArgumentNotValid ();
303 field_infos
.Add (fi
);
307 Expression mg
= Expression
.MemberLookup (
308 ec
, Type
, ".ctor", MemberTypes
.Constructor
,
309 BindingFlags
.Public
| BindingFlags
.Instance
, Location
);
312 Error_AttributeConstructorMismatch (Location
);
316 MethodBase constructor
= Invocation
.OverloadResolve (
317 ec
, (MethodGroupExpr
) mg
, pos_args
, Location
);
319 if (constructor
== null) {
320 Error_AttributeConstructorMismatch (Location
);
325 // Now we perform some checks on the positional args as they
326 // cannot be null for a constructor which expects a parameter
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 ();
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);
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
365 // using System.ComponentModel;
366 // [DefaultValue (CollectionChangeAction.Add)]
367 // class X { static void Main () {} }
371 "The compiler can not encode this attribute in .NET due to\n" +
372 "\ta bug in the .NET runtime. Try the Mono runtime");
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
)
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)
449 } else if (element
is Struct
) {
450 if ((targets
& AttributeTargets
.Struct
) != 0)
454 } else if (element
is Constructor
) {
455 if ((targets
& AttributeTargets
.Constructor
) != 0)
459 } else if (element
is Delegate
) {
460 if ((targets
& AttributeTargets
.Delegate
) != 0)
464 } else if (element
is Enum
) {
465 if ((targets
& AttributeTargets
.Enum
) != 0)
469 } else if (element
is Event
/*|| element is InterfaceEvent*/) {
470 if ((targets
& AttributeTargets
.Event
) != 0)
474 } else if (element
is Field
|| element
is FieldBuilder
) {
475 if ((targets
& AttributeTargets
.Field
) != 0)
479 } else if (element
is Interface
) {
480 if ((targets
& AttributeTargets
.Interface
) != 0)
484 } else if (element
is Method
|| element
is Accessor
) {
485 if ((targets
& AttributeTargets
.Method
) != 0)
489 } else if (element
is ParameterBuilder
) {
490 if ((targets
& AttributeTargets
.Parameter
) != 0)
494 } else if (element
is Property
/* || element is Indexer ||
495 element is InterfaceProperty || element is InterfaceIndexer*/) {
496 if ((targets
& AttributeTargets
.Property
) != 0)
500 } else if (element
is AssemblyBuilder
){
501 if ((targets
& AttributeTargets
.Assembly
) != 0)
505 } else if (element
is ModuleBuilder
){
506 if ((targets
& AttributeTargets
.Module
) != 0)
516 /// Returns AttributeUsage attribute for this type
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)
539 foreach (Attribute a
in opt_attrs
.Attrs
) {
540 if (a
.ResolveType (ec
) == null)
543 if (a
.Type
!= TypeManager
.indexer_name_type
)
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
);
553 ArrayList pos_args
= (ArrayList
) a
.Arguments
[0];
554 if (pos_args
.Count
== 0){
555 Error_AttributeConstructorMismatch (a
.Location
);
559 Argument arg
= (Argument
) pos_args
[0];
560 if (!arg
.Resolve (ec
, a
.Location
))
563 Expression e
= arg
.Expr
;
564 if (!(e
is StringConstant
)){
565 Error_AttributeConstructorMismatch (a
.Location
);
570 // Remove the attribute from the list
572 opt_attrs
.Attrs
.Remove (a
);
574 return (((StringConstant
) e
).Value
);
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
);
592 ArrayList pos_args
= (ArrayList
) Arguments
[0];
593 if (pos_args
.Count
!= 1){
594 Error_AttributeConstructorMismatch (Location
);
598 Argument arg
= (Argument
) pos_args
[0];
599 if (!(arg
.Expr
is StringConstant
)){
600 Error_AttributeConstructorMismatch (Location
);
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
)
614 // So we have an Obsolete, pull the data out.
616 if (Arguments
== null || Arguments
[0] == null)
619 ArrayList pos_args
= (ArrayList
) Arguments
[0];
620 if (pos_args
.Count
== 0)
622 else if (pos_args
.Count
> 2){
623 Error_AttributeConstructorMismatch (Location
);
627 Argument arg
= (Argument
) pos_args
[0];
628 if (!(arg
.Expr
is StringConstant
)){
629 Error_AttributeConstructorMismatch (Location
);
633 if (pos_args
.Count
== 2){
634 Argument arg2
= (Argument
) pos_args
[1];
635 if (!(arg2
.Expr
is BoolConstant
)){
636 Error_AttributeConstructorMismatch (Location
);
639 is_error
= ((BoolConstant
) arg2
.Expr
).Value
;
642 return ((StringConstant
) arg
.Expr
).Value
;
647 /// Emit attribute for Attributable symbol
649 public void Emit (EmitContext ec
, Attributable ias
, ListDictionary emitted_attr
)
651 CustomAttributeBuilder cb
= Resolve (ec
);
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.");
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)
673 foreach (Attribute a
in opt_attrs
.Attrs
) {
674 CustomAttributeBuilder cb
= a
.Resolve (ec
);
679 if (!(kind
is TypeContainer
))
680 if (!CheckAttribute (a
, kind
)) {
681 Error_AttributeNotValidForElement (a
, loc
);
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
);
697 ((ParameterBuilder
) builder
).SetCustomAttribute (cb
);
698 } else if (kind
is FieldBuilder
) {
699 ((FieldBuilder
) builder
).SetCustomAttribute (cb
);
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 !");
717 Type
= CheckAttributeType (ec
);
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
))
735 if (tmp
.Expr
is Constant
)
736 dll_name
= (string) ((Constant
) tmp
.Expr
).GetValue ();
738 Error_AttributeArgumentNotValid ();
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
))
760 Expression member
= Expression
.MemberLookup (
761 ec
, Type
, member_name
,
762 MemberTypes
.Field
| MemberTypes
.Property
,
763 BindingFlags
.Public
| BindingFlags
.Instance
,
766 if (member
== null || !(member
is FieldExpr
)) {
767 Error_InvalidNamedArgument (member_name
);
771 if (member
is FieldExpr
) {
772 FieldExpr fe
= (FieldExpr
) member
;
773 FieldInfo fi
= fe
.FieldInfo
;
776 Error_InvalidNamedArgument (member_name
);
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 ();
796 Error_AttributeArgumentNotValid ();
803 if (entry_point
== null)
806 MethodBuilder mb
= builder
.DefinePInvokeMethod (
807 name
, dll_name
, entry_point
, flags
| MethodAttributes
.HideBySig
,
808 CallingConventions
.Standard
,
815 mb
.SetImplementationFlags (MethodImplAttributes
.PreserveSig
);
820 public bool IsAssemblyAttribute
{
822 return ExplicitTarget
== "assembly";
826 public bool IsModuleAttribute
{
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 ();
843 public Attributes (ArrayList attrs
)
848 public void Add (Attribute 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
);