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
.Reflection
;
16 using System
.Reflection
.Emit
;
17 using System
.Runtime
.InteropServices
;
18 using System
.Runtime
.CompilerServices
;
21 namespace Mono
.MonoBASIC
{
24 /// Base class for objects that can have Attributes applied to them.
26 public abstract class Attributable
{
28 /// Attributes for this type
30 Attributes attributes
;
32 public Attributable(Attributes attrs
)
37 public Attributes OptAttributes
47 public class Attribute
{
48 public readonly string ExplicitTarget
;
49 public readonly string Name
;
50 public readonly ArrayList Arguments
;
56 // Is non-null if type is AttributeUsageAttribute
57 AttributeUsageAttribute usage_attribute
;
59 public AttributeUsageAttribute UsageAttribute
{
61 return usage_attribute
;
65 bool usage_attr
= false;
67 MethodImplOptions ImplOptions
;
68 UnmanagedType UnmanagedType
;
69 CustomAttributeBuilder cb
;
71 public Attribute (string name
, ArrayList args
, Location loc
)
78 public Attribute (string target
, string name
, ArrayList args
, Location loc
)
80 ExplicitTarget
= target
;
83 void Error_InvalidNamedArgument (string name
)
85 Report
.Error (617, Location
, "'" + name
+ "' is not a valid named attribute " +
86 "argument. Named attribute arguments must be fields which are not " +
87 "readonly, static or const, or properties with a set accessor which "+
91 void Error_AttributeArgumentNotValid ()
93 Report
.Error (182, Location
,
94 "An attribute argument must be a constant expression, typeof " +
95 "expression or array creation expression");
98 static void Error_AttributeConstructorMismatch (Location loc
)
102 "Could not find a constructor for this argument list.");
105 private Type
CheckAttributeType (EmitContext ec
) {
107 bool isattributeclass
= true;
109 t
= RootContext
.LookupType (ec
.DeclSpace
, Name
, true, Location
);
111 isattributeclass
= t
.IsSubclassOf (TypeManager
.attribute_type
);
112 if (isattributeclass
)
115 t
= RootContext
.LookupType (ec
.DeclSpace
, Name
+ "Attribute", true, Location
);
117 if (t
.IsSubclassOf (TypeManager
.attribute_type
))
120 if (!isattributeclass
) {
121 Report
.Error (616, Location
, "'" + Name
+ "': is not an attribute class");
125 Report
.Error (616, Location
, "'" + Name
+ "Attribute': is not an attribute class");
129 246, Location
, "Could not find attribute '" + Name
+ "' (are you" +
130 " missing a using directive or an assembly reference ?)");
135 public Type
ResolveType (EmitContext ec
)
137 Type
= CheckAttributeType (ec
);
142 public CustomAttributeBuilder
Resolve (EmitContext ec
)
145 Type
= CheckAttributeType (ec
);
149 bool MethodImplAttr
= false;
150 bool MarshalAsAttr
= false;
154 if (Type
== TypeManager
.attribute_usage_type
)
156 if (Type
== TypeManager
.methodimpl_attr_type
)
157 MethodImplAttr
= true;
158 if (Type
== TypeManager
.marshal_as_attr_type
)
159 MarshalAsAttr
= true;
161 // Now we extract the positional and named arguments
163 ArrayList pos_args
= new ArrayList ();
164 ArrayList named_args
= new ArrayList ();
165 int pos_arg_count
= 0;
167 if (Arguments
!= null) {
168 pos_args
= (ArrayList
) Arguments
[0];
169 if (pos_args
!= null)
170 pos_arg_count
= pos_args
.Count
;
171 if (Arguments
.Count
> 1)
172 named_args
= (ArrayList
) Arguments
[1];
175 object [] pos_values
= new object [pos_arg_count
];
178 // First process positional arguments
182 for (i
= 0; i
< pos_arg_count
; i
++) {
183 Argument a
= (Argument
) pos_args
[i
];
186 if (!a
.Resolve (ec
, Location
))
192 pos_values
[i
] = ((Constant
) e
).GetValue ();
193 } else if (e
is TypeOf
) {
194 pos_values
[i
] = ((TypeOf
) e
).TypeArg
;
196 Error_AttributeArgumentNotValid ();
201 usage_attribute
= new AttributeUsageAttribute ((AttributeTargets
) pos_values
[0]);
204 this.ImplOptions
= (MethodImplOptions
) pos_values
[0];
208 (System
.Runtime
.InteropServices
.UnmanagedType
) pos_values
[0];
212 // Now process named arguments
215 ArrayList field_infos
= new ArrayList ();
216 ArrayList prop_infos
= new ArrayList ();
217 ArrayList field_values
= new ArrayList ();
218 ArrayList prop_values
= new ArrayList ();
220 for (i
= 0; i
< named_args
.Count
; i
++) {
221 DictionaryEntry de
= (DictionaryEntry
) named_args
[i
];
222 string member_name
= (string) de
.Key
;
223 Argument a
= (Argument
) de
.Value
;
226 if (!a
.Resolve (ec
, Location
))
229 Expression member
= Expression
.MemberLookup (
230 ec
, Type
, member_name
,
231 MemberTypes
.Field
| MemberTypes
.Property
,
232 BindingFlags
.Public
| BindingFlags
.Instance
,
235 if (member
== null || !(member
is PropertyExpr
|| member
is FieldExpr
)) {
236 Error_InvalidNamedArgument (member_name
);
241 if (member
is PropertyExpr
) {
242 PropertyExpr pe
= (PropertyExpr
) member
;
243 PropertyInfo pi
= pe
.PropertyInfo
;
246 Error_InvalidNamedArgument (member_name
);
251 object o
= ((Constant
) e
).GetValue ();
255 if (member_name
== "AllowMultiple")
256 usage_attribute
.AllowMultiple
= (bool) o
;
257 if (member_name
== "Inherited")
258 usage_attribute
.Inherited
= (bool) o
;
261 } else if (e
is TypeOf
) {
262 prop_values
.Add (((TypeOf
) e
).TypeArg
);
264 Error_AttributeArgumentNotValid ();
270 } else if (member
is FieldExpr
) {
271 FieldExpr fe
= (FieldExpr
) member
;
272 FieldInfo fi
= fe
.FieldInfo
;
275 Error_InvalidNamedArgument (member_name
);
280 // Handle charset here, and set the TypeAttributes
283 object value = ((Constant
) e
).GetValue ();
285 field_values
.Add (value);
286 } else if (e
is TypeOf
) {
287 field_values
.Add (((TypeOf
) e
).TypeArg
);
289 Error_AttributeArgumentNotValid ();
293 field_infos
.Add (fi
);
297 Expression mg
= Expression
.MemberLookup (
298 ec
, Type
, ".ctor", MemberTypes
.Constructor
,
299 BindingFlags
.Public
| BindingFlags
.Instance
, Location
);
302 Error_AttributeConstructorMismatch (Location
);
306 MethodBase constructor
= Invocation
.OverloadResolve (
307 ec
, (MethodGroupExpr
) mg
, pos_args
, Location
);
309 if (constructor
== null) {
310 Error_AttributeConstructorMismatch (Location
);
315 // Now we perform some checks on the positional args as they
316 // cannot be null for a constructor which expects a parameter
320 ParameterData pd
= Invocation
.GetParameterData (constructor
);
322 for (int j
= 0; j
< pos_arg_count
; ++j
) {
323 Argument a
= (Argument
) pos_args
[j
];
325 if (a
.Expr
is NullLiteral
&& pd
.ParameterType (j
) == TypeManager
.object_type
) {
326 Error_AttributeArgumentNotValid ();
331 PropertyInfo
[] prop_info_arr
= new PropertyInfo
[prop_infos
.Count
];
332 FieldInfo
[] field_info_arr
= new FieldInfo
[field_infos
.Count
];
333 object [] field_values_arr
= new object [field_values
.Count
];
334 object [] prop_values_arr
= new object [prop_values
.Count
];
336 field_infos
.CopyTo (field_info_arr
, 0);
337 field_values
.CopyTo (field_values_arr
, 0);
339 prop_values
.CopyTo (prop_values_arr
, 0);
340 prop_infos
.CopyTo (prop_info_arr
, 0);
343 cb
= new CustomAttributeBuilder (
344 (ConstructorInfo
) constructor
, pos_values
,
345 prop_info_arr
, prop_values_arr
,
346 field_info_arr
, field_values_arr
);
348 } catch (NullReferenceException
) {
350 // Don't know what to do here
355 // using System.ComponentModel;
356 // [DefaultValue (CollectionChangeAction.Add)]
357 // class X { static void Main () {} }
361 "The compiler can not encode this attribute in .NET due to\n" +
362 "\ta bug in the .NET runtime. Try the Mono runtime");
368 static string GetValidPlaces (Attribute attr
)
370 StringBuilder sb
= new StringBuilder ();
371 AttributeTargets targets
= attr
.GetAttributeUsage ().ValidOn
;
373 if ((targets
& AttributeTargets
.Assembly
) != 0)
374 sb
.Append ("'assembly' ");
376 if ((targets
& AttributeTargets
.Class
) != 0)
377 sb
.Append ("'class' ");
379 if ((targets
& AttributeTargets
.Constructor
) != 0)
380 sb
.Append ("'constructor' ");
382 if ((targets
& AttributeTargets
.Delegate
) != 0)
383 sb
.Append ("'delegate' ");
385 if ((targets
& AttributeTargets
.Enum
) != 0)
386 sb
.Append ("'enum' ");
388 if ((targets
& AttributeTargets
.Event
) != 0)
389 sb
.Append ("'event' ");
391 if ((targets
& AttributeTargets
.Field
) != 0)
392 sb
.Append ("'field' ");
394 if ((targets
& AttributeTargets
.Interface
) != 0)
395 sb
.Append ("'interface' ");
397 if ((targets
& AttributeTargets
.Method
) != 0)
398 sb
.Append ("'method' ");
400 if ((targets
& AttributeTargets
.Module
) != 0)
401 sb
.Append ("'module' ");
403 if ((targets
& AttributeTargets
.Parameter
) != 0)
404 sb
.Append ("'parameter' ");
406 if ((targets
& AttributeTargets
.Property
) != 0)
407 sb
.Append ("'property' ");
409 if ((targets
& AttributeTargets
.ReturnValue
) != 0)
410 sb
.Append ("'return value' ");
412 if ((targets
& AttributeTargets
.Struct
) != 0)
413 sb
.Append ("'struct' ");
415 return sb
.ToString ();
419 public static void Error_AttributeNotValidForElement (Attribute a
, Location loc
)
422 592, loc
, "Attribute '" + a
.Name
+
423 "' is not valid on this declaration type. " +
424 "It is valid on " + GetValidPlaces (a
) + "declarations only.");
427 public static bool CheckAttribute (Attribute a
, object element
)
429 TypeContainer attr
= TypeManager
.LookupClass (a
.Type
);
430 AttributeTargets targets
= a
.GetAttributeUsage ().ValidOn
;
432 if (element
is Class
) {
433 if ((targets
& AttributeTargets
.Class
) != 0)
438 } else if (element
is Struct
) {
439 if ((targets
& AttributeTargets
.Struct
) != 0)
443 } else if (element
is Constructor
) {
444 if ((targets
& AttributeTargets
.Constructor
) != 0)
448 } else if (element
is Delegate
) {
449 if ((targets
& AttributeTargets
.Delegate
) != 0)
453 } else if (element
is Enum
) {
454 if ((targets
& AttributeTargets
.Enum
) != 0)
458 } else if (element
is Event
/*|| element is InterfaceEvent*/) {
459 if ((targets
& AttributeTargets
.Event
) != 0)
463 } else if (element
is Field
|| element
is FieldBuilder
) {
464 if ((targets
& AttributeTargets
.Field
) != 0)
468 } else if (element
is Interface
) {
469 if ((targets
& AttributeTargets
.Interface
) != 0)
473 } else if (element
is Method
|| element
is Accessor
) {
474 if ((targets
& AttributeTargets
.Method
) != 0)
478 } else if (element
is ParameterBuilder
) {
479 if ((targets
& AttributeTargets
.Parameter
) != 0)
483 } else if (element
is Property
|| element
is Indexer
/*||
484 element is InterfaceProperty || element is InterfaceIndexer*/) {
485 if ((targets
& AttributeTargets
.Property
) != 0)
489 } else if (element
is AssemblyBuilder
){
490 if ((targets
& AttributeTargets
.Assembly
) != 0)
494 } else if (element
is ModuleBuilder
){
495 if ((targets
& AttributeTargets
.Module
) != 0)
505 /// Returns AttributeUsage attribute for this type
507 public AttributeUsageAttribute
GetAttributeUsage ()
509 Class attr_class
= (Class
) TypeManager
.LookupClass (Type
);
511 if (attr_class
== null) {
512 object[] usage_attr
= Type
.GetCustomAttributes (TypeManager
.attribute_usage_type
, true);
513 return (AttributeUsageAttribute
)usage_attr
[0];
516 return attr_class
.AttributeUsage
;
520 // This method should be invoked to pull the IndexerName attribute from an
521 // Indexer if it exists.
523 public static string ScanForIndexerName (EmitContext ec
, Attributes opt_attrs
)
525 if (opt_attrs
== null)
528 foreach (Attribute a
in opt_attrs
.Attrs
) {
529 if (a
.ResolveType (ec
) == null)
532 if (a
.Type
!= TypeManager
.indexer_name_type
)
536 // So we have found an IndexerName, pull the data out.
538 if (a
.Arguments
== null || a
.Arguments
[0] == null){
539 Error_AttributeConstructorMismatch (a
.Location
);
542 ArrayList pos_args
= (ArrayList
) a
.Arguments
[0];
543 if (pos_args
.Count
== 0){
544 Error_AttributeConstructorMismatch (a
.Location
);
548 Argument arg
= (Argument
) pos_args
[0];
549 if (!arg
.Resolve (ec
, a
.Location
))
552 Expression e
= arg
.Expr
;
553 if (!(e
is StringConstant
)){
554 Error_AttributeConstructorMismatch (a
.Location
);
559 // Remove the attribute from the list
561 opt_attrs
.Attrs
.Remove (a
);
563 return (((StringConstant
) e
).Value
);
569 // This pulls the condition name out of a Conditional attribute
571 public string Conditional_GetConditionName ()
574 // So we have a Conditional, pull the data out.
576 if (Arguments
== null || Arguments
[0] == null){
577 Error_AttributeConstructorMismatch (Location
);
581 ArrayList pos_args
= (ArrayList
) Arguments
[0];
582 if (pos_args
.Count
!= 1){
583 Error_AttributeConstructorMismatch (Location
);
587 Argument arg
= (Argument
) pos_args
[0];
588 if (!(arg
.Expr
is StringConstant
)){
589 Error_AttributeConstructorMismatch (Location
);
593 return ((StringConstant
) arg
.Expr
).Value
;
597 // This pulls the obsolete message and error flag out of an Obsolete attribute
599 public string Obsolete_GetObsoleteMessage (out bool is_error
)
603 // So we have an Obsolete, pull the data out.
605 if (Arguments
== null || Arguments
[0] == null)
608 ArrayList pos_args
= (ArrayList
) Arguments
[0];
609 if (pos_args
.Count
== 0)
611 else if (pos_args
.Count
> 2){
612 Error_AttributeConstructorMismatch (Location
);
616 Argument arg
= (Argument
) pos_args
[0];
617 if (!(arg
.Expr
is StringConstant
)){
618 Error_AttributeConstructorMismatch (Location
);
622 if (pos_args
.Count
== 2){
623 Argument arg2
= (Argument
) pos_args
[1];
624 if (!(arg2
.Expr
is BoolConstant
)){
625 Error_AttributeConstructorMismatch (Location
);
628 is_error
= ((BoolConstant
) arg2
.Expr
).Value
;
631 return ((StringConstant
) arg
.Expr
).Value
;
635 // Applies the attributes to the `builder'.
637 public static void ApplyAttributes (EmitContext ec
, object builder
, object kind
,
638 Attributes opt_attrs
, Location loc
)
640 if (opt_attrs
== null)
643 foreach (Attribute a
in opt_attrs
.Attrs
) {
644 CustomAttributeBuilder cb
= a
.Resolve (ec
);
649 if (!(kind
is TypeContainer
))
650 if (!CheckAttribute (a
, kind
)) {
651 Error_AttributeNotValidForElement (a
, loc
);
655 if (kind
is Method
|| kind
is Accessor
) {
656 if (a
.Type
== TypeManager
.methodimpl_attr_type
) {
657 if (a
.ImplOptions
== MethodImplOptions
.InternalCall
)
658 ((MethodBuilder
) builder
).SetImplementationFlags (MethodImplAttributes
.InternalCall
| MethodImplAttributes
.Runtime
);
659 } else if (a
.Type
!= TypeManager
.dllimport_type
){
660 ((MethodBuilder
) builder
).SetCustomAttribute (cb
);
662 } else if (kind
is Constructor
) {
663 ((ConstructorBuilder
) builder
).SetCustomAttribute (cb
);
664 } else if (kind
is Field
) {
665 ((FieldBuilder
) builder
).SetCustomAttribute (cb
);
666 } else if (kind
is Property
|| kind
is Indexer
) {
667 ((PropertyBuilder
) builder
).SetCustomAttribute (cb
);
668 } else if (kind
is Event
) {
669 ((MyEventBuilder
) builder
).SetCustomAttribute (cb
);
670 } else if (kind
is ParameterBuilder
) {
671 if (a
.Type
== TypeManager
.marshal_as_attr_type
) {
672 UnmanagedMarshal marshal
= UnmanagedMarshal
.DefineUnmanagedMarshal (a
.UnmanagedType
);
673 ((ParameterBuilder
) builder
).SetMarshal (marshal
);
675 ((ParameterBuilder
) builder
).SetCustomAttribute (cb
);
676 } else if (kind
is Enum
) {
677 ((TypeBuilder
) builder
).SetCustomAttribute (cb
);
678 } else if (kind
is TypeContainer
) {
679 TypeContainer tc
= (TypeContainer
) kind
;
681 if (a
.UsageAttribute
!= null) {
682 tc
.AttributeUsage
= a
.UsageAttribute
;
683 } else if (a
.Type
== TypeManager
.default_member_type
) {
684 if (tc
.Indexers
!= null) {
685 Report
.Error (646, loc
, "Cannot specify the DefaultMember attribute on" + " a type containing an indexer");
689 if (!CheckAttribute (a
, kind
)) {
690 Error_AttributeNotValidForElement (a
, loc
);
696 ((TypeBuilder
) builder
).SetCustomAttribute (cb
);
697 } catch (System
.ArgumentException
) {
698 Report
.Warning (-21, loc
, "The CharSet named property on StructLayout\n"+"\tdoes not work correctly on Microsoft.NET\n"+"\tYou might want to remove the CharSet declaration\n"+"\tor compile using the Mono runtime instead of the\n"+"\tMicrosoft .NET runtime");
700 } else if (kind
is Interface
) {
701 Interface iface
= (Interface
) kind
;
702 if (!CheckAttribute (a
, kind
)) {
703 Error_AttributeNotValidForElement (a
, loc
);
706 ((TypeBuilder
) builder
).SetCustomAttribute (cb
);
707 } else if (kind
is AssemblyBuilder
){
708 ((AssemblyBuilder
) builder
).SetCustomAttribute (cb
);
709 } else if (kind
is ModuleBuilder
) {
710 ((ModuleBuilder
) builder
).SetCustomAttribute (cb
);
711 } else if (kind
is FieldBuilder
) {
712 ((FieldBuilder
) builder
).SetCustomAttribute (cb
);
714 throw new Exception ("Unknown kind: " + kind
);
718 public MethodBuilder
DefinePInvokeMethod (EmitContext ec
, TypeBuilder builder
, string name
,
719 MethodAttributes flags
, Type ret_type
, Type
[] param_types
)
722 // We extract from the attribute the information we need
725 if (Arguments
== null) {
726 Console
.WriteLine ("Internal error : this is not supposed to happen !");
730 Type
= CheckAttributeType (ec
);
734 ArrayList named_args
= new ArrayList ();
736 ArrayList pos_args
= (ArrayList
) Arguments
[0];
737 if (Arguments
.Count
> 1)
738 named_args
= (ArrayList
) Arguments
[1];
741 string dll_name
= null;
743 Argument tmp
= (Argument
) pos_args
[0];
745 if (!tmp
.Resolve (ec
, Location
))
748 if (tmp
.Expr
is Constant
)
749 dll_name
= (string) ((Constant
) tmp
.Expr
).GetValue ();
751 Error_AttributeArgumentNotValid ();
755 // Now we process the named arguments
756 CallingConvention cc
= CallingConvention
.Winapi
;
757 CharSet charset
= CharSet
.Ansi
;
758 bool preserve_sig
= true;
759 bool exact_spelling
= false;
760 bool set_last_err
= false;
761 string entry_point
= null;
763 for (int i
= 0; i
< named_args
.Count
; i
++) {
765 DictionaryEntry de
= (DictionaryEntry
) named_args
[i
];
767 string member_name
= (string) de
.Key
;
768 Argument a
= (Argument
) de
.Value
;
770 if (!a
.Resolve (ec
, Location
))
773 Expression member
= Expression
.MemberLookup (
774 ec
, Type
, member_name
,
775 MemberTypes
.Field
| MemberTypes
.Property
,
776 BindingFlags
.Public
| BindingFlags
.Instance
,
779 if (member
== null || !(member
is FieldExpr
)) {
780 Error_InvalidNamedArgument (member_name
);
784 if (member
is FieldExpr
) {
785 FieldExpr fe
= (FieldExpr
) member
;
786 FieldInfo fi
= fe
.FieldInfo
;
789 Error_InvalidNamedArgument (member_name
);
793 if (a
.Expr
is Constant
) {
794 Constant c
= (Constant
) a
.Expr
;
796 if (member_name
== "CallingConvention")
797 cc
= (CallingConvention
) c
.GetValue ();
798 else if (member_name
== "CharSet")
799 charset
= (CharSet
) c
.GetValue ();
800 else if (member_name
== "EntryPoint")
801 entry_point
= (string) c
.GetValue ();
802 else if (member_name
== "SetLastError")
803 set_last_err
= (bool) c
.GetValue ();
804 else if (member_name
== "ExactSpelling")
805 exact_spelling
= (bool) c
.GetValue ();
806 else if (member_name
== "PreserveSig")
807 preserve_sig
= (bool) c
.GetValue ();
809 Error_AttributeArgumentNotValid ();
816 if (entry_point
== null)
819 MethodBuilder mb
= builder
.DefinePInvokeMethod (
820 name
, dll_name
, entry_point
, flags
| MethodAttributes
.HideBySig
,
821 CallingConventions
.Standard
,
828 mb
.SetImplementationFlags (MethodImplAttributes
.PreserveSig
);
833 public bool IsAssemblyAttribute
{
835 return ExplicitTarget
== "assembly";
839 public bool IsModuleAttribute
{
841 return ExplicitTarget
== "module";
846 public class Attributes
{
847 public ArrayList Attrs
;
848 public Location Location
;
850 public Attributes (Attribute a
)
852 Attrs
= new ArrayList ();
856 public Attributes (ArrayList attrs
)
861 public void Add (Attribute attr
)
866 public void Add (ArrayList attrs
)
868 Attrs
.AddRange (attrs
);
871 // public void Emit (EmitContext ec, Attributable ias)
873 // ListDictionary ld = new ListDictionary ();
875 // foreach (Attribute a in Attrs)
876 // a.Emit (ec, ias, ld);