1 /* 2 Copyright (C) 2008-2013 Jeroen Frijters 3 4 This software is provided 'as-is', without any express or implied 5 warranty. In no event will the authors be held liable for any damages 6 arising from the use of this software. 7 8 Permission is granted to anyone to use this software for any purpose, 9 including commercial applications, and to alter it and redistribute it 10 freely, subject to the following restrictions: 11 12 1. The origin of this software must not be misrepresented; you must not 13 claim that you wrote the original software. If you use this software 14 in a product, an acknowledgment in the product documentation would be 15 appreciated but is not required. 16 2. Altered source versions must be plainly marked as such, and must not be 17 misrepresented as being the original software. 18 3. This notice may not be removed or altered from any source distribution. 19 20 Jeroen Frijters 21 jeroen@frijters.net 22 23 */ 24 using System; 25 using System.IO; 26 using System.Collections.Generic; 27 using System.Diagnostics; 28 using System.Text; 29 using IKVM.Reflection.Writer; 30 31 namespace IKVM.Reflection.Emit 32 { 33 public sealed class CustomAttributeBuilder 34 { 35 internal static readonly ConstructorInfo LegacyPermissionSet = new ConstructorBuilder(null); 36 private readonly ConstructorInfo con; 37 private readonly byte[] blob; 38 private readonly object[] constructorArgs; 39 private readonly PropertyInfo[] namedProperties; 40 private readonly object[] propertyValues; 41 private readonly FieldInfo[] namedFields; 42 private readonly object[] fieldValues; 43 CustomAttributeBuilder(ConstructorInfo con, byte[] blob)44 internal CustomAttributeBuilder(ConstructorInfo con, byte[] blob) 45 { 46 this.con = con; 47 this.blob = blob; 48 } 49 CustomAttributeBuilder(ConstructorInfo con, int securityAction, byte[] blob)50 private CustomAttributeBuilder(ConstructorInfo con, int securityAction, byte[] blob) 51 { 52 this.con = con; 53 this.blob = blob; 54 this.constructorArgs = new object[] { securityAction }; 55 } 56 CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs)57 public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs) 58 : this(con, constructorArgs, null, null, null,null) 59 { 60 } 61 CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, FieldInfo[] namedFields, object[] fieldValues)62 public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, FieldInfo[] namedFields, object[] fieldValues) 63 : this(con, constructorArgs, null, null, namedFields, fieldValues) 64 { 65 } 66 CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues)67 public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues) 68 : this(con, constructorArgs, namedProperties, propertyValues, null, null) 69 { 70 } 71 CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues, FieldInfo[] namedFields, object[] fieldValues)72 public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues, FieldInfo[] namedFields, object[] fieldValues) 73 { 74 this.con = con; 75 this.constructorArgs = constructorArgs; 76 this.namedProperties = namedProperties; 77 this.propertyValues = propertyValues; 78 this.namedFields = namedFields; 79 this.fieldValues = fieldValues; 80 } 81 __FromBlob(ConstructorInfo con, byte[] blob)82 public static CustomAttributeBuilder __FromBlob(ConstructorInfo con, byte[] blob) 83 { 84 return new CustomAttributeBuilder(con, blob); 85 } 86 __FromBlob(ConstructorInfo con, int securityAction, byte[] blob)87 public static CustomAttributeBuilder __FromBlob(ConstructorInfo con, int securityAction, byte[] blob) 88 { 89 return new CustomAttributeBuilder(con, securityAction, blob); 90 } 91 __MakeTypedArgument(Type type, object value)92 public static CustomAttributeTypedArgument __MakeTypedArgument(Type type, object value) 93 { 94 return new CustomAttributeTypedArgument(type, value); 95 } 96 97 private sealed class BlobWriter 98 { 99 private readonly Assembly assembly; 100 private readonly CustomAttributeBuilder cab; 101 private readonly ByteBuffer bb; 102 BlobWriter(Assembly assembly, CustomAttributeBuilder cab, ByteBuffer bb)103 internal BlobWriter(Assembly assembly, CustomAttributeBuilder cab, ByteBuffer bb) 104 { 105 this.assembly = assembly; 106 this.cab = cab; 107 this.bb = bb; 108 } 109 WriteCustomAttributeBlob()110 internal void WriteCustomAttributeBlob() 111 { 112 // prolog 113 WriteUInt16(1); 114 ParameterInfo[] pi = cab.con.GetParameters(); 115 for (int i = 0; i < pi.Length; i++) 116 { 117 WriteFixedArg(pi[i].ParameterType, cab.constructorArgs[i]); 118 } 119 WriteNamedArguments(false); 120 } 121 WriteNamedArguments(bool forDeclSecurity)122 internal void WriteNamedArguments(bool forDeclSecurity) 123 { 124 // NumNamed 125 int named = 0; 126 if (cab.namedFields != null) 127 { 128 named += cab.namedFields.Length; 129 } 130 if (cab.namedProperties != null) 131 { 132 named += cab.namedProperties.Length; 133 } 134 if (forDeclSecurity) 135 { 136 WritePackedLen(named); 137 } 138 else 139 { 140 WriteUInt16((ushort)named); 141 } 142 if (cab.namedFields != null) 143 { 144 for (int i = 0; i < cab.namedFields.Length; i++) 145 { 146 WriteNamedArg(0x53, cab.namedFields[i].FieldType, cab.namedFields[i].Name, cab.fieldValues[i]); 147 } 148 } 149 if (cab.namedProperties != null) 150 { 151 for (int i = 0; i < cab.namedProperties.Length; i++) 152 { 153 WriteNamedArg(0x54, cab.namedProperties[i].PropertyType, cab.namedProperties[i].Name, cab.propertyValues[i]); 154 } 155 } 156 } 157 WriteNamedArg(byte fieldOrProperty, Type type, string name, object value)158 private void WriteNamedArg(byte fieldOrProperty, Type type, string name, object value) 159 { 160 WriteByte(fieldOrProperty); 161 WriteFieldOrPropType(type); 162 WriteString(name); 163 WriteFixedArg(type, value); 164 } 165 WriteByte(byte value)166 private void WriteByte(byte value) 167 { 168 bb.Write(value); 169 } 170 WriteUInt16(ushort value)171 private void WriteUInt16(ushort value) 172 { 173 bb.Write(value); 174 } 175 WriteInt32(int value)176 private void WriteInt32(int value) 177 { 178 bb.Write(value); 179 } 180 WriteFixedArg(Type type, object value)181 private void WriteFixedArg(Type type, object value) 182 { 183 Universe u = assembly.universe; 184 if (type == u.System_String) 185 { 186 WriteString((string)value); 187 } 188 else if (type == u.System_Boolean) 189 { 190 WriteByte((bool)value ? (byte)1 : (byte)0); 191 } 192 else if (type == u.System_Char) 193 { 194 WriteUInt16((char)value); 195 } 196 else if (type == u.System_SByte) 197 { 198 WriteByte((byte)(sbyte)value); 199 } 200 else if (type == u.System_Byte) 201 { 202 WriteByte((byte)value); 203 } 204 else if (type == u.System_Int16) 205 { 206 WriteUInt16((ushort)(short)value); 207 } 208 else if (type == u.System_UInt16) 209 { 210 WriteUInt16((ushort)value); 211 } 212 else if (type == u.System_Int32) 213 { 214 WriteInt32((int)value); 215 } 216 else if (type == u.System_UInt32) 217 { 218 WriteInt32((int)(uint)value); 219 } 220 else if (type == u.System_Int64) 221 { 222 WriteInt64((long)value); 223 } 224 else if (type == u.System_UInt64) 225 { 226 WriteInt64((long)(ulong)value); 227 } 228 else if (type == u.System_Single) 229 { 230 WriteSingle((float)value); 231 } 232 else if (type == u.System_Double) 233 { 234 WriteDouble((double)value); 235 } 236 else if (type == u.System_Type) 237 { 238 WriteTypeName((Type)value); 239 } 240 else if (type == u.System_Object) 241 { 242 if (value == null) 243 { 244 type = u.System_String; 245 } 246 else if (value is Type) 247 { 248 // value.GetType() would return a subclass of Type, but we don't want to deal with that 249 type = u.System_Type; 250 } 251 else if (value is CustomAttributeTypedArgument) 252 { 253 CustomAttributeTypedArgument cta = (CustomAttributeTypedArgument)value; 254 value = cta.Value; 255 type = cta.ArgumentType; 256 } 257 else 258 { 259 type = u.Import(value.GetType()); 260 } 261 WriteFieldOrPropType(type); 262 WriteFixedArg(type, value); 263 } 264 else if (type.IsArray) 265 { 266 if (value == null) 267 { 268 WriteInt32(-1); 269 } 270 else 271 { 272 Array array = (Array)value; 273 Type elemType = type.GetElementType(); 274 WriteInt32(array.Length); 275 foreach (object val in array) 276 { 277 WriteFixedArg(elemType, val); 278 } 279 } 280 } 281 else if (type.IsEnum) 282 { 283 WriteFixedArg(type.GetEnumUnderlyingTypeImpl(), value); 284 } 285 else 286 { 287 throw new ArgumentException(); 288 } 289 } 290 WriteInt64(long value)291 private void WriteInt64(long value) 292 { 293 bb.Write(value); 294 } 295 WriteSingle(float value)296 private void WriteSingle(float value) 297 { 298 bb.Write(value); 299 } 300 WriteDouble(double value)301 private void WriteDouble(double value) 302 { 303 bb.Write(value); 304 } 305 WriteTypeName(Type type)306 private void WriteTypeName(Type type) 307 { 308 string name = null; 309 if (type != null) 310 { 311 StringBuilder sb = new StringBuilder(); 312 GetTypeName(sb, type, false); 313 name = sb.ToString(); 314 } 315 WriteString(name); 316 } 317 GetTypeName(StringBuilder sb, Type type, bool isTypeParam)318 private void GetTypeName(StringBuilder sb, Type type, bool isTypeParam) 319 { 320 bool v1 = !assembly.ManifestModule.__IsMissing && assembly.ManifestModule.MDStreamVersion < 0x20000; 321 bool includeAssemblyName = type.Assembly != assembly && (!v1 || type.Assembly != type.Module.universe.Mscorlib); 322 if (isTypeParam && includeAssemblyName) 323 { 324 sb.Append('['); 325 } 326 GetTypeNameImpl(sb, type); 327 if (includeAssemblyName) 328 { 329 if (v1) 330 { 331 sb.Append(','); 332 } 333 else 334 { 335 sb.Append(", "); 336 } 337 if (isTypeParam) 338 { 339 sb.Append(type.Assembly.FullName.Replace("]", "\\]")).Append(']'); 340 } 341 else 342 { 343 sb.Append(type.Assembly.FullName); 344 } 345 } 346 } 347 GetTypeNameImpl(StringBuilder sb, Type type)348 private void GetTypeNameImpl(StringBuilder sb, Type type) 349 { 350 if (type.HasElementType) 351 { 352 GetTypeNameImpl(sb, type.GetElementType()); 353 sb.Append(((ElementHolderType)type).GetSuffix()); 354 } 355 else if (type.IsConstructedGenericType) 356 { 357 sb.Append(type.GetGenericTypeDefinition().FullName); 358 sb.Append('['); 359 string sep = ""; 360 foreach (Type typeParam in type.GetGenericArguments()) 361 { 362 sb.Append(sep); 363 GetTypeName(sb, typeParam, true); 364 sep = ","; 365 } 366 sb.Append(']'); 367 } 368 else 369 { 370 sb.Append(type.FullName); 371 } 372 } 373 WriteString(string val)374 private void WriteString(string val) 375 { 376 bb.Write(val); 377 } 378 WritePackedLen(int len)379 private void WritePackedLen(int len) 380 { 381 bb.WriteCompressedUInt(len); 382 } 383 WriteFieldOrPropType(Type type)384 private void WriteFieldOrPropType(Type type) 385 { 386 Universe u = type.Module.universe; 387 if (type == u.System_Type) 388 { 389 WriteByte(0x50); 390 } 391 else if (type == u.System_Object) 392 { 393 WriteByte(0x51); 394 } 395 else if (type == u.System_Boolean) 396 { 397 WriteByte(0x02); 398 } 399 else if (type == u.System_Char) 400 { 401 WriteByte(0x03); 402 } 403 else if (type == u.System_SByte) 404 { 405 WriteByte(0x04); 406 } 407 else if (type == u.System_Byte) 408 { 409 WriteByte(0x05); 410 } 411 else if (type == u.System_Int16) 412 { 413 WriteByte(0x06); 414 } 415 else if (type == u.System_UInt16) 416 { 417 WriteByte(0x07); 418 } 419 else if (type == u.System_Int32) 420 { 421 WriteByte(0x08); 422 } 423 else if (type == u.System_UInt32) 424 { 425 WriteByte(0x09); 426 } 427 else if (type == u.System_Int64) 428 { 429 WriteByte(0x0A); 430 } 431 else if (type == u.System_UInt64) 432 { 433 WriteByte(0x0B); 434 } 435 else if (type == u.System_Single) 436 { 437 WriteByte(0x0C); 438 } 439 else if (type == u.System_Double) 440 { 441 WriteByte(0x0D); 442 } 443 else if (type == u.System_String) 444 { 445 WriteByte(0x0E); 446 } 447 else if (type.IsArray) 448 { 449 WriteByte(0x1D); 450 WriteFieldOrPropType(type.GetElementType()); 451 } 452 else if (type.IsEnum) 453 { 454 WriteByte(0x55); 455 WriteTypeName(type); 456 } 457 else 458 { 459 throw new ArgumentException(); 460 } 461 } 462 } 463 464 internal ConstructorInfo Constructor 465 { 466 get { return con; } 467 } 468 WriteBlob(ModuleBuilder moduleBuilder)469 internal int WriteBlob(ModuleBuilder moduleBuilder) 470 { 471 ByteBuffer bb; 472 if (blob != null) 473 { 474 bb = ByteBuffer.Wrap(blob); 475 } 476 else 477 { 478 bb = new ByteBuffer(100); 479 BlobWriter bw = new BlobWriter(moduleBuilder.Assembly, this, bb); 480 bw.WriteCustomAttributeBlob(); 481 } 482 return moduleBuilder.Blobs.Add(bb); 483 } 484 GetConstructorArgument(int pos)485 internal object GetConstructorArgument(int pos) 486 { 487 return constructorArgs[pos]; 488 } 489 490 internal int ConstructorArgumentCount 491 { 492 get { return constructorArgs == null ? 0 : constructorArgs.Length; } 493 } 494 495 internal T? GetFieldValue<T>(string name) where T : struct 496 { 497 object val = GetFieldValue(name); 498 if (val is T) 499 { 500 return (T)val; 501 } 502 else if (val != null) 503 { 504 if (typeof(T).IsEnum) 505 { 506 Debug.Assert(Enum.GetUnderlyingType(typeof(T)) == val.GetType()); 507 return (T)Enum.ToObject(typeof(T), val); 508 } 509 else 510 { 511 Debug.Assert(Enum.GetUnderlyingType(val.GetType()) == typeof(T)); 512 return (T)Convert.ChangeType(val, typeof(T)); 513 } 514 } 515 else 516 { 517 return null; 518 } 519 } 520 GetFieldValue(string name)521 internal object GetFieldValue(string name) 522 { 523 if (namedFields != null) 524 { 525 for (int i = 0; i < namedFields.Length; i++) 526 { 527 if (namedFields[i].Name == name) 528 { 529 return fieldValues[i]; 530 } 531 } 532 } 533 return null; 534 } 535 536 internal bool IsLegacyDeclSecurity 537 { 538 get 539 { 540 return ReferenceEquals(con, LegacyPermissionSet) 541 || (con.DeclaringType == con.Module.universe.System_Security_Permissions_PermissionSetAttribute 542 && blob == null 543 && (namedFields == null || namedFields.Length == 0) 544 && namedProperties != null 545 && namedProperties.Length == 1 546 && namedProperties[0].Name == "XML" 547 && propertyValues[0] is string); 548 } 549 } 550 WriteLegacyDeclSecurityBlob(ModuleBuilder moduleBuilder)551 internal int WriteLegacyDeclSecurityBlob(ModuleBuilder moduleBuilder) 552 { 553 if (blob != null) 554 { 555 return moduleBuilder.Blobs.Add(ByteBuffer.Wrap(blob)); 556 } 557 else 558 { 559 return moduleBuilder.Blobs.Add(ByteBuffer.Wrap(Encoding.Unicode.GetBytes((string)propertyValues[0]))); 560 } 561 } 562 WriteNamedArgumentsForDeclSecurity(ModuleBuilder moduleBuilder, ByteBuffer bb)563 internal void WriteNamedArgumentsForDeclSecurity(ModuleBuilder moduleBuilder, ByteBuffer bb) 564 { 565 if (blob != null) 566 { 567 bb.Write(blob); 568 } 569 else 570 { 571 BlobWriter bw = new BlobWriter(moduleBuilder.Assembly, this, bb); 572 bw.WriteNamedArguments(true); 573 } 574 } 575 ToData(Assembly asm)576 internal CustomAttributeData ToData(Assembly asm) 577 { 578 if (blob != null) 579 { 580 if (constructorArgs != null) 581 { 582 return new CustomAttributeData(asm, con, (int)constructorArgs[0], blob, -1); 583 } 584 return new CustomAttributeData(asm, con, new IKVM.Reflection.Reader.ByteReader(blob, 0, blob.Length)); 585 } 586 else 587 { 588 List<CustomAttributeNamedArgument> namedArgs = new List<CustomAttributeNamedArgument>(); 589 if (namedProperties != null) 590 { 591 for (int i = 0; i < namedProperties.Length; i++) 592 { 593 namedArgs.Add(new CustomAttributeNamedArgument(namedProperties[i], RewrapValue(namedProperties[i].PropertyType, propertyValues[i]))); 594 } 595 } 596 if (namedFields != null) 597 { 598 for (int i = 0; i < namedFields.Length; i++) 599 { 600 namedArgs.Add(new CustomAttributeNamedArgument(namedFields[i], RewrapValue(namedFields[i].FieldType, fieldValues[i]))); 601 } 602 } 603 List<CustomAttributeTypedArgument> args = new List<CustomAttributeTypedArgument>(constructorArgs.Length); 604 ParameterInfo[] parameters = this.Constructor.GetParameters(); 605 for (int i = 0; i < constructorArgs.Length; i++) 606 { 607 args.Add(RewrapValue(parameters[i].ParameterType, constructorArgs[i])); 608 } 609 return new CustomAttributeData(asm.ManifestModule, con, args, namedArgs); 610 } 611 } 612 RewrapValue(Type type, object value)613 private static CustomAttributeTypedArgument RewrapValue(Type type, object value) 614 { 615 if (value is Array) 616 { 617 Array array = (Array)value; 618 Type arrayType = type.Module.universe.Import(array.GetType()); 619 return RewrapArray(arrayType, array); 620 } 621 else if (value is CustomAttributeTypedArgument) 622 { 623 CustomAttributeTypedArgument arg = (CustomAttributeTypedArgument)value; 624 if (arg.Value is Array) 625 { 626 return RewrapArray(arg.ArgumentType, (Array)arg.Value); 627 } 628 return arg; 629 } 630 else 631 { 632 return new CustomAttributeTypedArgument(type, value); 633 } 634 } 635 RewrapArray(Type arrayType, Array array)636 private static CustomAttributeTypedArgument RewrapArray(Type arrayType, Array array) 637 { 638 Type elementType = arrayType.GetElementType(); 639 CustomAttributeTypedArgument[] newArray = new CustomAttributeTypedArgument[array.Length]; 640 for (int i = 0; i < newArray.Length; i++) 641 { 642 newArray[i] = RewrapValue(elementType, array.GetValue(i)); 643 } 644 return new CustomAttributeTypedArgument(arrayType, newArray); 645 } 646 647 internal bool HasBlob 648 { 649 get { return blob != null; } 650 } 651 DecodeBlob(Assembly asm)652 internal CustomAttributeBuilder DecodeBlob(Assembly asm) 653 { 654 if (blob == null) 655 { 656 return this; 657 } 658 else 659 { 660 return ToData(asm).__ToBuilder(); 661 } 662 } 663 GetBlob(Assembly asm)664 internal byte[] GetBlob(Assembly asm) 665 { 666 ByteBuffer bb = new ByteBuffer(100); 667 BlobWriter bw = new BlobWriter(asm, this, bb); 668 bw.WriteCustomAttributeBlob(); 669 return bb.ToArray(); 670 } 671 672 internal KnownCA KnownCA 673 { 674 get 675 { 676 TypeName typeName = con.DeclaringType.TypeName; 677 switch (typeName.Namespace) 678 { 679 case "System": 680 switch (typeName.Name) 681 { 682 case "SerializableAttribute": 683 return KnownCA.SerializableAttribute; 684 case "NonSerializedAttribute": 685 return KnownCA.NonSerializedAttribute; 686 } 687 break; 688 case "System.Runtime.CompilerServices": 689 switch (typeName.Name) 690 { 691 case "MethodImplAttribute": 692 return KnownCA.MethodImplAttribute; 693 case "SpecialNameAttribute": 694 return KnownCA.SpecialNameAttribute; 695 } 696 break; 697 case "System.Runtime.InteropServices": 698 switch (typeName.Name) 699 { 700 case "DllImportAttribute": 701 return KnownCA.DllImportAttribute; 702 case "ComImportAttribute": 703 return KnownCA.ComImportAttribute; 704 case "MarshalAsAttribute": 705 return KnownCA.MarshalAsAttribute; 706 case "PreserveSigAttribute": 707 return KnownCA.PreserveSigAttribute; 708 case "InAttribute": 709 return KnownCA.InAttribute; 710 case "OutAttribute": 711 return KnownCA.OutAttribute; 712 case "OptionalAttribute": 713 return KnownCA.OptionalAttribute; 714 case "StructLayoutAttribute": 715 return KnownCA.StructLayoutAttribute; 716 case "FieldOffsetAttribute": 717 return KnownCA.FieldOffsetAttribute; 718 } 719 break; 720 } 721 if (typeName.Matches("System.Security.SuppressUnmanagedCodeSecurityAttribute")) 722 { 723 return KnownCA.SuppressUnmanagedCodeSecurityAttribute; 724 } 725 return KnownCA.Unknown; 726 } 727 } 728 } 729 730 // These are the pseudo-custom attributes that are recognized by name by the runtime (i.e. the type identity is not considered). 731 // The corresponding list in the runtime is at https://github.com/dotnet/coreclr/blob/1afe5ce4f45045d724a4e129df4b816655d486fb/src/md/compiler/custattr_emit.cpp#L38 732 // Note that we only need to handle a subset of the types, since we don't need the ones that are only used for validation by the runtime. 733 enum KnownCA 734 { 735 Unknown, 736 DllImportAttribute, 737 ComImportAttribute, 738 SerializableAttribute, 739 NonSerializedAttribute, 740 MethodImplAttribute, 741 MarshalAsAttribute, 742 PreserveSigAttribute, 743 InAttribute, 744 OutAttribute, 745 OptionalAttribute, 746 StructLayoutAttribute, 747 FieldOffsetAttribute, 748 SpecialNameAttribute, 749 // the following is not part of the runtime known custom attributes, but we handle it here for efficiency and convenience 750 SuppressUnmanagedCodeSecurityAttribute, 751 } 752 } 753