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