You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

234 lines
6.8 KiB

  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System;
  5. using System.Collections.Immutable;
  6. using System.Reflection.Metadata;
  7. namespace ICSharpCode.Decompiler.Metadata
  8. {
  9. /// <summary>
  10. /// Decodes custom attribute blobs.
  11. /// </summary>
  12. internal readonly struct CustomAttributeDecoder<TType>
  13. {
  14. // This is a stripped-down copy of SRM's internal CustomAttributeDecoder.
  15. // We need it to decode security declarations.
  16. private readonly ICustomAttributeTypeProvider<TType> _provider;
  17. private readonly MetadataReader _reader;
  18. private readonly bool _provideBoxingTypeInfo;
  19. public CustomAttributeDecoder(ICustomAttributeTypeProvider<TType> provider, MetadataReader reader, bool provideBoxingTypeInfo = false)
  20. {
  21. _reader = reader;
  22. _provider = provider;
  23. _provideBoxingTypeInfo = provideBoxingTypeInfo;
  24. }
  25. public ImmutableArray<CustomAttributeNamedArgument<TType>> DecodeNamedArguments(ref BlobReader valueReader, int count)
  26. {
  27. var arguments = ImmutableArray.CreateBuilder<CustomAttributeNamedArgument<TType>>(count);
  28. for (int i = 0; i < count; i++)
  29. {
  30. CustomAttributeNamedArgumentKind kind = (CustomAttributeNamedArgumentKind)valueReader.ReadSerializationTypeCode();
  31. if (kind != CustomAttributeNamedArgumentKind.Field && kind != CustomAttributeNamedArgumentKind.Property)
  32. {
  33. throw new BadImageFormatException();
  34. }
  35. ArgumentTypeInfo info = DecodeNamedArgumentType(ref valueReader);
  36. string name = valueReader.ReadSerializedString();
  37. CustomAttributeTypedArgument<TType> argument = DecodeArgument(ref valueReader, info);
  38. arguments.Add(new CustomAttributeNamedArgument<TType>(name, kind, argument.Type, argument.Value));
  39. }
  40. return arguments.MoveToImmutable();
  41. }
  42. private struct ArgumentTypeInfo
  43. {
  44. public TType Type;
  45. public TType ElementType;
  46. public SerializationTypeCode TypeCode;
  47. public SerializationTypeCode ElementTypeCode;
  48. }
  49. private ArgumentTypeInfo DecodeNamedArgumentType(ref BlobReader valueReader, bool isElementType = false)
  50. {
  51. var info = new ArgumentTypeInfo {
  52. TypeCode = valueReader.ReadSerializationTypeCode(),
  53. };
  54. switch (info.TypeCode)
  55. {
  56. case SerializationTypeCode.Boolean:
  57. case SerializationTypeCode.Byte:
  58. case SerializationTypeCode.Char:
  59. case SerializationTypeCode.Double:
  60. case SerializationTypeCode.Int16:
  61. case SerializationTypeCode.Int32:
  62. case SerializationTypeCode.Int64:
  63. case SerializationTypeCode.SByte:
  64. case SerializationTypeCode.Single:
  65. case SerializationTypeCode.String:
  66. case SerializationTypeCode.UInt16:
  67. case SerializationTypeCode.UInt32:
  68. case SerializationTypeCode.UInt64:
  69. info.Type = _provider.GetPrimitiveType((PrimitiveTypeCode)info.TypeCode);
  70. break;
  71. case SerializationTypeCode.Type:
  72. info.Type = _provider.GetSystemType();
  73. break;
  74. case SerializationTypeCode.TaggedObject:
  75. info.Type = _provider.GetPrimitiveType(PrimitiveTypeCode.Object);
  76. break;
  77. case SerializationTypeCode.SZArray:
  78. if (isElementType)
  79. {
  80. // jagged arrays are not allowed.
  81. throw new BadImageFormatException();
  82. }
  83. var elementInfo = DecodeNamedArgumentType(ref valueReader, isElementType: true);
  84. info.ElementType = elementInfo.Type;
  85. info.ElementTypeCode = elementInfo.TypeCode;
  86. info.Type = _provider.GetSZArrayType(info.ElementType);
  87. break;
  88. case SerializationTypeCode.Enum:
  89. string typeName = valueReader.ReadSerializedString();
  90. info.Type = _provider.GetTypeFromSerializedName(typeName);
  91. info.TypeCode = (SerializationTypeCode)_provider.GetUnderlyingEnumType(info.Type);
  92. break;
  93. default:
  94. throw new BadImageFormatException();
  95. }
  96. return info;
  97. }
  98. private CustomAttributeTypedArgument<TType> DecodeArgument(ref BlobReader valueReader, ArgumentTypeInfo info)
  99. {
  100. var outer = info;
  101. if (info.TypeCode == SerializationTypeCode.TaggedObject)
  102. {
  103. info = DecodeNamedArgumentType(ref valueReader);
  104. }
  105. // PERF_TODO: https://github.com/dotnet/corefx/issues/6533
  106. // Cache /reuse common arguments to avoid boxing (small integers, true, false).
  107. object value;
  108. switch (info.TypeCode)
  109. {
  110. case SerializationTypeCode.Boolean:
  111. value = valueReader.ReadBoolean();
  112. break;
  113. case SerializationTypeCode.Byte:
  114. value = valueReader.ReadByte();
  115. break;
  116. case SerializationTypeCode.Char:
  117. value = valueReader.ReadChar();
  118. break;
  119. case SerializationTypeCode.Double:
  120. value = valueReader.ReadDouble();
  121. break;
  122. case SerializationTypeCode.Int16:
  123. value = valueReader.ReadInt16();
  124. break;
  125. case SerializationTypeCode.Int32:
  126. value = valueReader.ReadInt32();
  127. break;
  128. case SerializationTypeCode.Int64:
  129. value = valueReader.ReadInt64();
  130. break;
  131. case SerializationTypeCode.SByte:
  132. value = valueReader.ReadSByte();
  133. break;
  134. case SerializationTypeCode.Single:
  135. value = valueReader.ReadSingle();
  136. break;
  137. case SerializationTypeCode.UInt16:
  138. value = valueReader.ReadUInt16();
  139. break;
  140. case SerializationTypeCode.UInt32:
  141. value = valueReader.ReadUInt32();
  142. break;
  143. case SerializationTypeCode.UInt64:
  144. value = valueReader.ReadUInt64();
  145. break;
  146. case SerializationTypeCode.String:
  147. value = valueReader.ReadSerializedString();
  148. break;
  149. case SerializationTypeCode.Type:
  150. string typeName = valueReader.ReadSerializedString();
  151. value = _provider.GetTypeFromSerializedName(typeName);
  152. break;
  153. case SerializationTypeCode.SZArray:
  154. value = DecodeArrayArgument(ref valueReader, info);
  155. break;
  156. default:
  157. throw new BadImageFormatException();
  158. }
  159. if (_provideBoxingTypeInfo && outer.TypeCode == SerializationTypeCode.TaggedObject)
  160. {
  161. return new CustomAttributeTypedArgument<TType>(outer.Type, new CustomAttributeTypedArgument<TType>(info.Type, value));
  162. }
  163. return new CustomAttributeTypedArgument<TType>(info.Type, value);
  164. }
  165. private ImmutableArray<CustomAttributeTypedArgument<TType>>? DecodeArrayArgument(ref BlobReader blobReader, ArgumentTypeInfo info)
  166. {
  167. int count = blobReader.ReadInt32();
  168. if (count == -1)
  169. {
  170. return null;
  171. }
  172. if (count == 0)
  173. {
  174. return ImmutableArray<CustomAttributeTypedArgument<TType>>.Empty;
  175. }
  176. if (count < 0)
  177. {
  178. throw new BadImageFormatException();
  179. }
  180. var elementInfo = new ArgumentTypeInfo {
  181. Type = info.ElementType,
  182. TypeCode = info.ElementTypeCode,
  183. };
  184. var array = ImmutableArray.CreateBuilder<CustomAttributeTypedArgument<TType>>(count);
  185. for (int i = 0; i < count; i++)
  186. {
  187. array.Add(DecodeArgument(ref blobReader, elementInfo));
  188. }
  189. return array.MoveToImmutable();
  190. }
  191. }
  192. }