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)
94 if (t
is TypeBuilder
&& t
.IsEnum
) {
95 // Check that the enum is properly constructed, the unmanaged code
97 Enum
.GetUnderlyingType (t
);
99 if (t
.IsClass
&& !(t
.IsArray
|| t
== typeof (object) || t
== typeof (Type
) || t
== typeof (string) || t
.Assembly
.GetName ().Name
== "mscorlib"))
101 if (t
.IsValueType
&& !(t
.IsPrimitive
|| t
.IsEnum
|| ((t
.Assembly
is AssemblyBuilder
) && t
.Assembly
.GetName ().Name
== "mscorlib")))
106 private bool IsValidParam (object o
, Type paramType
)
108 Type t
= o
.GetType ();
109 if (!IsValidType (t
))
111 if (paramType
== typeof (object)) {
112 if (t
.IsArray
&& t
.GetArrayRank () == 1)
113 return IsValidType (t
.GetElementType ());
114 if (!t
.IsPrimitive
&& !typeof (Type
).IsAssignableFrom (t
) && t
!= typeof (string) && !t
.IsEnum
)
120 private void Initialize (ConstructorInfo con
, object [] constructorArgs
,
121 PropertyInfo
[] namedProperties
, object [] propertyValues
,
122 FieldInfo
[] namedFields
, object [] fieldValues
)
126 throw new ArgumentNullException ("con");
127 if (constructorArgs
== null)
128 throw new ArgumentNullException ("constructorArgs");
129 if (namedProperties
== null)
130 throw new ArgumentNullException ("namedProperties");
131 if (propertyValues
== null)
132 throw new ArgumentNullException ("propertyValues");
133 if (namedFields
== null)
134 throw new ArgumentNullException ("namedFields");
135 if (fieldValues
== null)
136 throw new ArgumentNullException ("fieldValues");
137 if (con
.GetParameterCount () != constructorArgs
.Length
)
138 throw new ArgumentException ("Parameter count does not match " +
139 "passed in argument value count.");
140 if (namedProperties
.Length
!= propertyValues
.Length
)
141 throw new ArgumentException ("Array lengths must be the same.",
142 "namedProperties, propertyValues");
143 if (namedFields
.Length
!= fieldValues
.Length
)
144 throw new ArgumentException ("Array lengths must be the same.",
145 "namedFields, fieldValues");
146 if ((con
.Attributes
& MethodAttributes
.Static
) == MethodAttributes
.Static
||
147 (con
.Attributes
& MethodAttributes
.MemberAccessMask
) == MethodAttributes
.Private
)
148 throw new ArgumentException ("Cannot have private or static constructor.");
150 Type atype
= ctor
.DeclaringType
;
153 foreach (FieldInfo fi
in namedFields
) {
154 Type t
= fi
.DeclaringType
;
155 if ((atype
!= t
) && (!t
.IsSubclassOf (atype
)) && (!atype
.IsSubclassOf (t
)))
156 throw new ArgumentException ("Field '" + fi
.Name
+ "' does not belong to the same class as the constructor");
157 if (!IsValidType (fi
.FieldType
))
158 throw new ArgumentException ("Field '" + fi
.Name
+ "' does not have a valid type.");
159 // FIXME: Check enums and TypeBuilders as well
160 if (fieldValues
[i
] != null)
161 // IsEnum does not seem to work on TypeBuilders
162 if (!(fi
.FieldType
is TypeBuilder
) && !fi
.FieldType
.IsEnum
&& !fi
.FieldType
.IsInstanceOfType (fieldValues
[i
])) {
164 // mcs allways uses object[] for array types and
165 // MS.NET allows this
167 if (!fi
.FieldType
.IsArray
)
168 throw new ArgumentException ("Value of field '" + fi
.Name
+ "' does not match field type: " + fi
.FieldType
);
174 foreach (PropertyInfo pi
in namedProperties
) {
176 throw new ArgumentException ("Property '" + pi
.Name
+ "' does not have a setter.");
177 Type t
= pi
.DeclaringType
;
178 if ((atype
!= t
) && (!t
.IsSubclassOf (atype
)) && (!atype
.IsSubclassOf (t
)))
179 throw new ArgumentException ("Property '" + pi
.Name
+ "' does not belong to the same class as the constructor");
180 if (!IsValidType (pi
.PropertyType
))
181 throw new ArgumentException ("Property '" + pi
.Name
+ "' does not have a valid type.");
182 if (propertyValues
[i
] != null) {
183 if (!(pi
.PropertyType
is TypeBuilder
) && !pi
.PropertyType
.IsEnum
&& !pi
.PropertyType
.IsInstanceOfType (propertyValues
[i
]))
184 if (!pi
.PropertyType
.IsArray
)
185 throw new ArgumentException ("Value of property '" + pi
.Name
+ "' does not match property type: " + pi
.PropertyType
+ " -> " + propertyValues
[i
]);
191 foreach (ParameterInfo pi
in GetParameters (con
)) {
193 Type paramType
= pi
.ParameterType
;
194 if (!IsValidType (paramType
))
195 throw new ArgumentException ("Parameter " + i
+ " does not have a valid type.");
196 if (constructorArgs
[i
] != null) {
197 if (!(paramType
is TypeBuilder
) && !paramType
.IsEnum
&& !paramType
.IsInstanceOfType (constructorArgs
[i
]))
198 if (!paramType
.IsArray
)
199 throw new ArgumentException ("Value of argument " + i
+ " does not match parameter type: " + paramType
+ " -> " + constructorArgs
[i
]);
200 if (!IsValidParam (constructorArgs
[i
], paramType
))
201 throw new ArgumentException ("Cannot emit a CustomAttribute with argument of type " + constructorArgs
[i
].GetType () + ".");
207 data
= GetBlob (atype
.Assembly
, con
, constructorArgs
, namedProperties
, propertyValues
, namedFields
, fieldValues
);
211 internal static int decode_len (byte[] data
, int pos
, out int rpos
) {
213 if ((data
[pos
] & 0x80) == 0) {
214 len
= (int)(data
[pos
++] & 0x7f);
215 } else if ((data
[pos
] & 0x40) == 0) {
216 len
= ((data
[pos
] & 0x3f) << 8) + data
[pos
+ 1];
219 len
= ((data
[pos
] & 0x1f) << 24) + (data
[pos
+ 1] << 16) + (data
[pos
+ 2] << 8) + data
[pos
+ 3];
226 internal static string string_from_bytes (byte[] data
, int pos
, int len
)
228 return System
.Text
.Encoding
.UTF8
.GetString(data
, pos
, len
);
231 internal string string_arg ()
234 int len
= decode_len (data
, pos
, out pos
);
235 return string_from_bytes (data
, pos
, len
);
238 internal static UnmanagedMarshal
get_umarshal (CustomAttributeBuilder customBuilder
, bool is_field
) {
239 byte[] data
= customBuilder
.Data
;
240 UnmanagedType subtype
= (UnmanagedType
)0x50; /* NATIVE_MAX */
242 int sizeParamIndex
= -1;
243 bool hasSize
= false;
245 int utype
; /* the (stupid) ctor takes a short or an enum ... */
246 string marshalTypeName
= null;
247 Type marshalTypeRef
= null;
248 string marshalCookie
= String
.Empty
;
249 utype
= (int)data
[2];
250 utype
|= ((int)data
[3]) << 8;
252 string first_type_name
= GetParameters (customBuilder
.Ctor
) [0].ParameterType
.FullName
;
254 if (first_type_name
== "System.Int16")
256 int nnamed
= (int)data
[pos
++];
257 nnamed
|= ((int)data
[pos
++]) << 8;
259 for (int i
= 0; i
< nnamed
; ++i
) {
260 int paramType
; // What is this ?
262 /* Skip field/property signature */
265 paramType
= ((int)data
[pos
++]);
266 if (paramType
== 0x55) {
267 /* enums, the value is preceeded by the type */
268 int len2
= decode_len (data
, pos
, out pos
);
269 string_from_bytes (data
, pos
, len2
);
272 int len
= decode_len (data
, pos
, out pos
);
273 string named_name
= string_from_bytes (data
, pos
, len
);
276 switch (named_name
) {
278 value = (int)data
[pos
++];
279 value |= ((int)data
[pos
++]) << 8;
280 value |= ((int)data
[pos
++]) << 16;
281 value |= ((int)data
[pos
++]) << 24;
282 subtype
= (UnmanagedType
)value;
285 value = (int)data
[pos
++];
286 value |= ((int)data
[pos
++]) << 8;
287 value |= ((int)data
[pos
++]) << 16;
288 value |= ((int)data
[pos
++]) << 24;
292 case "SafeArraySubType":
293 value = (int)data
[pos
++];
294 value |= ((int)data
[pos
++]) << 8;
295 value |= ((int)data
[pos
++]) << 16;
296 value |= ((int)data
[pos
++]) << 24;
297 subtype
= (UnmanagedType
)value;
299 case "IidParameterIndex":
302 case "SafeArrayUserDefinedSubType":
303 len
= decode_len (data
, pos
, out pos
);
304 string_from_bytes (data
, pos
, len
);
307 case "SizeParamIndex":
308 value = (int)data
[pos
++];
309 value |= ((int)data
[pos
++]) << 8;
310 sizeParamIndex
= value;
314 len
= decode_len (data
, pos
, out pos
);
315 marshalTypeName
= string_from_bytes (data
, pos
, len
);
318 case "MarshalTypeRef":
319 len
= decode_len (data
, pos
, out pos
);
320 marshalTypeName
= string_from_bytes (data
, pos
, len
);
321 marshalTypeRef
= Type
.GetType (marshalTypeName
);
324 case "MarshalCookie":
325 len
= decode_len (data
, pos
, out pos
);
326 marshalCookie
= string_from_bytes (data
, pos
, len
);
330 throw new Exception ("Unknown MarshalAsAttribute field: " + named_name
);
334 switch ((UnmanagedType
)utype
) {
335 case UnmanagedType
.LPArray
:
337 return UnmanagedMarshal
.DefineLPArrayInternal (subtype
, sizeConst
, sizeParamIndex
);
339 return UnmanagedMarshal
.DefineLPArray (subtype
);
340 case UnmanagedType
.SafeArray
:
341 return UnmanagedMarshal
.DefineSafeArray (subtype
);
342 case UnmanagedType
.ByValArray
:
344 throw new ArgumentException ("Specified unmanaged type is only valid on fields");
346 return UnmanagedMarshal
.DefineByValArray (sizeConst
);
347 case UnmanagedType
.ByValTStr
:
348 return UnmanagedMarshal
.DefineByValTStr (sizeConst
);
349 case UnmanagedType
.CustomMarshaler
:
350 return UnmanagedMarshal
.DefineCustom (marshalTypeRef
, marshalCookie
, marshalTypeName
, Guid
.Empty
);
352 return UnmanagedMarshal
.DefineUnmanagedMarshal ((UnmanagedType
)utype
);
356 static Type
elementTypeToType (int elementType
) {
357 /* Partition II, section 23.1.16 */
358 switch (elementType
) {
360 return typeof (bool);
362 return typeof (char);
364 return typeof (sbyte);
366 return typeof (byte);
368 return typeof (short);
370 return typeof (ushort);
374 return typeof (uint);
376 return typeof (long);
378 return typeof (ulong);
380 return typeof (float);
382 return typeof (double);
384 return typeof (string);
386 throw new Exception ("Unknown element type '" + elementType
+ "'");
390 static object decode_cattr_value (Type t
, byte[] data
, int pos
, out int rpos
) {
391 switch (Type
.GetTypeCode (t
)) {
392 case TypeCode
.String
:
393 if (data
[pos
] == 0xff) {
397 int len
= decode_len (data
, pos
, out pos
);
399 return string_from_bytes (data
, pos
, len
);
402 return data
[pos
] + (data
[pos
+ 1] << 8) + (data
[pos
+ 2] << 16) + (data
[pos
+ 3] << 24);
403 case TypeCode
.Boolean
:
405 return (data
[pos
] == 0) ? false : true;
406 case TypeCode
.Object
:
407 int subtype
= data
[pos
];
410 if (subtype
>= 0x02 && subtype
<= 0x0e)
411 return decode_cattr_value (elementTypeToType (subtype
), data
, pos
, out rpos
);
413 throw new Exception ("Subtype '" + subtype
+ "' of type object not yet handled in decode_cattr_value");
415 throw new Exception ("FIXME: Type " + t
+ " not yet handled in decode_cattr_value.");
419 internal struct CustomAttributeInfo
{
420 public ConstructorInfo ctor
;
421 public object[] ctorArgs
;
422 public string[] namedParamNames
;
423 public object[] namedParamValues
;
426 internal static CustomAttributeInfo
decode_cattr (CustomAttributeBuilder customBuilder
) {
427 byte[] data
= customBuilder
.Data
;
428 ConstructorInfo ctor
= customBuilder
.Ctor
;
431 CustomAttributeInfo info
= new CustomAttributeInfo ();
435 throw new Exception ("Custom attr length is only '" + data
.Length
+ "'");
436 if ((data
[0] != 0x1) || (data
[1] != 0x00))
437 throw new Exception ("Prolog invalid");
440 ParameterInfo
[] pi
= GetParameters (ctor
);
442 info
.ctorArgs
= new object [pi
.Length
];
443 for (int i
= 0; i
< pi
.Length
; ++i
)
444 info
.ctorArgs
[i
] = decode_cattr_value (pi
[i
].ParameterType
, data
, pos
, out pos
);
446 int num_named
= data
[pos
] + (data
[pos
+ 1] * 256);
449 info
.namedParamNames
= new string [num_named
];
450 info
.namedParamValues
= new object [num_named
];
451 for (int i
= 0; i
< num_named
; ++i
) {
452 int named_type
= data
[pos
++];
453 int data_type
= data
[pos
++];
454 string enum_type_name
= null;
456 if (data_type
== 0x55) {
457 int len2
= decode_len (data
, pos
, out pos
);
458 enum_type_name
= string_from_bytes (data
, pos
, len2
);
462 int len
= decode_len (data
, pos
, out pos
);
463 string name
= string_from_bytes (data
, pos
, len
);
464 info
.namedParamNames
[i
] = name
;
467 if (named_type
== 0x53) {
469 FieldInfo fi
= ctor
.DeclaringType
.GetField (name
, BindingFlags
.Public
|BindingFlags
.NonPublic
|BindingFlags
.Instance
);
471 throw new Exception ("Custom attribute type '" + ctor
.DeclaringType
+ "' doesn't contain a field named '" + name
+ "'");
473 object val
= decode_cattr_value (fi
.FieldType
, data
, pos
, out pos
);
474 if (enum_type_name
!= null) {
475 Type enumType
= Type
.GetType (enum_type_name
);
476 val
= Enum
.ToObject (enumType
, val
);
479 info
.namedParamValues
[i
] = val
;
483 throw new Exception ("Unknown named type: " + named_type
);
489 void _CustomAttributeBuilder
.GetIDsOfNames ([In
] ref Guid riid
, IntPtr rgszNames
, uint cNames
, uint lcid
, IntPtr rgDispId
)
491 throw new NotImplementedException ();
494 void _CustomAttributeBuilder
.GetTypeInfo (uint iTInfo
, uint lcid
, IntPtr ppTInfo
)
496 throw new NotImplementedException ();
499 void _CustomAttributeBuilder
.GetTypeInfoCount (out uint pcTInfo
)
501 throw new NotImplementedException ();
504 void _CustomAttributeBuilder
.Invoke (uint dispIdMember
, [In
] ref Guid riid
, uint lcid
, short wFlags
, IntPtr pDispParams
, IntPtr pVarResult
, IntPtr pExcepInfo
, IntPtr puArgErr
)
506 throw new NotImplementedException ();
509 static ParameterInfo
[] GetParameters (ConstructorInfo ctor
)
511 ConstructorBuilder cb
= ctor
as ConstructorBuilder
;
513 return cb
.GetParametersInternal ();
514 return ctor
.GetParameters ();