3 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 // System.Reflection.Emit/CustomAttributeBuilder.cs
29 // Paolo Molaro (lupus@ximian.com)
31 // (C) 2001 Ximian, Inc. http://www.ximian.com
35 using System
.Reflection
;
36 using System
.Reflection
.Emit
;
37 using System
.Runtime
.CompilerServices
;
38 using System
.Runtime
.InteropServices
;
40 namespace System
.Reflection
.Emit
{
42 [ComDefaultInterface (typeof (_CustomAttributeBuilder
))]
43 [ClassInterface (ClassInterfaceType
.None
)]
44 public class CustomAttributeBuilder
: _CustomAttributeBuilder
{
48 internal ConstructorInfo Ctor
{
52 internal byte[] Data
{
56 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
57 static extern byte[] GetBlob(Assembly asmb
, ConstructorInfo con
, object[] constructorArgs
, PropertyInfo
[] namedProperties
, object[] propertyValues
, FieldInfo
[] namedFields
, object[] fieldValues
);
59 internal CustomAttributeBuilder( ConstructorInfo con
, byte[] cdata
) {
61 data
= (byte[])cdata
.Clone ();
62 /* should we check that the user supplied data is correct? */
65 public CustomAttributeBuilder( ConstructorInfo con
, object[] constructorArgs
)
67 Initialize (con
, constructorArgs
, new PropertyInfo
[0], new object [0],
68 new FieldInfo
[0], new object [0]);
70 public CustomAttributeBuilder( ConstructorInfo con
, object[] constructorArgs
,
71 FieldInfo
[] namedFields
, object[] fieldValues
)
73 Initialize (con
, constructorArgs
, new PropertyInfo
[0], new object [0],
74 namedFields
, fieldValues
);
76 public CustomAttributeBuilder( ConstructorInfo con
, object[] constructorArgs
,
77 PropertyInfo
[] namedProperties
, object[] propertyValues
)
79 Initialize (con
, constructorArgs
, namedProperties
, propertyValues
, new FieldInfo
[0],
82 public CustomAttributeBuilder( ConstructorInfo con
, object[] constructorArgs
,
83 PropertyInfo
[] namedProperties
, object[] propertyValues
,
84 FieldInfo
[] namedFields
, object[] fieldValues
)
86 Initialize (con
, constructorArgs
, namedProperties
, propertyValues
, namedFields
, fieldValues
);
89 private bool IsValidType (Type t
)
91 /* FIXME: Add more checks */
92 if (t
.IsArray
&& t
.GetArrayRank () > 1)
97 private void Initialize (ConstructorInfo con
, object [] constructorArgs
,
98 PropertyInfo
[] namedProperties
, object [] propertyValues
,
99 FieldInfo
[] namedFields
, object [] fieldValues
)
103 throw new ArgumentNullException ("con");
104 if (constructorArgs
== null)
105 throw new ArgumentNullException ("constructorArgs");
106 if (namedProperties
== null)
107 throw new ArgumentNullException ("namedProperties");
108 if (propertyValues
== null)
109 throw new ArgumentNullException ("propertyValues");
110 if (namedFields
== null)
111 throw new ArgumentNullException ("namedFields");
112 if (fieldValues
== null)
113 throw new ArgumentNullException ("fieldValues");
114 if (con
.GetParameterCount () != constructorArgs
.Length
)
115 throw new ArgumentException ("Parameter count does not match " +
116 "passed in argument value count.");
117 if (namedProperties
.Length
!= propertyValues
.Length
)
118 throw new ArgumentException ("Array lengths must be the same.",
119 "namedProperties, propertyValues");
120 if (namedFields
.Length
!= fieldValues
.Length
)
121 throw new ArgumentException ("Array lengths must be the same.",
122 "namedFields, fieldValues");
123 if ((con
.Attributes
& MethodAttributes
.Static
) == MethodAttributes
.Static
||
124 (con
.Attributes
& MethodAttributes
.MemberAccessMask
) == MethodAttributes
.Private
)
125 throw new ArgumentException ("Cannot have private or static constructor.");
127 Type atype
= ctor
.DeclaringType
;
130 foreach (FieldInfo fi
in namedFields
) {
131 Type t
= fi
.DeclaringType
;
132 if (!IsValidType (t
))
133 throw new ArgumentException ("Field '" + fi
.Name
+ "' does not have a valid type.");
134 if ((atype
!= t
) && (!t
.IsSubclassOf (atype
)) && (!atype
.IsSubclassOf (t
)))
135 throw new ArgumentException ("Field '" + fi
.Name
+ "' does not belong to the same class as the constructor");
136 // FIXME: Check enums and TypeBuilders as well
137 if (fieldValues
[i
] != null)
138 // IsEnum does not seem to work on TypeBuilders
139 if (!(fi
.FieldType
is TypeBuilder
) && !fi
.FieldType
.IsEnum
&& !fi
.FieldType
.IsInstanceOfType (fieldValues
[i
])) {
141 // mcs allways uses object[] for array types and
142 // MS.NET allows this
144 if (!fi
.FieldType
.IsArray
)
145 throw new ArgumentException ("Value of field '" + fi
.Name
+ "' does not match field type: " + fi
.FieldType
);
151 foreach (PropertyInfo pi
in namedProperties
) {
153 throw new ArgumentException ("Property '" + pi
.Name
+ "' does not have a setter.");
154 Type t
= pi
.DeclaringType
;
155 if (!IsValidType (t
))
156 throw new ArgumentException ("Property '" + pi
.Name
+ "' does not have a valid type.");
157 if ((atype
!= t
) && (!t
.IsSubclassOf (atype
)) && (!atype
.IsSubclassOf (t
)))
158 throw new ArgumentException ("Property '" + pi
.Name
+ "' does not belong to the same class as the constructor");
159 if (propertyValues
[i
] != null) {
160 if (!(pi
.PropertyType
is TypeBuilder
) && !pi
.PropertyType
.IsEnum
&& !pi
.PropertyType
.IsInstanceOfType (propertyValues
[i
]))
161 if (!pi
.PropertyType
.IsArray
)
162 throw new ArgumentException ("Value of property '" + pi
.Name
+ "' does not match property type: " + pi
.PropertyType
+ " -> " + propertyValues
[i
]);
168 foreach (ParameterInfo pi
in GetParameters (con
)) {
170 Type paramType
= pi
.ParameterType
;
171 if (!IsValidType (paramType
))
172 throw new ArgumentException ("Argument " + i
+ " does not have a valid type.");
173 if (constructorArgs
[i
] != null)
174 if (!(paramType
is TypeBuilder
) && !paramType
.IsEnum
&& !paramType
.IsInstanceOfType (constructorArgs
[i
]))
175 if (!paramType
.IsArray
)
176 throw new ArgumentException ("Value of argument " + i
+ " does not match parameter type: " + paramType
+ " -> " + constructorArgs
[i
]);
181 data
= GetBlob (atype
.Assembly
, con
, constructorArgs
, namedProperties
, propertyValues
, namedFields
, fieldValues
);
185 internal static int decode_len (byte[] data
, int pos
, out int rpos
) {
187 if ((data
[pos
] & 0x80) == 0) {
188 len
= (int)(data
[pos
++] & 0x7f);
189 } else if ((data
[pos
] & 0x40) == 0) {
190 len
= ((data
[pos
] & 0x3f) << 8) + data
[pos
+ 1];
193 len
= ((data
[pos
] & 0x1f) << 24) + (data
[pos
+ 1] << 16) + (data
[pos
+ 2] << 8) + data
[pos
+ 3];
200 internal static string string_from_bytes (byte[] data
, int pos
, int len
)
202 return System
.Text
.Encoding
.UTF8
.GetString(data
, pos
, len
);
205 internal string string_arg ()
208 int len
= decode_len (data
, pos
, out pos
);
209 return string_from_bytes (data
, pos
, len
);
212 internal static UnmanagedMarshal
get_umarshal (CustomAttributeBuilder customBuilder
, bool is_field
) {
213 byte[] data
= customBuilder
.Data
;
214 UnmanagedType subtype
= (UnmanagedType
)0x50; /* NATIVE_MAX */
216 int sizeParamIndex
= -1;
217 bool hasSize
= false;
219 int utype
; /* the (stupid) ctor takes a short or an enum ... */
220 string marshalTypeName
= null;
221 Type marshalTypeRef
= null;
222 string marshalCookie
= String
.Empty
;
223 utype
= (int)data
[2];
224 utype
|= ((int)data
[3]) << 8;
226 string first_type_name
= GetParameters (customBuilder
.Ctor
) [0].ParameterType
.FullName
;
228 if (first_type_name
== "System.Int16")
230 int nnamed
= (int)data
[pos
++];
231 nnamed
|= ((int)data
[pos
++]) << 8;
233 for (int i
= 0; i
< nnamed
; ++i
) {
234 int paramType
; // What is this ?
236 /* Skip field/property signature */
239 paramType
= ((int)data
[pos
++]);
240 if (paramType
== 0x55) {
241 /* enums, the value is preceeded by the type */
242 int len2
= decode_len (data
, pos
, out pos
);
243 string_from_bytes (data
, pos
, len2
);
246 int len
= decode_len (data
, pos
, out pos
);
247 string named_name
= string_from_bytes (data
, pos
, len
);
250 switch (named_name
) {
252 value = (int)data
[pos
++];
253 value |= ((int)data
[pos
++]) << 8;
254 value |= ((int)data
[pos
++]) << 16;
255 value |= ((int)data
[pos
++]) << 24;
256 subtype
= (UnmanagedType
)value;
259 value = (int)data
[pos
++];
260 value |= ((int)data
[pos
++]) << 8;
261 value |= ((int)data
[pos
++]) << 16;
262 value |= ((int)data
[pos
++]) << 24;
266 case "SafeArraySubType":
267 value = (int)data
[pos
++];
268 value |= ((int)data
[pos
++]) << 8;
269 value |= ((int)data
[pos
++]) << 16;
270 value |= ((int)data
[pos
++]) << 24;
271 subtype
= (UnmanagedType
)value;
273 case "IidParameterIndex":
276 case "SafeArrayUserDefinedSubType":
277 len
= decode_len (data
, pos
, out pos
);
278 string_from_bytes (data
, pos
, len
);
281 case "SizeParamIndex":
282 value = (int)data
[pos
++];
283 value |= ((int)data
[pos
++]) << 8;
284 sizeParamIndex
= value;
288 len
= decode_len (data
, pos
, out pos
);
289 marshalTypeName
= string_from_bytes (data
, pos
, len
);
292 case "MarshalTypeRef":
293 len
= decode_len (data
, pos
, out pos
);
294 marshalTypeName
= string_from_bytes (data
, pos
, len
);
295 marshalTypeRef
= Type
.GetType (marshalTypeName
);
298 case "MarshalCookie":
299 len
= decode_len (data
, pos
, out pos
);
300 marshalCookie
= string_from_bytes (data
, pos
, len
);
304 throw new Exception ("Unknown MarshalAsAttribute field: " + named_name
);
308 switch ((UnmanagedType
)utype
) {
309 case UnmanagedType
.LPArray
:
311 return UnmanagedMarshal
.DefineLPArrayInternal (subtype
, sizeConst
, sizeParamIndex
);
313 return UnmanagedMarshal
.DefineLPArray (subtype
);
314 case UnmanagedType
.SafeArray
:
315 return UnmanagedMarshal
.DefineSafeArray (subtype
);
316 case UnmanagedType
.ByValArray
:
318 throw new ArgumentException ("Specified unmanaged type is only valid on fields");
320 return UnmanagedMarshal
.DefineByValArray (sizeConst
);
321 case UnmanagedType
.ByValTStr
:
322 return UnmanagedMarshal
.DefineByValTStr (sizeConst
);
323 case UnmanagedType
.CustomMarshaler
:
324 return UnmanagedMarshal
.DefineCustom (marshalTypeRef
, marshalCookie
, marshalTypeName
, Guid
.Empty
);
326 return UnmanagedMarshal
.DefineUnmanagedMarshal ((UnmanagedType
)utype
);
330 static Type
elementTypeToType (int elementType
) {
331 /* Partition II, section 23.1.16 */
332 switch (elementType
) {
334 return typeof (bool);
336 return typeof (char);
338 return typeof (sbyte);
340 return typeof (byte);
342 return typeof (short);
344 return typeof (ushort);
348 return typeof (uint);
350 return typeof (long);
352 return typeof (ulong);
354 return typeof (float);
356 return typeof (double);
358 return typeof (string);
360 throw new Exception ("Unknown element type '" + elementType
+ "'");
364 static object decode_cattr_value (Type t
, byte[] data
, int pos
, out int rpos
) {
365 switch (Type
.GetTypeCode (t
)) {
366 case TypeCode
.String
:
367 if (data
[pos
] == 0xff) {
371 int len
= decode_len (data
, pos
, out pos
);
373 return string_from_bytes (data
, pos
, len
);
376 return data
[pos
] + (data
[pos
+ 1] << 8) + (data
[pos
+ 2] << 16) + (data
[pos
+ 3] << 24);
377 case TypeCode
.Boolean
:
379 return (data
[pos
] == 0) ? false : true;
380 case TypeCode
.Object
:
381 int subtype
= data
[pos
];
384 if (subtype
>= 0x02 && subtype
<= 0x0e)
385 return decode_cattr_value (elementTypeToType (subtype
), data
, pos
, out rpos
);
387 throw new Exception ("Subtype '" + subtype
+ "' of type object not yet handled in decode_cattr_value");
389 throw new Exception ("FIXME: Type " + t
+ " not yet handled in decode_cattr_value.");
393 internal struct CustomAttributeInfo
{
394 public ConstructorInfo ctor
;
395 public object[] ctorArgs
;
396 public string[] namedParamNames
;
397 public object[] namedParamValues
;
400 internal static CustomAttributeInfo
decode_cattr (CustomAttributeBuilder customBuilder
) {
401 byte[] data
= customBuilder
.Data
;
402 ConstructorInfo ctor
= customBuilder
.Ctor
;
405 CustomAttributeInfo info
= new CustomAttributeInfo ();
409 throw new Exception ("Custom attr length is only '" + data
.Length
+ "'");
410 if ((data
[0] != 0x1) || (data
[1] != 0x00))
411 throw new Exception ("Prolog invalid");
414 ParameterInfo
[] pi
= GetParameters (ctor
);
416 info
.ctorArgs
= new object [pi
.Length
];
417 for (int i
= 0; i
< pi
.Length
; ++i
)
418 info
.ctorArgs
[i
] = decode_cattr_value (pi
[i
].ParameterType
, data
, pos
, out pos
);
420 int num_named
= data
[pos
] + (data
[pos
+ 1] * 256);
423 info
.namedParamNames
= new string [num_named
];
424 info
.namedParamValues
= new object [num_named
];
425 for (int i
= 0; i
< num_named
; ++i
) {
426 int named_type
= data
[pos
++];
427 int data_type
= data
[pos
++];
428 string enum_type_name
= null;
430 if (data_type
== 0x55) {
431 int len2
= decode_len (data
, pos
, out pos
);
432 enum_type_name
= string_from_bytes (data
, pos
, len2
);
436 int len
= decode_len (data
, pos
, out pos
);
437 string name
= string_from_bytes (data
, pos
, len
);
438 info
.namedParamNames
[i
] = name
;
441 if (named_type
== 0x53) {
443 FieldInfo fi
= ctor
.DeclaringType
.GetField (name
, BindingFlags
.Public
|BindingFlags
.NonPublic
|BindingFlags
.Instance
);
445 throw new Exception ("Custom attribute type '" + ctor
.DeclaringType
+ "' doesn't contain a field named '" + name
+ "'");
447 object val
= decode_cattr_value (fi
.FieldType
, data
, pos
, out pos
);
448 if (enum_type_name
!= null) {
449 Type enumType
= Type
.GetType (enum_type_name
);
450 val
= Enum
.ToObject (enumType
, val
);
453 info
.namedParamValues
[i
] = val
;
457 throw new Exception ("Unknown named type: " + named_type
);
463 void _CustomAttributeBuilder
.GetIDsOfNames ([In
] ref Guid riid
, IntPtr rgszNames
, uint cNames
, uint lcid
, IntPtr rgDispId
)
465 throw new NotImplementedException ();
468 void _CustomAttributeBuilder
.GetTypeInfo (uint iTInfo
, uint lcid
, IntPtr ppTInfo
)
470 throw new NotImplementedException ();
473 void _CustomAttributeBuilder
.GetTypeInfoCount (out uint pcTInfo
)
475 throw new NotImplementedException ();
478 void _CustomAttributeBuilder
.Invoke (uint dispIdMember
, [In
] ref Guid riid
, uint lcid
, short wFlags
, IntPtr pDispParams
, IntPtr pVarResult
, IntPtr pExcepInfo
, IntPtr puArgErr
)
480 throw new NotImplementedException ();
483 static ParameterInfo
[] GetParameters (ConstructorInfo ctor
)
485 ConstructorBuilder cb
= ctor
as ConstructorBuilder
;
487 return cb
.GetParametersInternal ();
488 return ctor
.GetParameters ();