do not check order sequence if option /order was not used
[mono.git] / mcs / class / corlib / System.Reflection.Emit / CustomAttributeBuilder.cs
1
2 //
3 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
4 //
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:
12 // 
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 // 
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.
23 //
24
25 //
26 // System.Reflection.Emit/CustomAttributeBuilder.cs
27 //
28 // Author:
29 //   Paolo Molaro (lupus@ximian.com)
30 //
31 // (C) 2001 Ximian, Inc.  http://www.ximian.com
32 //
33
34 using System;
35 using System.Reflection;
36 using System.Reflection.Emit;
37 using System.Runtime.CompilerServices;
38 using System.Runtime.InteropServices;
39
40 namespace System.Reflection.Emit {
41         [ComVisible (true)]
42         [ComDefaultInterface (typeof (_CustomAttributeBuilder))]
43         [ClassInterface (ClassInterfaceType.None)]
44         [StructLayout (LayoutKind.Sequential)]
45         public class CustomAttributeBuilder : _CustomAttributeBuilder {
46                 ConstructorInfo ctor;
47                 byte[] data;
48
49                 internal ConstructorInfo Ctor {
50                         get {return ctor;}
51                 }
52
53                 internal byte[] Data {
54                         get {return data;}
55                 }
56                 
57                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
58                 static extern byte[] GetBlob(Assembly asmb, ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues, FieldInfo[] namedFields, object[] fieldValues);
59                 
60                 internal CustomAttributeBuilder( ConstructorInfo con, byte[] cdata) {
61                         ctor = con;
62                         data = (byte[])cdata.Clone ();
63                         /* should we check that the user supplied data is correct? */
64                 }
65                 
66                 public CustomAttributeBuilder( ConstructorInfo con, object[] constructorArgs)
67                 {
68                         Initialize (con, constructorArgs, new PropertyInfo [0], new object [0],
69                                         new FieldInfo [0], new object [0]);
70                 }
71                 public CustomAttributeBuilder( ConstructorInfo con, object[] constructorArgs,
72                                 FieldInfo[] namedFields, object[] fieldValues) 
73                 {
74                         Initialize (con, constructorArgs, new PropertyInfo [0], new object [0],
75                                         namedFields, fieldValues);
76                 }
77                 public CustomAttributeBuilder( ConstructorInfo con, object[] constructorArgs,
78                                 PropertyInfo[] namedProperties, object[] propertyValues)
79                 {
80                         Initialize (con, constructorArgs, namedProperties, propertyValues, new FieldInfo [0],
81                                         new object [0]);
82                 }
83                 public CustomAttributeBuilder( ConstructorInfo con, object[] constructorArgs,
84                                 PropertyInfo[] namedProperties, object[] propertyValues,
85                                 FieldInfo[] namedFields, object[] fieldValues)
86                 {
87                         Initialize (con, constructorArgs, namedProperties, propertyValues, namedFields, fieldValues);
88                 }
89
90                 private bool IsValidType (Type t)
91                 {
92                         /* FIXME: Add more checks */
93                         if (t.IsArray && t.GetArrayRank () > 1)
94                                 return false;
95                         if (t is TypeBuilder && t.IsEnum) {
96                                 // Check that the enum is properly constructed, the unmanaged code
97                                 // depends on this
98                                 Enum.GetUnderlyingType (t);
99                         }
100                         if (t.IsClass && !(t.IsArray || t == typeof (object) || t == typeof (Type) || t == typeof (string) || t.Assembly.GetName ().Name == "mscorlib"))
101                                 return false;
102                         if (t.IsValueType && !(t.IsPrimitive || t.IsEnum || ((t.Assembly is AssemblyBuilder) && t.Assembly.GetName ().Name == "mscorlib")))
103                                 return false;
104                         return true;
105                 }
106
107                 private bool IsValidParam (object o, Type paramType)
108                 {
109                         Type t = o.GetType ();
110                         if (!IsValidType (t))
111                                 return false;
112                         if (paramType == typeof (object)) {
113                                 if (t.IsArray && t.GetArrayRank () == 1)
114                                         return IsValidType (t.GetElementType ());
115                                 if (!t.IsPrimitive && !typeof (Type).IsAssignableFrom (t) && t != typeof (string) && !t.IsEnum)
116                                         return false;
117                         }
118                         return true;
119                 }
120
121                 static bool IsValidValue (Type type, object value) {
122                         if (type.IsValueType && value == null)
123                                 return false;
124                         if (type.IsArray && type.GetElementType ().IsValueType) {
125                                 foreach (var v in (Array)value) {
126                                         if (v == null)
127                                                 return false;
128                                 }
129                         }
130                         return true;
131                 } 
132
133                 private void Initialize (ConstructorInfo con, object [] constructorArgs,
134                                 PropertyInfo [] namedProperties, object [] propertyValues,
135                                 FieldInfo [] namedFields, object [] fieldValues)
136                 {
137                         ctor = con;
138                         if (con == null)
139                                 throw new ArgumentNullException ("con");
140                         if (constructorArgs == null)
141                                 throw new ArgumentNullException ("constructorArgs");
142                         if (namedProperties == null)
143                                 throw new ArgumentNullException ("namedProperties");
144                         if (propertyValues == null)
145                                 throw new ArgumentNullException ("propertyValues");
146                         if (namedFields == null)
147                                 throw new ArgumentNullException ("namedFields");
148                         if (fieldValues == null)
149                                 throw new ArgumentNullException ("fieldValues");
150                         if (con.GetParameterCount () != constructorArgs.Length)
151                                 throw new ArgumentException ("Parameter count does not match " +
152                                                 "passed in argument value count.");
153                         if (namedProperties.Length != propertyValues.Length)
154                                 throw new ArgumentException ("Array lengths must be the same.",
155                                                 "namedProperties, propertyValues");
156                         if (namedFields.Length != fieldValues.Length)
157                                 throw new ArgumentException ("Array lengths must be the same.",
158                                                 "namedFields, fieldValues");
159                         if ((con.Attributes & MethodAttributes.Static) == MethodAttributes.Static ||
160                                         (con.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Private)
161                                 throw new ArgumentException ("Cannot have private or static constructor.");
162
163                         Type atype = ctor.DeclaringType;
164                         int i;
165                         i = 0;
166                         foreach (FieldInfo fi in namedFields) {
167                                 Type t = fi.DeclaringType;
168                                 if ((atype != t) && (!t.IsSubclassOf (atype)) && (!atype.IsSubclassOf (t)))
169                                         throw new ArgumentException ("Field '" + fi.Name + "' does not belong to the same class as the constructor");
170                                 if (!IsValidType (fi.FieldType))
171                                         throw new ArgumentException ("Field '" + fi.Name + "' does not have a valid type.");
172                                 if (!IsValidValue (fi.FieldType, fieldValues [i]))
173                                         throw new ArgumentException ("Field " + fi.Name + " is not a valid value.");
174                                 // FIXME: Check enums and TypeBuilders as well
175                                 if (fieldValues [i] != null)
176                                         // IsEnum does not seem to work on TypeBuilders
177                                         if (!(fi.FieldType is TypeBuilder) && !fi.FieldType.IsEnum && !fi.FieldType.IsInstanceOfType (fieldValues [i])) {
178                                                 //
179                                                 // mcs allways uses object[] for array types and
180                                                 // MS.NET allows this
181                                                 //
182                                                 if (!fi.FieldType.IsArray)
183                                                         throw new ArgumentException ("Value of field '" + fi.Name + "' does not match field type: " + fi.FieldType);
184                                                 }
185                                 i ++;
186                         }
187
188                         i = 0;
189                         foreach (PropertyInfo pi in namedProperties) {
190                                 if (!pi.CanWrite)
191                                         throw new ArgumentException ("Property '" + pi.Name + "' does not have a setter.");
192                                 Type t = pi.DeclaringType;
193                                 if ((atype != t) && (!t.IsSubclassOf (atype)) && (!atype.IsSubclassOf (t)))
194                                         throw new ArgumentException ("Property '" + pi.Name + "' does not belong to the same class as the constructor");
195                                 if (!IsValidType (pi.PropertyType))
196                                         throw new ArgumentException ("Property '" + pi.Name + "' does not have a valid type.");
197                                 if (!IsValidValue (pi.PropertyType, propertyValues [i]))
198                                         throw new ArgumentException ("Property " + pi.Name + " is not a valid value.");
199                                 if (propertyValues [i] != null) {
200                                         if (!(pi.PropertyType is TypeBuilder) && !pi.PropertyType.IsEnum && !pi.PropertyType.IsInstanceOfType (propertyValues [i]))
201                                                 if (!pi.PropertyType.IsArray)
202                                                         throw new ArgumentException ("Value of property '" + pi.Name + "' does not match property type: " + pi.PropertyType + " -> " + propertyValues [i]);
203                                 }
204                                 i ++;
205                         }
206
207                         i = 0;
208                         foreach (ParameterInfo pi in GetParameters (con)) {
209                                 if (pi != null) {
210                                         Type paramType = pi.ParameterType;
211                                         if (!IsValidType (paramType))
212                                                 throw new ArgumentException ("Parameter " + i + " does not have a valid type.");
213                                         if (!IsValidValue (paramType, constructorArgs [i]))
214                                                 throw new ArgumentException ("Parameter " + i + " is not a valid value.");
215                                         
216                                         if (constructorArgs [i] != null) {
217                                                 if (!(paramType is TypeBuilder) && !paramType.IsEnum && !paramType.IsInstanceOfType (constructorArgs [i]))
218                                                         if (!paramType.IsArray)
219                                                                 throw new ArgumentException ("Value of argument " + i + " does not match parameter type: " + paramType + " -> " + constructorArgs [i]);
220                                                 if (!IsValidParam (constructorArgs [i], paramType))
221                                                         throw new ArgumentException ("Cannot emit a CustomAttribute with argument of type " + constructorArgs [i].GetType () + "."); 
222                                         }
223                                 }
224                                 i ++;
225                         }
226                                 
227                         data = GetBlob (atype.Assembly, con, constructorArgs, namedProperties, propertyValues, namedFields, fieldValues);
228                 }
229
230                 /* helper methods */
231                 internal static int decode_len (byte[] data, int pos, out int rpos) {
232                         int len = 0;
233                         if ((data [pos] & 0x80) == 0) {
234                                 len = (int)(data [pos++] & 0x7f);
235                         } else if ((data [pos] & 0x40) == 0) {
236                                 len = ((data [pos] & 0x3f) << 8) + data [pos + 1];
237                                 pos += 2;
238                         } else {
239                                 len = ((data [pos] & 0x1f) << 24) + (data [pos + 1] << 16) + (data [pos + 2] << 8) + data [pos + 3];
240                                 pos += 4;
241                         }
242                         rpos = pos;
243                         return len;
244                 }
245
246                 internal static string string_from_bytes (byte[] data, int pos, int len) 
247                 {
248                         return System.Text.Encoding.UTF8.GetString(data, pos, len);
249                 }
250
251                 internal string string_arg ()
252                 {
253                         int pos = 2;
254                         int len = decode_len (data, pos, out pos);
255                         return string_from_bytes (data, pos, len);
256                 }                       
257
258                 internal static UnmanagedMarshal get_umarshal (CustomAttributeBuilder customBuilder, bool is_field) {
259                         byte[] data = customBuilder.Data;
260                         UnmanagedType subtype = (UnmanagedType)0x50; /* NATIVE_MAX */
261                         int sizeConst = -1;
262                         int sizeParamIndex = -1;
263                         bool hasSize = false;
264                         int value;
265                         int utype; /* the (stupid) ctor takes a short or an enum ... */
266                         string marshalTypeName = null;
267                         Type marshalTypeRef = null;
268                         string marshalCookie = String.Empty;
269                         utype = (int)data [2];
270                         utype |= ((int)data [3]) << 8;
271
272                         string first_type_name = GetParameters (customBuilder.Ctor) [0].ParameterType.FullName;
273                         int pos = 6;
274                         if (first_type_name == "System.Int16")
275                                 pos = 4;
276                         int nnamed = (int)data [pos++];
277                         nnamed |= ((int)data [pos++]) << 8;
278
279                         for (int i = 0; i < nnamed; ++i) {
280                                 int paramType; // What is this ?
281                                 
282                                 /* Skip field/property signature */
283                                 pos ++;
284                                 /* Read type */
285                                 paramType = ((int)data [pos++]);
286                                 if (paramType == 0x55) {
287                                         /* enums, the value is preceeded by the type */
288                                         int len2 = decode_len (data, pos, out pos);
289                                         string_from_bytes (data, pos, len2);
290                                         pos += len2;
291                                 }
292                                 int len = decode_len (data, pos, out pos);
293                                 string named_name = string_from_bytes (data, pos, len);
294                                 pos += len;
295
296                                 switch (named_name) {
297                                 case "ArraySubType":
298                                         value = (int)data [pos++];
299                                         value |= ((int)data [pos++]) << 8;
300                                         value |= ((int)data [pos++]) << 16;
301                                         value |= ((int)data [pos++]) << 24;
302                                         subtype = (UnmanagedType)value;
303                                         break;
304                                 case "SizeConst":
305                                         value = (int)data [pos++];
306                                         value |= ((int)data [pos++]) << 8;
307                                         value |= ((int)data [pos++]) << 16;
308                                         value |= ((int)data [pos++]) << 24;
309                                         sizeConst = value;
310                                         hasSize = true;
311                                         break;
312                                 case "SafeArraySubType":
313                                         value = (int)data[pos++];
314                                         value |= ((int)data[pos++]) << 8;
315                                         value |= ((int)data[pos++]) << 16;
316                                         value |= ((int)data[pos++]) << 24;
317                                         subtype = (UnmanagedType)value;
318                                         break;
319                                 case "IidParameterIndex":
320                                         pos += 4;
321                                         break;
322                                 case "SafeArrayUserDefinedSubType":
323                                         len = decode_len (data, pos, out pos);
324                                         string_from_bytes (data, pos, len);
325                                         pos += len;
326                                         break;
327                                 case "SizeParamIndex":
328                                         value = (int)data [pos++];
329                                         value |= ((int)data [pos++]) << 8;
330                                         sizeParamIndex = value;
331                                         hasSize = true;
332                                         break;
333                                 case "MarshalType":
334                                         len = decode_len (data, pos, out pos);
335                                         marshalTypeName = string_from_bytes (data, pos, len);
336                                         pos += len;
337                                         break;
338                                 case "MarshalTypeRef":
339                                         len = decode_len (data, pos, out pos);
340                                         marshalTypeName = string_from_bytes (data, pos, len);
341                                         marshalTypeRef = Type.GetType (marshalTypeName);
342                                         pos += len;
343                                         break;
344                                 case "MarshalCookie":
345                                         len = decode_len (data, pos, out pos);
346                                         marshalCookie = string_from_bytes (data, pos, len);
347                                         pos += len;
348                                         break;
349                                 default:
350                                         throw new Exception ("Unknown MarshalAsAttribute field: " + named_name);
351                                 }
352                         }
353
354                         switch ((UnmanagedType)utype) {
355                         case UnmanagedType.LPArray:
356                                 if (hasSize)
357                                         return UnmanagedMarshal.DefineLPArrayInternal (subtype, sizeConst, sizeParamIndex);
358                                 else
359                                         return UnmanagedMarshal.DefineLPArray (subtype);
360                         case UnmanagedType.SafeArray:
361                                 return UnmanagedMarshal.DefineSafeArray (subtype);
362                         case UnmanagedType.ByValArray:
363                                 if (!is_field)
364                                         throw new ArgumentException ("Specified unmanaged type is only valid on fields");
365                         
366                                 return UnmanagedMarshal.DefineByValArray (sizeConst);
367                         case UnmanagedType.ByValTStr:
368                                 return UnmanagedMarshal.DefineByValTStr (sizeConst);
369                         case UnmanagedType.CustomMarshaler:
370                                 return UnmanagedMarshal.DefineCustom (marshalTypeRef, marshalCookie, marshalTypeName, Guid.Empty);
371                         default:
372                                 return UnmanagedMarshal.DefineUnmanagedMarshal ((UnmanagedType)utype);
373                         }
374                 }
375
376                 static Type elementTypeToType (int elementType) {
377                         /* Partition II, section 23.1.16 */
378                         switch (elementType) {
379                         case 0x02:
380                                 return typeof (bool);
381                         case 0x03:
382                                 return typeof (char);
383                         case 0x04:
384                                 return typeof (sbyte);
385                         case 0x05:
386                                 return typeof (byte);
387                         case 0x06:
388                                 return typeof (short);
389                         case 0x07:
390                                 return typeof (ushort);
391                         case 0x08:
392                                 return typeof (int);
393                         case 0x09:
394                                 return typeof (uint);
395                         case 0x0a:
396                                 return typeof (long);
397                         case 0x0b:
398                                 return typeof (ulong);
399                         case 0x0c:
400                                 return typeof (float);
401                         case 0x0d:
402                                 return typeof (double);
403                         case 0x0e:
404                                 return typeof (string);
405                         default:
406                                 throw new Exception ("Unknown element type '" + elementType + "'");
407                         }
408                 }
409
410                 static object decode_cattr_value (Type t, byte[] data, int pos, out int rpos) {
411                         switch (Type.GetTypeCode (t)) {
412                         case TypeCode.String:
413                                 if (data [pos] == 0xff) {
414                                         rpos = pos + 1;
415                                         return null;
416                                 }
417                                 int len = decode_len (data, pos, out pos);
418                                 rpos = pos + len;
419                                 return string_from_bytes (data, pos, len);
420                         case TypeCode.Int32:
421                                 rpos = pos + 4;
422                                 return data [pos] + (data [pos + 1] << 8) + (data [pos + 2] << 16) + (data [pos + 3] << 24);
423                         case TypeCode.Boolean:
424                                 rpos = pos + 1;
425                                 return (data [pos] == 0) ? false : true;
426                         case TypeCode.Object:
427                                 int subtype = data [pos];
428                                 pos += 1;
429
430                                 if (subtype >= 0x02 && subtype <= 0x0e)
431                                         return decode_cattr_value (elementTypeToType (subtype), data, pos, out rpos);
432                                 else
433                                         throw new Exception ("Subtype '" + subtype + "' of type object not yet handled in decode_cattr_value");
434                         default:
435                                 throw new Exception ("FIXME: Type " + t + " not yet handled in decode_cattr_value.");
436                         }
437                 }
438
439                 internal struct CustomAttributeInfo {
440                         public ConstructorInfo ctor;
441                         public object[] ctorArgs;
442                         public string[] namedParamNames;
443                         public object[] namedParamValues;
444                 }
445
446                 internal static CustomAttributeInfo decode_cattr (CustomAttributeBuilder customBuilder) {
447                         byte[] data = customBuilder.Data;
448                         ConstructorInfo ctor = customBuilder.Ctor;
449                         int pos = 0;
450
451                         CustomAttributeInfo info = new CustomAttributeInfo ();
452
453                         // Prolog
454                         if (data.Length < 2)
455                                 throw new Exception ("Custom attr length is only '" + data.Length + "'");
456                         if ((data [0] != 0x1) || (data [1] != 0x00))
457                                 throw new Exception ("Prolog invalid");
458                         pos = 2;
459
460                         ParameterInfo [] pi = GetParameters (ctor);
461                         info.ctor = ctor;
462                         info.ctorArgs = new object [pi.Length];
463                         for (int i = 0; i < pi.Length; ++i)
464                                 info.ctorArgs [i] = decode_cattr_value (pi [i].ParameterType, data, pos, out pos);
465
466                         int num_named = data [pos] + (data [pos + 1] * 256);
467                         pos += 2;
468
469                         info.namedParamNames = new string [num_named];
470                         info.namedParamValues = new object [num_named];
471                         for (int i = 0; i < num_named; ++i) {
472                                 int named_type = data [pos++];
473                                 int data_type = data [pos++];
474                                 string enum_type_name = null;
475
476                                 if (data_type == 0x55) {
477                                         int len2 = decode_len (data, pos, out pos);
478                                         enum_type_name = string_from_bytes (data, pos, len2);
479                                         pos += len2;
480                                 }
481
482                                 int len = decode_len (data, pos, out pos);
483                                 string name = string_from_bytes (data, pos, len);
484                                 info.namedParamNames [i] = name;
485                                 pos += len;
486
487                                 if (named_type == 0x53) {
488                                         /* Field */
489                                         FieldInfo fi = ctor.DeclaringType.GetField (name, BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance);
490                                         if (fi == null)
491                                                 throw new Exception ("Custom attribute type '" + ctor.DeclaringType + "' doesn't contain a field named '" + name + "'");
492
493                                         object val = decode_cattr_value (fi.FieldType, data, pos, out pos);
494                                         if (enum_type_name != null) {
495                                                 Type enumType = Type.GetType (enum_type_name);
496                                                 val = Enum.ToObject (enumType, val);
497                                         }
498
499                                         info.namedParamValues [i] = val;
500                                 }
501                                 else
502                                         // FIXME:
503                                         throw new Exception ("Unknown named type: " + named_type);
504                         }
505
506                         return info;
507                 }
508
509                 void _CustomAttributeBuilder.GetIDsOfNames ([In] ref Guid riid, IntPtr rgszNames, uint cNames, uint lcid, IntPtr rgDispId)
510                 {
511                         throw new NotImplementedException ();
512                 }
513
514                 void _CustomAttributeBuilder.GetTypeInfo (uint iTInfo, uint lcid, IntPtr ppTInfo)
515                 {
516                         throw new NotImplementedException ();
517                 }
518
519                 void _CustomAttributeBuilder.GetTypeInfoCount (out uint pcTInfo)
520                 {
521                         throw new NotImplementedException ();
522                 }
523
524                 void _CustomAttributeBuilder.Invoke (uint dispIdMember, [In] ref Guid riid, uint lcid, short wFlags, IntPtr pDispParams, IntPtr pVarResult, IntPtr pExcepInfo, IntPtr puArgErr)
525                 {
526                         throw new NotImplementedException ();
527                 }
528
529                 static ParameterInfo [] GetParameters (ConstructorInfo ctor)
530                 {
531                         ConstructorBuilder cb = ctor as ConstructorBuilder;
532                         if (cb != null)
533                                 return cb.GetParametersInternal ();
534                         return ctor.GetParameters ();
535                 }
536         }
537 }