| | | 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 | | |
| | | 4 | | using System.Collections.Generic; |
| | | 5 | | using System.Collections.Immutable; |
| | | 6 | | using System.Diagnostics; |
| | | 7 | | using System.Reflection.Internal; |
| | | 8 | | using System.Reflection.Metadata.Ecma335; |
| | | 9 | | using System.Runtime.InteropServices; |
| | | 10 | | using System.Text; |
| | | 11 | | |
| | | 12 | | namespace System.Reflection.Metadata |
| | | 13 | | { |
| | | 14 | | /// <summary> |
| | | 15 | | /// Reads metadata as defined byte the ECMA 335 CLI specification. |
| | | 16 | | /// </summary> |
| | | 17 | | public sealed partial class MetadataReader |
| | | 18 | | { |
| | | 19 | | internal readonly NamespaceCache NamespaceCache; |
| | | 20 | | internal readonly MemoryBlock Block; |
| | | 21 | | |
| | | 22 | | // A row id of "mscorlib" AssemblyRef in a WinMD file (each WinMD file must have such a reference). |
| | | 23 | | internal readonly int WinMDMscorlibRef; |
| | | 24 | | |
| | | 25 | | // Keeps the underlying memory alive. |
| | | 26 | | private readonly object? _memoryOwnerObj; |
| | | 27 | | |
| | | 28 | | private readonly MetadataReaderOptions _options; |
| | | 29 | | private Dictionary<TypeDefinitionHandle, ImmutableArray<TypeDefinitionHandle>>? _lazyNestedTypesMap; |
| | | 30 | | |
| | | 31 | | #region Constructors |
| | | 32 | | |
| | | 33 | | /// <summary> |
| | | 34 | | /// Creates a metadata reader from the metadata stored at the given memory location. |
| | | 35 | | /// </summary> |
| | | 36 | | /// <remarks> |
| | | 37 | | /// The memory is owned by the caller and it must be kept memory alive and unmodified throughout the lifetime of |
| | | 38 | | /// </remarks> |
| | | 39 | | public unsafe MetadataReader(byte* metadata, int length) |
| | 0 | 40 | | : this(metadata, length, MetadataReaderOptions.Default, utf8Decoder: null, memoryOwner: null) |
| | 0 | 41 | | { |
| | 0 | 42 | | } |
| | | 43 | | |
| | | 44 | | /// <summary> |
| | | 45 | | /// Creates a metadata reader from the metadata stored at the given memory location. |
| | | 46 | | /// </summary> |
| | | 47 | | /// <remarks> |
| | | 48 | | /// The memory is owned by the caller and it must be kept memory alive and unmodified throughout the lifetime of |
| | | 49 | | /// Use <see cref="PEReaderExtensions.GetMetadataReader(PortableExecutable.PEReader, MetadataReaderOptions)"/> t |
| | | 50 | | /// metadata from a PE image. |
| | | 51 | | /// </remarks> |
| | | 52 | | public unsafe MetadataReader(byte* metadata, int length, MetadataReaderOptions options) |
| | 0 | 53 | | : this(metadata, length, options, utf8Decoder: null, memoryOwner: null) |
| | 0 | 54 | | { |
| | 0 | 55 | | } |
| | | 56 | | |
| | | 57 | | /// <summary> |
| | | 58 | | /// Creates a metadata reader from the metadata stored at the given memory location. |
| | | 59 | | /// </summary> |
| | | 60 | | /// <remarks> |
| | | 61 | | /// The memory is owned by the caller and it must be kept memory alive and unmodified throughout the lifetime of |
| | | 62 | | /// Use <see cref="PEReaderExtensions.GetMetadataReader(PortableExecutable.PEReader, MetadataReaderOptions, Meta |
| | | 63 | | /// metadata from a PE image. |
| | | 64 | | /// </remarks> |
| | | 65 | | /// <exception cref="ArgumentOutOfRangeException"><paramref name="length"/> is not positive.</exception> |
| | | 66 | | /// <exception cref="ArgumentNullException"><paramref name="metadata"/> is null.</exception> |
| | | 67 | | /// <exception cref="ArgumentException">The encoding of <paramref name="utf8Decoder"/> is not <see cref="UTF8Enc |
| | | 68 | | /// <exception cref="PlatformNotSupportedException">The current platform is big-endian.</exception> |
| | | 69 | | /// <exception cref="BadImageFormatException">Bad metadata header.</exception> |
| | | 70 | | public unsafe MetadataReader(byte* metadata, int length, MetadataReaderOptions options, MetadataStringDecoder? u |
| | 0 | 71 | | : this(metadata, length, options, utf8Decoder, memoryOwner: null) |
| | 0 | 72 | | { |
| | 0 | 73 | | } |
| | | 74 | | |
| | 0 | 75 | | internal unsafe MetadataReader(byte* metadata, int length, MetadataReaderOptions options, MetadataStringDecoder? |
| | 0 | 76 | | { |
| | | 77 | | // Do not throw here when length is 0. We'll throw BadImageFormatException later on, so that the caller does |
| | | 78 | | // worry about the image (stream) being empty and can handle all image errors by catching BadImageFormatExce |
| | 0 | 79 | | if (length < 0) |
| | 0 | 80 | | { |
| | 0 | 81 | | Throw.ArgumentOutOfRange(nameof(length)); |
| | | 82 | | } |
| | | 83 | | |
| | 0 | 84 | | if (metadata == null) |
| | 0 | 85 | | { |
| | 0 | 86 | | Throw.ArgumentNull(nameof(metadata)); |
| | | 87 | | } |
| | | 88 | | |
| | 0 | 89 | | utf8Decoder ??= MetadataStringDecoder.DefaultUTF8; |
| | | 90 | | |
| | 0 | 91 | | if (!(utf8Decoder.Encoding is UTF8Encoding)) |
| | 0 | 92 | | { |
| | 0 | 93 | | Throw.InvalidArgument(SR.MetadataStringDecoderEncodingMustBeUtf8, nameof(utf8Decoder)); |
| | | 94 | | } |
| | | 95 | | |
| | 0 | 96 | | Block = new MemoryBlock(metadata, length); |
| | | 97 | | |
| | 0 | 98 | | _memoryOwnerObj = memoryOwner; |
| | 0 | 99 | | _options = options; |
| | 0 | 100 | | UTF8Decoder = utf8Decoder; |
| | | 101 | | |
| | 0 | 102 | | var headerReader = new BlobReader(Block); |
| | 0 | 103 | | ReadMetadataHeader(ref headerReader, out _versionString); |
| | 0 | 104 | | _metadataKind = GetMetadataKind(_versionString); |
| | 0 | 105 | | var streamHeaders = ReadStreamHeaders(ref headerReader); |
| | | 106 | | |
| | | 107 | | // storage header and stream headers: |
| | 0 | 108 | | InitializeStreamReaders(Block, streamHeaders, out _metadataStreamKind, out var metadataTableStream, out var |
| | | 109 | | |
| | | 110 | | int[]? externalTableRowCountsOpt; |
| | 0 | 111 | | if (pdbStream.Length > 0) |
| | 0 | 112 | | { |
| | 0 | 113 | | int pdbStreamOffset = (int)(pdbStream.Pointer - metadata); |
| | 0 | 114 | | ReadStandalonePortablePdbStream(pdbStream, pdbStreamOffset, out _debugMetadataHeader, out externalTableR |
| | 0 | 115 | | } |
| | | 116 | | else |
| | 0 | 117 | | { |
| | 0 | 118 | | externalTableRowCountsOpt = null; |
| | 0 | 119 | | } |
| | | 120 | | |
| | 0 | 121 | | var tableReader = new BlobReader(metadataTableStream); |
| | | 122 | | |
| | 0 | 123 | | ReadMetadataTableHeader(ref tableReader, out var heapSizes, out var metadataTableRowCounts, out _sortedTable |
| | | 124 | | |
| | 0 | 125 | | InitializeTableReaders(tableReader.GetMemoryBlockAt(0, tableReader.RemainingBytes), heapSizes, metadataTable |
| | | 126 | | |
| | | 127 | | // This previously could occur in obfuscated assemblies but a check was added to prevent |
| | | 128 | | // it getting to this point |
| | 0 | 129 | | Debug.Assert(AssemblyTable.NumberOfRows <= 1); |
| | | 130 | | |
| | | 131 | | // Although the specification states that the module table will have exactly one row, |
| | | 132 | | // the native metadata reader would successfully read files containing more than one row. |
| | | 133 | | // Such files exist in the wild and may be produced by obfuscators. |
| | 0 | 134 | | if (pdbStream.Length == 0 && ModuleTable.NumberOfRows < 1) |
| | 0 | 135 | | { |
| | 0 | 136 | | throw new BadImageFormatException(SR.Format(SR.ModuleTableInvalidNumberOfRows, this.ModuleTable.NumberOf |
| | | 137 | | } |
| | | 138 | | |
| | | 139 | | // read |
| | 0 | 140 | | NamespaceCache = new NamespaceCache(this); |
| | | 141 | | |
| | 0 | 142 | | if (_metadataKind != MetadataKind.Ecma335) |
| | 0 | 143 | | { |
| | 0 | 144 | | WinMDMscorlibRef = FindMscorlibAssemblyRefNoProjection(); |
| | 0 | 145 | | } |
| | 0 | 146 | | } |
| | | 147 | | |
| | | 148 | | #endregion |
| | | 149 | | |
| | | 150 | | #region Metadata Headers |
| | | 151 | | |
| | | 152 | | private readonly string _versionString; |
| | | 153 | | private readonly MetadataKind _metadataKind; |
| | | 154 | | private readonly MetadataStreamKind _metadataStreamKind; |
| | | 155 | | private readonly DebugMetadataHeader? _debugMetadataHeader; |
| | | 156 | | |
| | | 157 | | internal StringHeap StringHeap; |
| | | 158 | | internal BlobHeap BlobHeap; |
| | | 159 | | internal GuidHeap GuidHeap; |
| | | 160 | | internal UserStringHeap UserStringHeap; |
| | | 161 | | |
| | | 162 | | /// <summary> |
| | | 163 | | /// True if the metadata stream has minimal delta format. Used for EnC. |
| | | 164 | | /// </summary> |
| | | 165 | | /// <remarks> |
| | | 166 | | /// The metadata stream has minimal delta format if "#JTD" stream is present. |
| | | 167 | | /// Minimal delta format uses large size (4B) when encoding table/heap references. |
| | | 168 | | /// The heaps in minimal delta only contain data of the delta, |
| | | 169 | | /// there is no padding at the beginning of the heaps that would align them |
| | | 170 | | /// with the original full metadata heaps. |
| | | 171 | | /// </remarks> |
| | | 172 | | internal bool IsMinimalDelta; |
| | | 173 | | |
| | | 174 | | /// <summary> |
| | | 175 | | /// Looks like this function reads beginning of the header described in |
| | | 176 | | /// ECMA-335 24.2.1 Metadata root |
| | | 177 | | /// </summary> |
| | | 178 | | private void ReadMetadataHeader(ref BlobReader memReader, out string versionString) |
| | 0 | 179 | | { |
| | 0 | 180 | | if (memReader.RemainingBytes < COR20Constants.MinimumSizeofMetadataHeader) |
| | 0 | 181 | | { |
| | 0 | 182 | | throw new BadImageFormatException(SR.MetadataHeaderTooSmall); |
| | | 183 | | } |
| | | 184 | | |
| | 0 | 185 | | uint signature = memReader.ReadUInt32(); |
| | 0 | 186 | | if (signature != COR20Constants.COR20MetadataSignature) |
| | 0 | 187 | | { |
| | 0 | 188 | | throw new BadImageFormatException(SR.MetadataSignature); |
| | | 189 | | } |
| | | 190 | | |
| | | 191 | | // major version |
| | 0 | 192 | | memReader.ReadUInt16(); |
| | | 193 | | |
| | | 194 | | // minor version |
| | 0 | 195 | | memReader.ReadUInt16(); |
| | | 196 | | |
| | | 197 | | // reserved: |
| | 0 | 198 | | memReader.ReadUInt32(); |
| | | 199 | | |
| | 0 | 200 | | int versionStringSize = memReader.ReadInt32(); |
| | 0 | 201 | | if (memReader.RemainingBytes < versionStringSize) |
| | 0 | 202 | | { |
| | 0 | 203 | | throw new BadImageFormatException(SR.NotEnoughSpaceForVersionString); |
| | | 204 | | } |
| | | 205 | | |
| | 0 | 206 | | versionString = memReader.GetMemoryBlockAt(0, versionStringSize).PeekUtf8NullTerminated(0, null, UTF8Decoder |
| | 0 | 207 | | memReader.Offset += versionStringSize; |
| | 0 | 208 | | } |
| | | 209 | | |
| | | 210 | | private MetadataKind GetMetadataKind(string versionString) |
| | 0 | 211 | | { |
| | | 212 | | // Treat metadata as CLI raw metadata if the client doesn't want to see projections. |
| | 0 | 213 | | if ((_options & MetadataReaderOptions.ApplyWindowsRuntimeProjections) == 0) |
| | 0 | 214 | | { |
| | 0 | 215 | | return MetadataKind.Ecma335; |
| | | 216 | | } |
| | | 217 | | |
| | 0 | 218 | | if (!versionString.Contains("WindowsRuntime")) |
| | 0 | 219 | | { |
| | 0 | 220 | | return MetadataKind.Ecma335; |
| | | 221 | | } |
| | 0 | 222 | | else if (versionString.Contains("CLR")) |
| | 0 | 223 | | { |
| | 0 | 224 | | return MetadataKind.ManagedWindowsMetadata; |
| | | 225 | | } |
| | | 226 | | else |
| | 0 | 227 | | { |
| | 0 | 228 | | return MetadataKind.WindowsMetadata; |
| | | 229 | | } |
| | 0 | 230 | | } |
| | | 231 | | |
| | | 232 | | /// <summary> |
| | | 233 | | /// Reads stream headers described in ECMA-335 24.2.2 Stream header |
| | | 234 | | /// </summary> |
| | | 235 | | private static StreamHeader[] ReadStreamHeaders(ref BlobReader memReader) |
| | 0 | 236 | | { |
| | | 237 | | // storage header: |
| | 0 | 238 | | memReader.ReadUInt16(); |
| | 0 | 239 | | int streamCount = memReader.ReadInt16(); |
| | | 240 | | |
| | 0 | 241 | | var streamHeaders = new StreamHeader[streamCount]; |
| | 0 | 242 | | for (int i = 0; i < streamHeaders.Length; i++) |
| | 0 | 243 | | { |
| | 0 | 244 | | if (memReader.RemainingBytes < COR20Constants.MinimumSizeofStreamHeader) |
| | 0 | 245 | | { |
| | 0 | 246 | | throw new BadImageFormatException(SR.StreamHeaderTooSmall); |
| | | 247 | | } |
| | | 248 | | |
| | 0 | 249 | | streamHeaders[i].Offset = memReader.ReadUInt32(); |
| | 0 | 250 | | streamHeaders[i].Size = memReader.ReadInt32(); |
| | 0 | 251 | | streamHeaders[i].Name = memReader.ReadUtf8NullTerminated(); |
| | 0 | 252 | | bool aligned = memReader.TryAlign(4); |
| | | 253 | | |
| | 0 | 254 | | if (!aligned || memReader.RemainingBytes == 0) |
| | 0 | 255 | | { |
| | 0 | 256 | | throw new BadImageFormatException(SR.NotEnoughSpaceForStreamHeaderName); |
| | | 257 | | } |
| | 0 | 258 | | } |
| | | 259 | | |
| | 0 | 260 | | return streamHeaders; |
| | 0 | 261 | | } |
| | | 262 | | |
| | | 263 | | private void InitializeStreamReaders( |
| | | 264 | | in MemoryBlock metadataRoot, |
| | | 265 | | StreamHeader[] streamHeaders, |
| | | 266 | | out MetadataStreamKind metadataStreamKind, |
| | | 267 | | out MemoryBlock metadataTableStream, |
| | | 268 | | out MemoryBlock standalonePdbStream) |
| | 0 | 269 | | { |
| | 0 | 270 | | metadataTableStream = default; |
| | 0 | 271 | | standalonePdbStream = default; |
| | 0 | 272 | | metadataStreamKind = MetadataStreamKind.Illegal; |
| | | 273 | | |
| | 0 | 274 | | foreach (StreamHeader streamHeader in streamHeaders) |
| | 0 | 275 | | { |
| | 0 | 276 | | switch (streamHeader.Name) |
| | | 277 | | { |
| | | 278 | | case COR20Constants.StringStreamName: |
| | 0 | 279 | | if (metadataRoot.Length < streamHeader.Offset + streamHeader.Size) |
| | 0 | 280 | | { |
| | 0 | 281 | | throw new BadImageFormatException(SR.NotEnoughSpaceForStringStream); |
| | | 282 | | } |
| | | 283 | | |
| | 0 | 284 | | this.StringHeap = new StringHeap(metadataRoot.GetMemoryBlockAt((int)streamHeader.Offset, streamH |
| | 0 | 285 | | break; |
| | | 286 | | |
| | | 287 | | case COR20Constants.BlobStreamName: |
| | 0 | 288 | | if (metadataRoot.Length < streamHeader.Offset + streamHeader.Size) |
| | 0 | 289 | | { |
| | 0 | 290 | | throw new BadImageFormatException(SR.NotEnoughSpaceForBlobStream); |
| | | 291 | | } |
| | | 292 | | |
| | 0 | 293 | | this.BlobHeap = new BlobHeap(metadataRoot.GetMemoryBlockAt((int)streamHeader.Offset, streamHeade |
| | 0 | 294 | | break; |
| | | 295 | | |
| | | 296 | | case COR20Constants.GUIDStreamName: |
| | 0 | 297 | | if (metadataRoot.Length < streamHeader.Offset + streamHeader.Size) |
| | 0 | 298 | | { |
| | 0 | 299 | | throw new BadImageFormatException(SR.NotEnoughSpaceForGUIDStream); |
| | | 300 | | } |
| | | 301 | | |
| | 0 | 302 | | this.GuidHeap = new GuidHeap(metadataRoot.GetMemoryBlockAt((int)streamHeader.Offset, streamHeade |
| | 0 | 303 | | break; |
| | | 304 | | |
| | | 305 | | case COR20Constants.UserStringStreamName: |
| | 0 | 306 | | if (metadataRoot.Length < streamHeader.Offset + streamHeader.Size) |
| | 0 | 307 | | { |
| | 0 | 308 | | throw new BadImageFormatException(SR.NotEnoughSpaceForBlobStream); |
| | | 309 | | } |
| | | 310 | | |
| | 0 | 311 | | this.UserStringHeap = new UserStringHeap(metadataRoot.GetMemoryBlockAt((int)streamHeader.Offset, |
| | 0 | 312 | | break; |
| | | 313 | | |
| | | 314 | | case COR20Constants.CompressedMetadataTableStreamName: |
| | 0 | 315 | | if (metadataRoot.Length < streamHeader.Offset + streamHeader.Size) |
| | 0 | 316 | | { |
| | 0 | 317 | | throw new BadImageFormatException(SR.NotEnoughSpaceForMetadataStream); |
| | | 318 | | } |
| | | 319 | | |
| | 0 | 320 | | metadataStreamKind = MetadataStreamKind.Compressed; |
| | 0 | 321 | | metadataTableStream = metadataRoot.GetMemoryBlockAt((int)streamHeader.Offset, streamHeader.Size) |
| | 0 | 322 | | break; |
| | | 323 | | |
| | | 324 | | case COR20Constants.UncompressedMetadataTableStreamName: |
| | 0 | 325 | | if (metadataRoot.Length < streamHeader.Offset + streamHeader.Size) |
| | 0 | 326 | | { |
| | 0 | 327 | | throw new BadImageFormatException(SR.NotEnoughSpaceForMetadataStream); |
| | | 328 | | } |
| | | 329 | | |
| | 0 | 330 | | metadataStreamKind = MetadataStreamKind.Uncompressed; |
| | 0 | 331 | | metadataTableStream = metadataRoot.GetMemoryBlockAt((int)streamHeader.Offset, streamHeader.Size) |
| | 0 | 332 | | break; |
| | | 333 | | |
| | | 334 | | case COR20Constants.MinimalDeltaMetadataTableStreamName: |
| | 0 | 335 | | if (metadataRoot.Length < streamHeader.Offset + streamHeader.Size) |
| | 0 | 336 | | { |
| | 0 | 337 | | throw new BadImageFormatException(SR.NotEnoughSpaceForMetadataStream); |
| | | 338 | | } |
| | | 339 | | |
| | | 340 | | // the content of the stream is ignored |
| | 0 | 341 | | this.IsMinimalDelta = true; |
| | 0 | 342 | | break; |
| | | 343 | | |
| | | 344 | | case COR20Constants.StandalonePdbStreamName: |
| | 0 | 345 | | if (metadataRoot.Length < streamHeader.Offset + streamHeader.Size) |
| | 0 | 346 | | { |
| | 0 | 347 | | throw new BadImageFormatException(SR.NotEnoughSpaceForMetadataStream); |
| | | 348 | | } |
| | | 349 | | |
| | 0 | 350 | | standalonePdbStream = metadataRoot.GetMemoryBlockAt((int)streamHeader.Offset, streamHeader.Size) |
| | 0 | 351 | | break; |
| | | 352 | | |
| | | 353 | | default: |
| | | 354 | | // Skip unknown streams. Some obfuscators insert invalid streams. |
| | 0 | 355 | | continue; |
| | | 356 | | } |
| | 0 | 357 | | } |
| | | 358 | | |
| | 0 | 359 | | if (IsMinimalDelta && metadataStreamKind != MetadataStreamKind.Uncompressed) |
| | 0 | 360 | | { |
| | 0 | 361 | | throw new BadImageFormatException(SR.InvalidMetadataStreamFormat); |
| | | 362 | | } |
| | 0 | 363 | | } |
| | | 364 | | |
| | | 365 | | #endregion |
| | | 366 | | |
| | | 367 | | #region Tables and Heaps |
| | | 368 | | |
| | | 369 | | private readonly TableMask _sortedTables; |
| | | 370 | | |
| | | 371 | | /// <summary> |
| | | 372 | | /// A row count for each possible table. May be indexed by <see cref="TableIndex"/>. |
| | | 373 | | /// </summary> |
| | 0 | 374 | | internal int[] TableRowCounts = null!; |
| | | 375 | | |
| | | 376 | | internal ModuleTableReader ModuleTable; |
| | | 377 | | internal TypeRefTableReader TypeRefTable; |
| | | 378 | | internal TypeDefTableReader TypeDefTable; |
| | | 379 | | internal FieldPtrTableReader FieldPtrTable; |
| | | 380 | | internal FieldTableReader FieldTable; |
| | | 381 | | internal MethodPtrTableReader MethodPtrTable; |
| | | 382 | | internal MethodTableReader MethodDefTable; |
| | | 383 | | internal ParamPtrTableReader ParamPtrTable; |
| | | 384 | | internal ParamTableReader ParamTable; |
| | | 385 | | internal InterfaceImplTableReader InterfaceImplTable; |
| | | 386 | | internal MemberRefTableReader MemberRefTable; |
| | | 387 | | internal ConstantTableReader ConstantTable; |
| | | 388 | | internal CustomAttributeTableReader CustomAttributeTable; |
| | | 389 | | internal FieldMarshalTableReader FieldMarshalTable; |
| | | 390 | | internal DeclSecurityTableReader DeclSecurityTable; |
| | | 391 | | internal ClassLayoutTableReader ClassLayoutTable; |
| | | 392 | | internal FieldLayoutTableReader FieldLayoutTable; |
| | | 393 | | internal StandAloneSigTableReader StandAloneSigTable; |
| | | 394 | | internal EventMapTableReader EventMapTable; |
| | | 395 | | internal EventPtrTableReader EventPtrTable; |
| | | 396 | | internal EventTableReader EventTable; |
| | | 397 | | internal PropertyMapTableReader PropertyMapTable; |
| | | 398 | | internal PropertyPtrTableReader PropertyPtrTable; |
| | | 399 | | internal PropertyTableReader PropertyTable; |
| | | 400 | | internal MethodSemanticsTableReader MethodSemanticsTable; |
| | | 401 | | internal MethodImplTableReader MethodImplTable; |
| | | 402 | | internal ModuleRefTableReader ModuleRefTable; |
| | | 403 | | internal TypeSpecTableReader TypeSpecTable; |
| | | 404 | | internal ImplMapTableReader ImplMapTable; |
| | | 405 | | internal FieldRVATableReader FieldRvaTable; |
| | | 406 | | internal EnCLogTableReader EncLogTable; |
| | | 407 | | internal EnCMapTableReader EncMapTable; |
| | | 408 | | internal AssemblyTableReader AssemblyTable; |
| | | 409 | | internal AssemblyProcessorTableReader AssemblyProcessorTable; // unused |
| | | 410 | | internal AssemblyOSTableReader AssemblyOSTable; // unused |
| | | 411 | | internal AssemblyRefTableReader AssemblyRefTable; |
| | | 412 | | internal AssemblyRefProcessorTableReader AssemblyRefProcessorTable; // unused |
| | | 413 | | internal AssemblyRefOSTableReader AssemblyRefOSTable; // unused |
| | | 414 | | internal FileTableReader FileTable; |
| | | 415 | | internal ExportedTypeTableReader ExportedTypeTable; |
| | | 416 | | internal ManifestResourceTableReader ManifestResourceTable; |
| | | 417 | | internal NestedClassTableReader NestedClassTable; |
| | | 418 | | internal GenericParamTableReader GenericParamTable; |
| | | 419 | | internal MethodSpecTableReader MethodSpecTable; |
| | | 420 | | internal GenericParamConstraintTableReader GenericParamConstraintTable; |
| | | 421 | | |
| | | 422 | | // debug tables |
| | | 423 | | internal DocumentTableReader DocumentTable; |
| | | 424 | | internal MethodDebugInformationTableReader MethodDebugInformationTable; |
| | | 425 | | internal LocalScopeTableReader LocalScopeTable; |
| | | 426 | | internal LocalVariableTableReader LocalVariableTable; |
| | | 427 | | internal LocalConstantTableReader LocalConstantTable; |
| | | 428 | | internal ImportScopeTableReader ImportScopeTable; |
| | | 429 | | internal StateMachineMethodTableReader StateMachineMethodTable; |
| | | 430 | | internal CustomDebugInformationTableReader CustomDebugInformationTable; |
| | | 431 | | |
| | | 432 | | private void ReadMetadataTableHeader(ref BlobReader reader, out HeapSizes heapSizes, out int[] metadataTableRowC |
| | 0 | 433 | | { |
| | 0 | 434 | | if (reader.RemainingBytes < MetadataStreamConstants.SizeOfMetadataTableHeader) |
| | 0 | 435 | | { |
| | 0 | 436 | | throw new BadImageFormatException(SR.MetadataTableHeaderTooSmall); |
| | | 437 | | } |
| | | 438 | | |
| | | 439 | | // reserved (shall be ignored): |
| | 0 | 440 | | reader.ReadUInt32(); |
| | | 441 | | |
| | | 442 | | // major version (shall be ignored): |
| | 0 | 443 | | reader.ReadByte(); |
| | | 444 | | |
| | | 445 | | // minor version (shall be ignored): |
| | 0 | 446 | | reader.ReadByte(); |
| | | 447 | | |
| | | 448 | | // heap sizes: |
| | 0 | 449 | | heapSizes = (HeapSizes)reader.ReadByte(); |
| | | 450 | | |
| | | 451 | | // reserved (shall be ignored): |
| | 0 | 452 | | reader.ReadByte(); |
| | | 453 | | |
| | 0 | 454 | | ulong presentTables = reader.ReadUInt64(); |
| | 0 | 455 | | sortedTables = (TableMask)reader.ReadUInt64(); |
| | | 456 | | |
| | | 457 | | // According to ECMA-335, MajorVersion and MinorVersion have fixed values and, |
| | | 458 | | // based on recommendation in 24.1 Fixed fields: When writing these fields it |
| | | 459 | | // is best that they be set to the value indicated, on reading they should be ignored. |
| | | 460 | | // We will not be checking version values. We will continue checking that the set of |
| | | 461 | | // present tables is within the set we understand. |
| | | 462 | | |
| | 0 | 463 | | ulong validTables = (ulong)(TableMask.TypeSystemTables | TableMask.DebugTables); |
| | | 464 | | |
| | 0 | 465 | | if ((presentTables & ~validTables) != 0) |
| | 0 | 466 | | { |
| | 0 | 467 | | throw new BadImageFormatException(SR.Format(SR.UnknownTables, presentTables)); |
| | | 468 | | } |
| | | 469 | | |
| | 0 | 470 | | if (_metadataStreamKind == MetadataStreamKind.Compressed) |
| | 0 | 471 | | { |
| | | 472 | | // In general Ptr tables and EnC tables are not allowed in a compressed stream. |
| | | 473 | | // However when asked for a snapshot of the current metadata after an EnC change has been applied |
| | | 474 | | // the CLR includes the EnCLog table into the snapshot. We need to be able to read the image, |
| | | 475 | | // so we'll allow the table here but pretend it's empty later. |
| | 0 | 476 | | if ((presentTables & (ulong)(TableMask.PtrTables | TableMask.EnCMap)) != 0) |
| | 0 | 477 | | { |
| | 0 | 478 | | throw new BadImageFormatException(SR.IllegalTablesInCompressedMetadataStream); |
| | | 479 | | } |
| | 0 | 480 | | } |
| | | 481 | | |
| | 0 | 482 | | metadataTableRowCounts = ReadMetadataTableRowCounts(ref reader, presentTables); |
| | | 483 | | |
| | 0 | 484 | | if ((heapSizes & HeapSizes.ExtraData) == HeapSizes.ExtraData) |
| | 0 | 485 | | { |
| | | 486 | | // Skip "extra data" used by some obfuscators. Although it is not mentioned in the CLI spec, |
| | | 487 | | // it is honored by the native metadata reader. |
| | 0 | 488 | | reader.ReadUInt32(); |
| | 0 | 489 | | } |
| | 0 | 490 | | } |
| | | 491 | | |
| | | 492 | | private static int[] ReadMetadataTableRowCounts(ref BlobReader memReader, ulong presentTableMask) |
| | 0 | 493 | | { |
| | 0 | 494 | | ulong currentTableBit = 1; |
| | | 495 | | |
| | 0 | 496 | | var rowCounts = new int[MetadataTokens.TableCount]; |
| | 0 | 497 | | for (int i = 0; i < rowCounts.Length; i++) |
| | 0 | 498 | | { |
| | 0 | 499 | | if ((presentTableMask & currentTableBit) != 0) |
| | 0 | 500 | | { |
| | 0 | 501 | | if (memReader.RemainingBytes < sizeof(uint)) |
| | 0 | 502 | | { |
| | 0 | 503 | | throw new BadImageFormatException(SR.TableRowCountSpaceTooSmall); |
| | | 504 | | } |
| | | 505 | | |
| | 0 | 506 | | uint rowCount = memReader.ReadUInt32(); |
| | 0 | 507 | | if (rowCount > TokenTypeIds.RIDMask) |
| | 0 | 508 | | { |
| | 0 | 509 | | throw new BadImageFormatException(SR.Format(SR.InvalidRowCount, rowCount)); |
| | | 510 | | } |
| | | 511 | | |
| | 0 | 512 | | rowCounts[i] = (int)rowCount; |
| | 0 | 513 | | } |
| | | 514 | | |
| | 0 | 515 | | currentTableBit <<= 1; |
| | 0 | 516 | | } |
| | | 517 | | |
| | 0 | 518 | | return rowCounts; |
| | 0 | 519 | | } |
| | | 520 | | |
| | | 521 | | // internal for testing |
| | | 522 | | internal static void ReadStandalonePortablePdbStream(MemoryBlock pdbStreamBlock, int pdbStreamOffset, out DebugM |
| | 0 | 523 | | { |
| | 0 | 524 | | var reader = new BlobReader(pdbStreamBlock); |
| | | 525 | | |
| | | 526 | | const int PdbIdSize = 20; |
| | 0 | 527 | | byte[]? pdbId = reader.ReadBytes(PdbIdSize); |
| | | 528 | | |
| | | 529 | | // ECMA-335 15.4.1.2: |
| | | 530 | | // The entry point to an application shall be static. |
| | | 531 | | // This entry point method can be a global method or it can appear inside a type. |
| | | 532 | | // The entry point method shall either accept no arguments or a vector of strings. |
| | | 533 | | // The return type of the entry point method shall be void, int32, or unsigned int32. |
| | | 534 | | // The entry point method cannot be defined in a generic class. |
| | 0 | 535 | | uint entryPointToken = reader.ReadUInt32(); |
| | 0 | 536 | | int entryPointRowId = (int)(entryPointToken & TokenTypeIds.RIDMask); |
| | 0 | 537 | | if (entryPointToken != 0 && ((entryPointToken & TokenTypeIds.TypeMask) != TokenTypeIds.MethodDef || entryPoi |
| | 0 | 538 | | { |
| | 0 | 539 | | throw new BadImageFormatException(SR.Format(SR.InvalidEntryPointToken, entryPointToken)); |
| | | 540 | | } |
| | | 541 | | |
| | 0 | 542 | | ulong externalTableMask = reader.ReadUInt64(); |
| | | 543 | | |
| | | 544 | | // EnC & Ptr tables can't be referenced from standalone PDB metadata: |
| | | 545 | | const ulong validTables = (ulong)TableMask.ValidPortablePdbExternalTables; |
| | | 546 | | |
| | 0 | 547 | | if ((externalTableMask & ~validTables) != 0) |
| | 0 | 548 | | { |
| | 0 | 549 | | throw new BadImageFormatException(SR.Format(SR.UnknownTables, externalTableMask)); |
| | | 550 | | } |
| | | 551 | | |
| | 0 | 552 | | externalTableRowCounts = ReadMetadataTableRowCounts(ref reader, externalTableMask); |
| | | 553 | | |
| | 0 | 554 | | debugMetadataHeader = new DebugMetadataHeader( |
| | 0 | 555 | | ImmutableCollectionsMarshal.AsImmutableArray(pdbId), |
| | 0 | 556 | | MethodDefinitionHandle.FromRowId(entryPointRowId), |
| | 0 | 557 | | idStartOffset: pdbStreamOffset); |
| | 0 | 558 | | } |
| | | 559 | | |
| | | 560 | | private const int SmallIndexSize = 2; |
| | | 561 | | private const int LargeIndexSize = 4; |
| | | 562 | | |
| | | 563 | | private int GetReferenceSize(int[] rowCounts, TableIndex index) |
| | 0 | 564 | | { |
| | 0 | 565 | | return (rowCounts[(int)index] < MetadataStreamConstants.LargeTableRowCount && !IsMinimalDelta) ? SmallIndexS |
| | 0 | 566 | | } |
| | | 567 | | |
| | | 568 | | private void InitializeTableReaders(MemoryBlock metadataTablesMemoryBlock, HeapSizes heapSizes, int[] rowCounts, |
| | 0 | 569 | | { |
| | | 570 | | // Size of reference tags in each table. |
| | 0 | 571 | | this.TableRowCounts = rowCounts; |
| | | 572 | | |
| | | 573 | | // TODO (https://github.com/dotnet/runtime/issues/14721): |
| | | 574 | | // Shouldn't XxxPtr table be always the same size or smaller than the corresponding Xxx table? |
| | | 575 | | |
| | | 576 | | // Compute ref sizes for tables that can have pointer tables |
| | 0 | 577 | | int fieldRefSizeSorted = GetReferenceSize(rowCounts, TableIndex.FieldPtr) > SmallIndexSize ? LargeIndexSize |
| | 0 | 578 | | int methodRefSizeSorted = GetReferenceSize(rowCounts, TableIndex.MethodPtr) > SmallIndexSize ? LargeIndexSiz |
| | 0 | 579 | | int paramRefSizeSorted = GetReferenceSize(rowCounts, TableIndex.ParamPtr) > SmallIndexSize ? LargeIndexSize |
| | 0 | 580 | | int eventRefSizeSorted = GetReferenceSize(rowCounts, TableIndex.EventPtr) > SmallIndexSize ? LargeIndexSize |
| | 0 | 581 | | int propertyRefSizeSorted = GetReferenceSize(rowCounts, TableIndex.PropertyPtr) > SmallIndexSize ? LargeInde |
| | | 582 | | |
| | | 583 | | // Compute the coded token ref sizes |
| | 0 | 584 | | int typeDefOrRefRefSize = ComputeCodedTokenSize(TypeDefOrRefTag.LargeRowSize, rowCounts, TypeDefOrRefTag.Tab |
| | 0 | 585 | | int hasConstantRefSize = ComputeCodedTokenSize(HasConstantTag.LargeRowSize, rowCounts, HasConstantTag.Tables |
| | 0 | 586 | | int hasCustomAttributeRefSize = ComputeCodedTokenSize(HasCustomAttributeTag.LargeRowSize, rowCounts, HasCust |
| | 0 | 587 | | int hasFieldMarshalRefSize = ComputeCodedTokenSize(HasFieldMarshalTag.LargeRowSize, rowCounts, HasFieldMarsh |
| | 0 | 588 | | int hasDeclSecurityRefSize = ComputeCodedTokenSize(HasDeclSecurityTag.LargeRowSize, rowCounts, HasDeclSecuri |
| | 0 | 589 | | int memberRefParentRefSize = ComputeCodedTokenSize(MemberRefParentTag.LargeRowSize, rowCounts, MemberRefPare |
| | 0 | 590 | | int hasSemanticsRefSize = ComputeCodedTokenSize(HasSemanticsTag.LargeRowSize, rowCounts, HasSemanticsTag.Tab |
| | 0 | 591 | | int methodDefOrRefRefSize = ComputeCodedTokenSize(MethodDefOrRefTag.LargeRowSize, rowCounts, MethodDefOrRefT |
| | 0 | 592 | | int memberForwardedRefSize = ComputeCodedTokenSize(MemberForwardedTag.LargeRowSize, rowCounts, MemberForward |
| | 0 | 593 | | int implementationRefSize = ComputeCodedTokenSize(ImplementationTag.LargeRowSize, rowCounts, ImplementationT |
| | 0 | 594 | | int customAttributeTypeRefSize = ComputeCodedTokenSize(CustomAttributeTypeTag.LargeRowSize, rowCounts, Custo |
| | 0 | 595 | | int resolutionScopeRefSize = ComputeCodedTokenSize(ResolutionScopeTag.LargeRowSize, rowCounts, ResolutionSco |
| | 0 | 596 | | int typeOrMethodDefRefSize = ComputeCodedTokenSize(TypeOrMethodDefTag.LargeRowSize, rowCounts, TypeOrMethodD |
| | | 597 | | |
| | | 598 | | // Compute HeapRef Sizes |
| | 0 | 599 | | int stringHeapRefSize = (heapSizes & HeapSizes.StringHeapLarge) == HeapSizes.StringHeapLarge ? LargeIndexSiz |
| | 0 | 600 | | int guidHeapRefSize = (heapSizes & HeapSizes.GuidHeapLarge) == HeapSizes.GuidHeapLarge ? LargeIndexSize : Sm |
| | 0 | 601 | | int blobHeapRefSize = (heapSizes & HeapSizes.BlobHeapLarge) == HeapSizes.BlobHeapLarge ? LargeIndexSize : Sm |
| | | 602 | | |
| | | 603 | | // Populate the Table blocks |
| | 0 | 604 | | int totalRequiredSize = 0; |
| | 0 | 605 | | this.ModuleTable = new ModuleTableReader(rowCounts[(int)TableIndex.Module], stringHeapRefSize, guidHeapRefSi |
| | 0 | 606 | | totalRequiredSize += this.ModuleTable.Block.Length; |
| | | 607 | | |
| | 0 | 608 | | this.TypeRefTable = new TypeRefTableReader(rowCounts[(int)TableIndex.TypeRef], resolutionScopeRefSize, strin |
| | 0 | 609 | | totalRequiredSize += this.TypeRefTable.Block.Length; |
| | | 610 | | |
| | 0 | 611 | | this.TypeDefTable = new TypeDefTableReader(rowCounts[(int)TableIndex.TypeDef], fieldRefSizeSorted, methodRef |
| | 0 | 612 | | totalRequiredSize += this.TypeDefTable.Block.Length; |
| | | 613 | | |
| | 0 | 614 | | this.FieldPtrTable = new FieldPtrTableReader(rowCounts[(int)TableIndex.FieldPtr], GetReferenceSize(rowCounts |
| | 0 | 615 | | totalRequiredSize += this.FieldPtrTable.Block.Length; |
| | | 616 | | |
| | 0 | 617 | | this.FieldTable = new FieldTableReader(rowCounts[(int)TableIndex.Field], stringHeapRefSize, blobHeapRefSize, |
| | 0 | 618 | | totalRequiredSize += this.FieldTable.Block.Length; |
| | | 619 | | |
| | 0 | 620 | | this.MethodPtrTable = new MethodPtrTableReader(rowCounts[(int)TableIndex.MethodPtr], GetReferenceSize(rowCou |
| | 0 | 621 | | totalRequiredSize += this.MethodPtrTable.Block.Length; |
| | | 622 | | |
| | 0 | 623 | | this.MethodDefTable = new MethodTableReader(rowCounts[(int)TableIndex.MethodDef], paramRefSizeSorted, string |
| | 0 | 624 | | totalRequiredSize += this.MethodDefTable.Block.Length; |
| | | 625 | | |
| | 0 | 626 | | this.ParamPtrTable = new ParamPtrTableReader(rowCounts[(int)TableIndex.ParamPtr], GetReferenceSize(rowCounts |
| | 0 | 627 | | totalRequiredSize += this.ParamPtrTable.Block.Length; |
| | | 628 | | |
| | 0 | 629 | | this.ParamTable = new ParamTableReader(rowCounts[(int)TableIndex.Param], stringHeapRefSize, metadataTablesMe |
| | 0 | 630 | | totalRequiredSize += this.ParamTable.Block.Length; |
| | | 631 | | |
| | 0 | 632 | | this.InterfaceImplTable = new InterfaceImplTableReader(rowCounts[(int)TableIndex.InterfaceImpl], IsDeclaredS |
| | 0 | 633 | | totalRequiredSize += this.InterfaceImplTable.Block.Length; |
| | | 634 | | |
| | 0 | 635 | | this.MemberRefTable = new MemberRefTableReader(rowCounts[(int)TableIndex.MemberRef], memberRefParentRefSize, |
| | 0 | 636 | | totalRequiredSize += this.MemberRefTable.Block.Length; |
| | | 637 | | |
| | 0 | 638 | | this.ConstantTable = new ConstantTableReader(rowCounts[(int)TableIndex.Constant], IsDeclaredSorted(TableMask |
| | 0 | 639 | | totalRequiredSize += this.ConstantTable.Block.Length; |
| | | 640 | | |
| | 0 | 641 | | this.CustomAttributeTable = new CustomAttributeTableReader(rowCounts[(int)TableIndex.CustomAttribute], |
| | 0 | 642 | | IsDeclaredSorted(TableMask.CustomAttribute), |
| | 0 | 643 | | hasCustomAttributeRefSize, |
| | 0 | 644 | | customAttributeTypeRefSize, |
| | 0 | 645 | | blobHeapRefSize, |
| | 0 | 646 | | metadataTablesMemoryBlock, |
| | 0 | 647 | | totalRequiredSize); |
| | 0 | 648 | | totalRequiredSize += this.CustomAttributeTable.Block.Length; |
| | | 649 | | |
| | 0 | 650 | | this.FieldMarshalTable = new FieldMarshalTableReader(rowCounts[(int)TableIndex.FieldMarshal], IsDeclaredSort |
| | 0 | 651 | | totalRequiredSize += this.FieldMarshalTable.Block.Length; |
| | | 652 | | |
| | 0 | 653 | | this.DeclSecurityTable = new DeclSecurityTableReader(rowCounts[(int)TableIndex.DeclSecurity], IsDeclaredSort |
| | 0 | 654 | | totalRequiredSize += this.DeclSecurityTable.Block.Length; |
| | | 655 | | |
| | 0 | 656 | | this.ClassLayoutTable = new ClassLayoutTableReader(rowCounts[(int)TableIndex.ClassLayout], IsDeclaredSorted( |
| | 0 | 657 | | totalRequiredSize += this.ClassLayoutTable.Block.Length; |
| | | 658 | | |
| | 0 | 659 | | this.FieldLayoutTable = new FieldLayoutTableReader(rowCounts[(int)TableIndex.FieldLayout], IsDeclaredSorted( |
| | 0 | 660 | | totalRequiredSize += this.FieldLayoutTable.Block.Length; |
| | | 661 | | |
| | 0 | 662 | | this.StandAloneSigTable = new StandAloneSigTableReader(rowCounts[(int)TableIndex.StandAloneSig], blobHeapRef |
| | 0 | 663 | | totalRequiredSize += this.StandAloneSigTable.Block.Length; |
| | | 664 | | |
| | 0 | 665 | | this.EventMapTable = new EventMapTableReader(rowCounts[(int)TableIndex.EventMap], GetReferenceSize(rowCounts |
| | 0 | 666 | | totalRequiredSize += this.EventMapTable.Block.Length; |
| | | 667 | | |
| | 0 | 668 | | this.EventPtrTable = new EventPtrTableReader(rowCounts[(int)TableIndex.EventPtr], GetReferenceSize(rowCounts |
| | 0 | 669 | | totalRequiredSize += this.EventPtrTable.Block.Length; |
| | | 670 | | |
| | 0 | 671 | | this.EventTable = new EventTableReader(rowCounts[(int)TableIndex.Event], typeDefOrRefRefSize, stringHeapRefS |
| | 0 | 672 | | totalRequiredSize += this.EventTable.Block.Length; |
| | | 673 | | |
| | 0 | 674 | | this.PropertyMapTable = new PropertyMapTableReader(rowCounts[(int)TableIndex.PropertyMap], GetReferenceSize( |
| | 0 | 675 | | totalRequiredSize += this.PropertyMapTable.Block.Length; |
| | | 676 | | |
| | 0 | 677 | | this.PropertyPtrTable = new PropertyPtrTableReader(rowCounts[(int)TableIndex.PropertyPtr], GetReferenceSize( |
| | 0 | 678 | | totalRequiredSize += this.PropertyPtrTable.Block.Length; |
| | | 679 | | |
| | 0 | 680 | | this.PropertyTable = new PropertyTableReader(rowCounts[(int)TableIndex.Property], stringHeapRefSize, blobHea |
| | 0 | 681 | | totalRequiredSize += this.PropertyTable.Block.Length; |
| | | 682 | | |
| | 0 | 683 | | this.MethodSemanticsTable = new MethodSemanticsTableReader(rowCounts[(int)TableIndex.MethodSemantics], IsDec |
| | 0 | 684 | | totalRequiredSize += this.MethodSemanticsTable.Block.Length; |
| | | 685 | | |
| | 0 | 686 | | this.MethodImplTable = new MethodImplTableReader(rowCounts[(int)TableIndex.MethodImpl], IsDeclaredSorted(Tab |
| | 0 | 687 | | totalRequiredSize += this.MethodImplTable.Block.Length; |
| | | 688 | | |
| | 0 | 689 | | this.ModuleRefTable = new ModuleRefTableReader(rowCounts[(int)TableIndex.ModuleRef], stringHeapRefSize, meta |
| | 0 | 690 | | totalRequiredSize += this.ModuleRefTable.Block.Length; |
| | | 691 | | |
| | 0 | 692 | | this.TypeSpecTable = new TypeSpecTableReader(rowCounts[(int)TableIndex.TypeSpec], blobHeapRefSize, metadataT |
| | 0 | 693 | | totalRequiredSize += this.TypeSpecTable.Block.Length; |
| | | 694 | | |
| | 0 | 695 | | this.ImplMapTable = new ImplMapTableReader(rowCounts[(int)TableIndex.ImplMap], IsDeclaredSorted(TableMask.Im |
| | 0 | 696 | | totalRequiredSize += this.ImplMapTable.Block.Length; |
| | | 697 | | |
| | 0 | 698 | | this.FieldRvaTable = new FieldRVATableReader(rowCounts[(int)TableIndex.FieldRva], IsDeclaredSorted(TableMask |
| | 0 | 699 | | totalRequiredSize += this.FieldRvaTable.Block.Length; |
| | | 700 | | |
| | 0 | 701 | | this.EncLogTable = new EnCLogTableReader(rowCounts[(int)TableIndex.EncLog], metadataTablesMemoryBlock, total |
| | 0 | 702 | | totalRequiredSize += this.EncLogTable.Block.Length; |
| | | 703 | | |
| | 0 | 704 | | this.EncMapTable = new EnCMapTableReader(rowCounts[(int)TableIndex.EncMap], metadataTablesMemoryBlock, total |
| | 0 | 705 | | totalRequiredSize += this.EncMapTable.Block.Length; |
| | | 706 | | |
| | 0 | 707 | | this.AssemblyTable = new AssemblyTableReader(rowCounts[(int)TableIndex.Assembly], stringHeapRefSize, blobHea |
| | 0 | 708 | | totalRequiredSize += this.AssemblyTable.Block.Length; |
| | | 709 | | |
| | 0 | 710 | | this.AssemblyProcessorTable = new AssemblyProcessorTableReader(rowCounts[(int)TableIndex.AssemblyProcessor], |
| | 0 | 711 | | totalRequiredSize += this.AssemblyProcessorTable.Block.Length; |
| | | 712 | | |
| | 0 | 713 | | this.AssemblyOSTable = new AssemblyOSTableReader(rowCounts[(int)TableIndex.AssemblyOS], metadataTablesMemory |
| | 0 | 714 | | totalRequiredSize += this.AssemblyOSTable.Block.Length; |
| | | 715 | | |
| | 0 | 716 | | this.AssemblyRefTable = new AssemblyRefTableReader(rowCounts[(int)TableIndex.AssemblyRef], stringHeapRefSize |
| | 0 | 717 | | totalRequiredSize += this.AssemblyRefTable.Block.Length; |
| | | 718 | | |
| | 0 | 719 | | this.AssemblyRefProcessorTable = new AssemblyRefProcessorTableReader(rowCounts[(int)TableIndex.AssemblyRefPr |
| | 0 | 720 | | totalRequiredSize += this.AssemblyRefProcessorTable.Block.Length; |
| | | 721 | | |
| | 0 | 722 | | this.AssemblyRefOSTable = new AssemblyRefOSTableReader(rowCounts[(int)TableIndex.AssemblyRefOS], GetReferenc |
| | 0 | 723 | | totalRequiredSize += this.AssemblyRefOSTable.Block.Length; |
| | | 724 | | |
| | 0 | 725 | | this.FileTable = new FileTableReader(rowCounts[(int)TableIndex.File], stringHeapRefSize, blobHeapRefSize, me |
| | 0 | 726 | | totalRequiredSize += this.FileTable.Block.Length; |
| | | 727 | | |
| | 0 | 728 | | this.ExportedTypeTable = new ExportedTypeTableReader(rowCounts[(int)TableIndex.ExportedType], implementation |
| | 0 | 729 | | totalRequiredSize += this.ExportedTypeTable.Block.Length; |
| | | 730 | | |
| | 0 | 731 | | this.ManifestResourceTable = new ManifestResourceTableReader(rowCounts[(int)TableIndex.ManifestResource], im |
| | 0 | 732 | | totalRequiredSize += this.ManifestResourceTable.Block.Length; |
| | | 733 | | |
| | 0 | 734 | | this.NestedClassTable = new NestedClassTableReader(rowCounts[(int)TableIndex.NestedClass], IsDeclaredSorted( |
| | 0 | 735 | | totalRequiredSize += this.NestedClassTable.Block.Length; |
| | | 736 | | |
| | 0 | 737 | | this.GenericParamTable = new GenericParamTableReader(rowCounts[(int)TableIndex.GenericParam], IsDeclaredSort |
| | 0 | 738 | | totalRequiredSize += this.GenericParamTable.Block.Length; |
| | | 739 | | |
| | 0 | 740 | | this.MethodSpecTable = new MethodSpecTableReader(rowCounts[(int)TableIndex.MethodSpec], methodDefOrRefRefSiz |
| | 0 | 741 | | totalRequiredSize += this.MethodSpecTable.Block.Length; |
| | | 742 | | |
| | 0 | 743 | | this.GenericParamConstraintTable = new GenericParamConstraintTableReader(rowCounts[(int)TableIndex.GenericPa |
| | 0 | 744 | | totalRequiredSize += this.GenericParamConstraintTable.Block.Length; |
| | | 745 | | |
| | | 746 | | // debug tables: |
| | | 747 | | // Type-system metadata tables may be stored in a separate (external) metadata file. |
| | | 748 | | // We need to use the row counts of the external tables when referencing them. |
| | | 749 | | // Debug tables are local to the current metadata image and type system metadata tables are external and pre |
| | 0 | 750 | | var combinedRowCounts = (externalRowCountsOpt != null) ? CombineRowCounts(rowCounts, externalRowCountsOpt, f |
| | | 751 | | |
| | 0 | 752 | | int methodRefSizeCombined = GetReferenceSize(combinedRowCounts, TableIndex.MethodDef); |
| | 0 | 753 | | int hasCustomDebugInformationRefSizeCombined = ComputeCodedTokenSize(HasCustomDebugInformationTag.LargeRowSi |
| | | 754 | | |
| | 0 | 755 | | this.DocumentTable = new DocumentTableReader(rowCounts[(int)TableIndex.Document], guidHeapRefSize, blobHeapR |
| | 0 | 756 | | totalRequiredSize += this.DocumentTable.Block.Length; |
| | | 757 | | |
| | 0 | 758 | | this.MethodDebugInformationTable = new MethodDebugInformationTableReader(rowCounts[(int)TableIndex.MethodDeb |
| | 0 | 759 | | totalRequiredSize += this.MethodDebugInformationTable.Block.Length; |
| | | 760 | | |
| | 0 | 761 | | this.LocalScopeTable = new LocalScopeTableReader(rowCounts[(int)TableIndex.LocalScope], IsDeclaredSorted(Tab |
| | 0 | 762 | | totalRequiredSize += this.LocalScopeTable.Block.Length; |
| | | 763 | | |
| | 0 | 764 | | this.LocalVariableTable = new LocalVariableTableReader(rowCounts[(int)TableIndex.LocalVariable], stringHeapR |
| | 0 | 765 | | totalRequiredSize += this.LocalVariableTable.Block.Length; |
| | | 766 | | |
| | 0 | 767 | | this.LocalConstantTable = new LocalConstantTableReader(rowCounts[(int)TableIndex.LocalConstant], stringHeapR |
| | 0 | 768 | | totalRequiredSize += this.LocalConstantTable.Block.Length; |
| | | 769 | | |
| | 0 | 770 | | this.ImportScopeTable = new ImportScopeTableReader(rowCounts[(int)TableIndex.ImportScope], GetReferenceSize( |
| | 0 | 771 | | totalRequiredSize += this.ImportScopeTable.Block.Length; |
| | | 772 | | |
| | 0 | 773 | | this.StateMachineMethodTable = new StateMachineMethodTableReader(rowCounts[(int)TableIndex.StateMachineMetho |
| | 0 | 774 | | totalRequiredSize += this.StateMachineMethodTable.Block.Length; |
| | | 775 | | |
| | 0 | 776 | | this.CustomDebugInformationTable = new CustomDebugInformationTableReader(rowCounts[(int)TableIndex.CustomDeb |
| | 0 | 777 | | totalRequiredSize += this.CustomDebugInformationTable.Block.Length; |
| | | 778 | | |
| | 0 | 779 | | if (totalRequiredSize > metadataTablesMemoryBlock.Length) |
| | 0 | 780 | | { |
| | 0 | 781 | | throw new BadImageFormatException(SR.MetadataTablesTooSmall); |
| | | 782 | | } |
| | 0 | 783 | | } |
| | | 784 | | |
| | | 785 | | private static int[] CombineRowCounts(int[] local, int[] external, TableIndex firstLocalTableIndex) |
| | 0 | 786 | | { |
| | 0 | 787 | | Debug.Assert(local.Length == external.Length); |
| | | 788 | | |
| | 0 | 789 | | var rowCounts = new int[local.Length]; |
| | 0 | 790 | | for (int i = 0; i < (int)firstLocalTableIndex; i++) |
| | 0 | 791 | | { |
| | 0 | 792 | | rowCounts[i] = external[i]; |
| | 0 | 793 | | } |
| | | 794 | | |
| | 0 | 795 | | for (int i = (int)firstLocalTableIndex; i < rowCounts.Length; i++) |
| | 0 | 796 | | { |
| | 0 | 797 | | rowCounts[i] = local[i]; |
| | 0 | 798 | | } |
| | | 799 | | |
| | 0 | 800 | | return rowCounts; |
| | 0 | 801 | | } |
| | | 802 | | |
| | | 803 | | private int ComputeCodedTokenSize(int largeRowSize, int[] rowCounts, TableMask tablesReferenced) |
| | 0 | 804 | | { |
| | 0 | 805 | | if (IsMinimalDelta) |
| | 0 | 806 | | { |
| | 0 | 807 | | return LargeIndexSize; |
| | | 808 | | } |
| | | 809 | | |
| | 0 | 810 | | bool isAllReferencedTablesSmall = true; |
| | 0 | 811 | | ulong tablesReferencedMask = (ulong)tablesReferenced; |
| | 0 | 812 | | for (int tableIndex = 0; tableIndex < MetadataTokens.TableCount; tableIndex++) |
| | 0 | 813 | | { |
| | 0 | 814 | | if ((tablesReferencedMask & 1) != 0) |
| | 0 | 815 | | { |
| | 0 | 816 | | isAllReferencedTablesSmall = isAllReferencedTablesSmall && (rowCounts[tableIndex] < largeRowSize); |
| | 0 | 817 | | } |
| | | 818 | | |
| | 0 | 819 | | tablesReferencedMask >>= 1; |
| | 0 | 820 | | } |
| | | 821 | | |
| | 0 | 822 | | return isAllReferencedTablesSmall ? SmallIndexSize : LargeIndexSize; |
| | 0 | 823 | | } |
| | | 824 | | |
| | | 825 | | private bool IsDeclaredSorted(TableMask index) |
| | 0 | 826 | | { |
| | 0 | 827 | | return (_sortedTables & index) != 0; |
| | 0 | 828 | | } |
| | | 829 | | |
| | | 830 | | #endregion |
| | | 831 | | |
| | | 832 | | #region Helpers |
| | | 833 | | |
| | 0 | 834 | | internal bool UseFieldPtrTable => FieldPtrTable.NumberOfRows > 0; |
| | 0 | 835 | | internal bool UseMethodPtrTable => MethodPtrTable.NumberOfRows > 0; |
| | 0 | 836 | | internal bool UseParamPtrTable => ParamPtrTable.NumberOfRows > 0; |
| | 0 | 837 | | internal bool UseEventPtrTable => EventPtrTable.NumberOfRows > 0; |
| | 0 | 838 | | internal bool UsePropertyPtrTable => PropertyPtrTable.NumberOfRows > 0; |
| | | 839 | | |
| | | 840 | | internal void GetFieldRange(TypeDefinitionHandle typeDef, out int firstFieldRowId, out int lastFieldRowId) |
| | 0 | 841 | | { |
| | 0 | 842 | | int typeDefRowId = typeDef.RowId; |
| | | 843 | | |
| | 0 | 844 | | firstFieldRowId = this.TypeDefTable.GetFieldStart(typeDefRowId); |
| | 0 | 845 | | if (firstFieldRowId == 0) |
| | 0 | 846 | | { |
| | 0 | 847 | | firstFieldRowId = 1; |
| | 0 | 848 | | lastFieldRowId = 0; |
| | 0 | 849 | | } |
| | 0 | 850 | | else if (typeDefRowId == this.TypeDefTable.NumberOfRows) |
| | 0 | 851 | | { |
| | 0 | 852 | | lastFieldRowId = (this.UseFieldPtrTable) ? this.FieldPtrTable.NumberOfRows : this.FieldTable.NumberOfRow |
| | 0 | 853 | | } |
| | | 854 | | else |
| | 0 | 855 | | { |
| | 0 | 856 | | lastFieldRowId = this.TypeDefTable.GetFieldStart(typeDefRowId + 1) - 1; |
| | 0 | 857 | | } |
| | 0 | 858 | | } |
| | | 859 | | |
| | | 860 | | internal void GetMethodRange(TypeDefinitionHandle typeDef, out int firstMethodRowId, out int lastMethodRowId) |
| | 0 | 861 | | { |
| | 0 | 862 | | int typeDefRowId = typeDef.RowId; |
| | 0 | 863 | | firstMethodRowId = this.TypeDefTable.GetMethodStart(typeDefRowId); |
| | 0 | 864 | | if (firstMethodRowId == 0) |
| | 0 | 865 | | { |
| | 0 | 866 | | firstMethodRowId = 1; |
| | 0 | 867 | | lastMethodRowId = 0; |
| | 0 | 868 | | } |
| | 0 | 869 | | else if (typeDefRowId == this.TypeDefTable.NumberOfRows) |
| | 0 | 870 | | { |
| | 0 | 871 | | lastMethodRowId = (this.UseMethodPtrTable) ? this.MethodPtrTable.NumberOfRows : this.MethodDefTable.Numb |
| | 0 | 872 | | } |
| | | 873 | | else |
| | 0 | 874 | | { |
| | 0 | 875 | | lastMethodRowId = this.TypeDefTable.GetMethodStart(typeDefRowId + 1) - 1; |
| | 0 | 876 | | } |
| | 0 | 877 | | } |
| | | 878 | | |
| | | 879 | | internal void GetEventRange(TypeDefinitionHandle typeDef, out int firstEventRowId, out int lastEventRowId) |
| | 0 | 880 | | { |
| | 0 | 881 | | int eventMapRowId = this.EventMapTable.FindEventMapRowIdFor(typeDef); |
| | 0 | 882 | | if (eventMapRowId == 0) |
| | 0 | 883 | | { |
| | 0 | 884 | | firstEventRowId = 1; |
| | 0 | 885 | | lastEventRowId = 0; |
| | 0 | 886 | | return; |
| | | 887 | | } |
| | | 888 | | |
| | 0 | 889 | | firstEventRowId = this.EventMapTable.GetEventListStartFor(eventMapRowId); |
| | 0 | 890 | | if (eventMapRowId == this.EventMapTable.NumberOfRows) |
| | 0 | 891 | | { |
| | 0 | 892 | | lastEventRowId = this.UseEventPtrTable ? this.EventPtrTable.NumberOfRows : this.EventTable.NumberOfRows; |
| | 0 | 893 | | } |
| | | 894 | | else |
| | 0 | 895 | | { |
| | 0 | 896 | | lastEventRowId = this.EventMapTable.GetEventListStartFor(eventMapRowId + 1) - 1; |
| | 0 | 897 | | } |
| | 0 | 898 | | } |
| | | 899 | | |
| | | 900 | | internal void GetPropertyRange(TypeDefinitionHandle typeDef, out int firstPropertyRowId, out int lastPropertyRow |
| | 0 | 901 | | { |
| | 0 | 902 | | int propertyMapRowId = this.PropertyMapTable.FindPropertyMapRowIdFor(typeDef); |
| | 0 | 903 | | if (propertyMapRowId == 0) |
| | 0 | 904 | | { |
| | 0 | 905 | | firstPropertyRowId = 1; |
| | 0 | 906 | | lastPropertyRowId = 0; |
| | 0 | 907 | | return; |
| | | 908 | | } |
| | | 909 | | |
| | 0 | 910 | | firstPropertyRowId = this.PropertyMapTable.GetPropertyListStartFor(propertyMapRowId); |
| | 0 | 911 | | if (propertyMapRowId == this.PropertyMapTable.NumberOfRows) |
| | 0 | 912 | | { |
| | 0 | 913 | | lastPropertyRowId = (this.UsePropertyPtrTable) ? this.PropertyPtrTable.NumberOfRows : this.PropertyTable |
| | 0 | 914 | | } |
| | | 915 | | else |
| | 0 | 916 | | { |
| | 0 | 917 | | lastPropertyRowId = this.PropertyMapTable.GetPropertyListStartFor(propertyMapRowId + 1) - 1; |
| | 0 | 918 | | } |
| | 0 | 919 | | } |
| | | 920 | | |
| | | 921 | | internal void GetParameterRange(MethodDefinitionHandle methodDef, out int firstParamRowId, out int lastParamRowI |
| | 0 | 922 | | { |
| | 0 | 923 | | int rid = methodDef.RowId; |
| | | 924 | | |
| | 0 | 925 | | firstParamRowId = this.MethodDefTable.GetParamStart(rid); |
| | 0 | 926 | | if (firstParamRowId == 0) |
| | 0 | 927 | | { |
| | 0 | 928 | | firstParamRowId = 1; |
| | 0 | 929 | | lastParamRowId = 0; |
| | 0 | 930 | | } |
| | 0 | 931 | | else if (rid == this.MethodDefTable.NumberOfRows) |
| | 0 | 932 | | { |
| | 0 | 933 | | lastParamRowId = (this.UseParamPtrTable ? this.ParamPtrTable.NumberOfRows : this.ParamTable.NumberOfRows |
| | 0 | 934 | | } |
| | | 935 | | else |
| | 0 | 936 | | { |
| | 0 | 937 | | lastParamRowId = this.MethodDefTable.GetParamStart(rid + 1) - 1; |
| | 0 | 938 | | } |
| | 0 | 939 | | } |
| | | 940 | | |
| | | 941 | | internal void GetLocalVariableRange(LocalScopeHandle scope, out int firstVariableRowId, out int lastVariableRowI |
| | 0 | 942 | | { |
| | 0 | 943 | | int scopeRowId = scope.RowId; |
| | | 944 | | |
| | 0 | 945 | | firstVariableRowId = this.LocalScopeTable.GetVariableStart(scopeRowId); |
| | 0 | 946 | | if (firstVariableRowId == 0) |
| | 0 | 947 | | { |
| | 0 | 948 | | firstVariableRowId = 1; |
| | 0 | 949 | | lastVariableRowId = 0; |
| | 0 | 950 | | } |
| | 0 | 951 | | else if (scopeRowId == this.LocalScopeTable.NumberOfRows) |
| | 0 | 952 | | { |
| | 0 | 953 | | lastVariableRowId = this.LocalVariableTable.NumberOfRows; |
| | 0 | 954 | | } |
| | | 955 | | else |
| | 0 | 956 | | { |
| | 0 | 957 | | lastVariableRowId = this.LocalScopeTable.GetVariableStart(scopeRowId + 1) - 1; |
| | 0 | 958 | | } |
| | 0 | 959 | | } |
| | | 960 | | |
| | | 961 | | internal void GetLocalConstantRange(LocalScopeHandle scope, out int firstConstantRowId, out int lastConstantRowI |
| | 0 | 962 | | { |
| | 0 | 963 | | int scopeRowId = scope.RowId; |
| | | 964 | | |
| | 0 | 965 | | firstConstantRowId = this.LocalScopeTable.GetConstantStart(scopeRowId); |
| | 0 | 966 | | if (firstConstantRowId == 0) |
| | 0 | 967 | | { |
| | 0 | 968 | | firstConstantRowId = 1; |
| | 0 | 969 | | lastConstantRowId = 0; |
| | 0 | 970 | | } |
| | 0 | 971 | | else if (scopeRowId == this.LocalScopeTable.NumberOfRows) |
| | 0 | 972 | | { |
| | 0 | 973 | | lastConstantRowId = this.LocalConstantTable.NumberOfRows; |
| | 0 | 974 | | } |
| | | 975 | | else |
| | 0 | 976 | | { |
| | 0 | 977 | | lastConstantRowId = this.LocalScopeTable.GetConstantStart(scopeRowId + 1) - 1; |
| | 0 | 978 | | } |
| | 0 | 979 | | } |
| | | 980 | | |
| | | 981 | | #endregion |
| | | 982 | | |
| | | 983 | | #region Public APIs |
| | | 984 | | |
| | | 985 | | /// <summary> |
| | | 986 | | /// Pointer to the underlying data. |
| | | 987 | | /// </summary> |
| | 0 | 988 | | public unsafe byte* MetadataPointer => Block.Pointer; |
| | | 989 | | |
| | | 990 | | /// <summary> |
| | | 991 | | /// Length of the underlying data. |
| | | 992 | | /// </summary> |
| | 0 | 993 | | public int MetadataLength => Block.Length; |
| | | 994 | | |
| | | 995 | | /// <summary> |
| | | 996 | | /// Options passed to the constructor. |
| | | 997 | | /// </summary> |
| | 0 | 998 | | public MetadataReaderOptions Options => _options; |
| | | 999 | | |
| | | 1000 | | /// <summary> |
| | | 1001 | | /// Version string read from metadata header. |
| | | 1002 | | /// </summary> |
| | 0 | 1003 | | public string MetadataVersion => _versionString; |
| | | 1004 | | |
| | | 1005 | | /// <summary> |
| | | 1006 | | /// Information decoded from #Pdb stream, or null if the stream is not present. |
| | | 1007 | | /// </summary> |
| | 0 | 1008 | | public DebugMetadataHeader? DebugMetadataHeader => _debugMetadataHeader; |
| | | 1009 | | |
| | | 1010 | | /// <summary> |
| | | 1011 | | /// The kind of the metadata (plain ECMA335, WinMD, etc.). |
| | | 1012 | | /// </summary> |
| | 0 | 1013 | | public MetadataKind MetadataKind => _metadataKind; |
| | | 1014 | | |
| | | 1015 | | /// <summary> |
| | | 1016 | | /// Comparer used to compare strings stored in metadata. |
| | | 1017 | | /// </summary> |
| | 0 | 1018 | | public MetadataStringComparer StringComparer => new MetadataStringComparer(this); |
| | | 1019 | | |
| | | 1020 | | /// <summary> |
| | | 1021 | | /// The decoder used by the reader to produce <see cref="string"/> instances from UTF-8 encoded byte sequences. |
| | | 1022 | | /// </summary> |
| | 0 | 1023 | | public MetadataStringDecoder UTF8Decoder { get; } |
| | | 1024 | | |
| | | 1025 | | /// <summary> |
| | | 1026 | | /// Returns true if the metadata represent an assembly. |
| | | 1027 | | /// </summary> |
| | 0 | 1028 | | public bool IsAssembly => AssemblyTable.NumberOfRows == 1; |
| | | 1029 | | |
| | 0 | 1030 | | public AssemblyReferenceHandleCollection AssemblyReferences => new AssemblyReferenceHandleCollection(this); |
| | 0 | 1031 | | public TypeDefinitionHandleCollection TypeDefinitions => new TypeDefinitionHandleCollection(TypeDefTable.NumberO |
| | 0 | 1032 | | public TypeReferenceHandleCollection TypeReferences => new TypeReferenceHandleCollection(TypeRefTable.NumberOfRo |
| | 0 | 1033 | | public CustomAttributeHandleCollection CustomAttributes => new CustomAttributeHandleCollection(this); |
| | 0 | 1034 | | public DeclarativeSecurityAttributeHandleCollection DeclarativeSecurityAttributes => new DeclarativeSecurityAttr |
| | 0 | 1035 | | public MemberReferenceHandleCollection MemberReferences => new MemberReferenceHandleCollection(MemberRefTable.Nu |
| | 0 | 1036 | | public ManifestResourceHandleCollection ManifestResources => new ManifestResourceHandleCollection(ManifestResour |
| | 0 | 1037 | | public AssemblyFileHandleCollection AssemblyFiles => new AssemblyFileHandleCollection(FileTable.NumberOfRows); |
| | 0 | 1038 | | public ExportedTypeHandleCollection ExportedTypes => new ExportedTypeHandleCollection(ExportedTypeTable.NumberOf |
| | 0 | 1039 | | public MethodDefinitionHandleCollection MethodDefinitions => new MethodDefinitionHandleCollection(this); |
| | 0 | 1040 | | public FieldDefinitionHandleCollection FieldDefinitions => new FieldDefinitionHandleCollection(this); |
| | 0 | 1041 | | public EventDefinitionHandleCollection EventDefinitions => new EventDefinitionHandleCollection(this); |
| | 0 | 1042 | | public PropertyDefinitionHandleCollection PropertyDefinitions => new PropertyDefinitionHandleCollection(this); |
| | 0 | 1043 | | public DocumentHandleCollection Documents => new DocumentHandleCollection(this); |
| | 0 | 1044 | | public MethodDebugInformationHandleCollection MethodDebugInformation => new MethodDebugInformationHandleCollecti |
| | 0 | 1045 | | public LocalScopeHandleCollection LocalScopes => new LocalScopeHandleCollection(this, 0); |
| | 0 | 1046 | | public LocalVariableHandleCollection LocalVariables => new LocalVariableHandleCollection(this, default(LocalScop |
| | 0 | 1047 | | public LocalConstantHandleCollection LocalConstants => new LocalConstantHandleCollection(this, default(LocalScop |
| | 0 | 1048 | | public ImportScopeCollection ImportScopes => new ImportScopeCollection(this); |
| | 0 | 1049 | | public CustomDebugInformationHandleCollection CustomDebugInformation => new CustomDebugInformationHandleCollecti |
| | | 1050 | | |
| | | 1051 | | public AssemblyDefinition GetAssemblyDefinition() |
| | 0 | 1052 | | { |
| | 0 | 1053 | | if (!IsAssembly) |
| | 0 | 1054 | | { |
| | 0 | 1055 | | throw new InvalidOperationException(SR.MetadataImageDoesNotRepresentAnAssembly); |
| | | 1056 | | } |
| | | 1057 | | |
| | 0 | 1058 | | return new AssemblyDefinition(this); |
| | 0 | 1059 | | } |
| | | 1060 | | |
| | | 1061 | | public string GetString(StringHandle handle) |
| | 0 | 1062 | | { |
| | 0 | 1063 | | return StringHeap.GetString(handle, UTF8Decoder); |
| | 0 | 1064 | | } |
| | | 1065 | | |
| | | 1066 | | public string GetString(NamespaceDefinitionHandle handle) |
| | 0 | 1067 | | { |
| | 0 | 1068 | | if (handle.HasFullName) |
| | 0 | 1069 | | { |
| | 0 | 1070 | | return StringHeap.GetString(handle.GetFullName(), UTF8Decoder); |
| | | 1071 | | } |
| | | 1072 | | |
| | 0 | 1073 | | return NamespaceCache.GetFullName(handle); |
| | 0 | 1074 | | } |
| | | 1075 | | |
| | | 1076 | | public byte[] GetBlobBytes(BlobHandle handle) |
| | 0 | 1077 | | { |
| | 0 | 1078 | | return BlobHeap.GetBytes(handle); |
| | 0 | 1079 | | } |
| | | 1080 | | |
| | | 1081 | | public ImmutableArray<byte> GetBlobContent(BlobHandle handle) |
| | 0 | 1082 | | { |
| | 0 | 1083 | | byte[]? bytes = BlobHeap.GetBytes(handle, unique: false); |
| | 0 | 1084 | | return ImmutableCollectionsMarshal.AsImmutableArray(bytes); |
| | 0 | 1085 | | } |
| | | 1086 | | |
| | | 1087 | | public BlobReader GetBlobReader(BlobHandle handle) |
| | 0 | 1088 | | { |
| | 0 | 1089 | | return BlobHeap.GetBlobReader(handle); |
| | 0 | 1090 | | } |
| | | 1091 | | |
| | | 1092 | | public BlobReader GetBlobReader(StringHandle handle) |
| | 0 | 1093 | | { |
| | 0 | 1094 | | return StringHeap.GetBlobReader(handle); |
| | 0 | 1095 | | } |
| | | 1096 | | |
| | | 1097 | | public string GetUserString(UserStringHandle handle) |
| | 0 | 1098 | | { |
| | 0 | 1099 | | return UserStringHeap.GetString(handle); |
| | 0 | 1100 | | } |
| | | 1101 | | |
| | | 1102 | | public Guid GetGuid(GuidHandle handle) |
| | 0 | 1103 | | { |
| | 0 | 1104 | | return GuidHeap.GetGuid(handle); |
| | 0 | 1105 | | } |
| | | 1106 | | |
| | | 1107 | | public ModuleDefinition GetModuleDefinition() |
| | 0 | 1108 | | { |
| | 0 | 1109 | | if (_debugMetadataHeader != null) |
| | 0 | 1110 | | { |
| | 0 | 1111 | | throw new InvalidOperationException(SR.StandaloneDebugMetadataImageDoesNotContainModuleTable); |
| | | 1112 | | } |
| | | 1113 | | |
| | 0 | 1114 | | return new ModuleDefinition(this); |
| | 0 | 1115 | | } |
| | | 1116 | | |
| | | 1117 | | public AssemblyReference GetAssemblyReference(AssemblyReferenceHandle handle) |
| | 0 | 1118 | | { |
| | 0 | 1119 | | return new AssemblyReference(this, handle.Value); |
| | 0 | 1120 | | } |
| | | 1121 | | |
| | | 1122 | | public TypeDefinition GetTypeDefinition(TypeDefinitionHandle handle) |
| | 0 | 1123 | | { |
| | | 1124 | | // PERF: This code pattern is JIT friendly and results in very efficient code. |
| | 0 | 1125 | | return new TypeDefinition(this, GetTypeDefTreatmentAndRowId(handle)); |
| | 0 | 1126 | | } |
| | | 1127 | | |
| | | 1128 | | public NamespaceDefinition GetNamespaceDefinitionRoot() |
| | 0 | 1129 | | { |
| | 0 | 1130 | | NamespaceData data = NamespaceCache.GetRootNamespace(); |
| | 0 | 1131 | | return new NamespaceDefinition(data); |
| | 0 | 1132 | | } |
| | | 1133 | | |
| | | 1134 | | public NamespaceDefinition GetNamespaceDefinition(NamespaceDefinitionHandle handle) |
| | 0 | 1135 | | { |
| | 0 | 1136 | | NamespaceData data = NamespaceCache.GetNamespaceData(handle); |
| | 0 | 1137 | | return new NamespaceDefinition(data); |
| | 0 | 1138 | | } |
| | | 1139 | | |
| | | 1140 | | private uint GetTypeDefTreatmentAndRowId(TypeDefinitionHandle handle) |
| | 0 | 1141 | | { |
| | | 1142 | | // PERF: This code pattern is JIT friendly and results in very efficient code. |
| | 0 | 1143 | | if (_metadataKind == MetadataKind.Ecma335) |
| | 0 | 1144 | | { |
| | 0 | 1145 | | return (uint)handle.RowId; |
| | | 1146 | | } |
| | | 1147 | | |
| | 0 | 1148 | | return CalculateTypeDefTreatmentAndRowId(handle); |
| | 0 | 1149 | | } |
| | | 1150 | | |
| | | 1151 | | public TypeReference GetTypeReference(TypeReferenceHandle handle) |
| | 0 | 1152 | | { |
| | | 1153 | | // PERF: This code pattern is JIT friendly and results in very efficient code. |
| | 0 | 1154 | | return new TypeReference(this, GetTypeRefTreatmentAndRowId(handle)); |
| | 0 | 1155 | | } |
| | | 1156 | | |
| | | 1157 | | private uint GetTypeRefTreatmentAndRowId(TypeReferenceHandle handle) |
| | 0 | 1158 | | { |
| | | 1159 | | // PERF: This code pattern is JIT friendly and results in very efficient code. |
| | 0 | 1160 | | if (_metadataKind == MetadataKind.Ecma335) |
| | 0 | 1161 | | { |
| | 0 | 1162 | | return (uint)handle.RowId; |
| | | 1163 | | } |
| | | 1164 | | |
| | 0 | 1165 | | return CalculateTypeRefTreatmentAndRowId(handle); |
| | 0 | 1166 | | } |
| | | 1167 | | |
| | | 1168 | | public ExportedType GetExportedType(ExportedTypeHandle handle) |
| | 0 | 1169 | | { |
| | 0 | 1170 | | return new ExportedType(this, handle.RowId); |
| | 0 | 1171 | | } |
| | | 1172 | | |
| | | 1173 | | public CustomAttributeHandleCollection GetCustomAttributes(EntityHandle handle) |
| | 0 | 1174 | | { |
| | 0 | 1175 | | return new CustomAttributeHandleCollection(this, handle); |
| | 0 | 1176 | | } |
| | | 1177 | | |
| | | 1178 | | public CustomAttribute GetCustomAttribute(CustomAttributeHandle handle) |
| | 0 | 1179 | | { |
| | | 1180 | | // PERF: This code pattern is JIT friendly and results in very efficient code. |
| | 0 | 1181 | | return new CustomAttribute(this, GetCustomAttributeTreatmentAndRowId(handle)); |
| | 0 | 1182 | | } |
| | | 1183 | | |
| | | 1184 | | private uint GetCustomAttributeTreatmentAndRowId(CustomAttributeHandle handle) |
| | 0 | 1185 | | { |
| | | 1186 | | // PERF: This code pattern is JIT friendly and results in very efficient code. |
| | 0 | 1187 | | if (_metadataKind == MetadataKind.Ecma335) |
| | 0 | 1188 | | { |
| | 0 | 1189 | | return (uint)handle.RowId; |
| | | 1190 | | } |
| | | 1191 | | |
| | 0 | 1192 | | return TreatmentAndRowId((byte)CustomAttributeTreatment.WinMD, handle.RowId); |
| | 0 | 1193 | | } |
| | | 1194 | | |
| | | 1195 | | public DeclarativeSecurityAttribute GetDeclarativeSecurityAttribute(DeclarativeSecurityAttributeHandle handle) |
| | 0 | 1196 | | { |
| | | 1197 | | // PERF: This code pattern is JIT friendly and results in very efficient code. |
| | 0 | 1198 | | return new DeclarativeSecurityAttribute(this, handle.RowId); |
| | 0 | 1199 | | } |
| | | 1200 | | |
| | | 1201 | | public Constant GetConstant(ConstantHandle handle) |
| | 0 | 1202 | | { |
| | 0 | 1203 | | return new Constant(this, handle.RowId); |
| | 0 | 1204 | | } |
| | | 1205 | | |
| | | 1206 | | public MethodDefinition GetMethodDefinition(MethodDefinitionHandle handle) |
| | 0 | 1207 | | { |
| | | 1208 | | // PERF: This code pattern is JIT friendly and results in very efficient code. |
| | 0 | 1209 | | return new MethodDefinition(this, GetMethodDefTreatmentAndRowId(handle)); |
| | 0 | 1210 | | } |
| | | 1211 | | |
| | | 1212 | | private uint GetMethodDefTreatmentAndRowId(MethodDefinitionHandle handle) |
| | 0 | 1213 | | { |
| | | 1214 | | // PERF: This code pattern is JIT friendly and results in very efficient code. |
| | 0 | 1215 | | if (_metadataKind == MetadataKind.Ecma335) |
| | 0 | 1216 | | { |
| | 0 | 1217 | | return (uint)handle.RowId; |
| | | 1218 | | } |
| | | 1219 | | |
| | 0 | 1220 | | return CalculateMethodDefTreatmentAndRowId(handle); |
| | 0 | 1221 | | } |
| | | 1222 | | |
| | | 1223 | | public FieldDefinition GetFieldDefinition(FieldDefinitionHandle handle) |
| | 0 | 1224 | | { |
| | | 1225 | | // PERF: This code pattern is JIT friendly and results in very efficient code. |
| | 0 | 1226 | | return new FieldDefinition(this, GetFieldDefTreatmentAndRowId(handle)); |
| | 0 | 1227 | | } |
| | | 1228 | | |
| | | 1229 | | private uint GetFieldDefTreatmentAndRowId(FieldDefinitionHandle handle) |
| | 0 | 1230 | | { |
| | | 1231 | | // PERF: This code pattern is JIT friendly and results in very efficient code. |
| | 0 | 1232 | | if (_metadataKind == MetadataKind.Ecma335) |
| | 0 | 1233 | | { |
| | 0 | 1234 | | return (uint)handle.RowId; |
| | | 1235 | | } |
| | | 1236 | | |
| | 0 | 1237 | | return CalculateFieldDefTreatmentAndRowId(handle); |
| | 0 | 1238 | | } |
| | | 1239 | | |
| | | 1240 | | public PropertyDefinition GetPropertyDefinition(PropertyDefinitionHandle handle) |
| | 0 | 1241 | | { |
| | 0 | 1242 | | return new PropertyDefinition(this, handle); |
| | 0 | 1243 | | } |
| | | 1244 | | |
| | | 1245 | | public EventDefinition GetEventDefinition(EventDefinitionHandle handle) |
| | 0 | 1246 | | { |
| | 0 | 1247 | | return new EventDefinition(this, handle); |
| | 0 | 1248 | | } |
| | | 1249 | | |
| | | 1250 | | public MethodImplementation GetMethodImplementation(MethodImplementationHandle handle) |
| | 0 | 1251 | | { |
| | 0 | 1252 | | return new MethodImplementation(this, handle); |
| | 0 | 1253 | | } |
| | | 1254 | | |
| | | 1255 | | public MemberReference GetMemberReference(MemberReferenceHandle handle) |
| | 0 | 1256 | | { |
| | | 1257 | | // PERF: This code pattern is JIT friendly and results in very efficient code. |
| | 0 | 1258 | | return new MemberReference(this, GetMemberRefTreatmentAndRowId(handle)); |
| | 0 | 1259 | | } |
| | | 1260 | | |
| | | 1261 | | private uint GetMemberRefTreatmentAndRowId(MemberReferenceHandle handle) |
| | 0 | 1262 | | { |
| | | 1263 | | // PERF: This code pattern is JIT friendly and results in very efficient code. |
| | 0 | 1264 | | if (_metadataKind == MetadataKind.Ecma335) |
| | 0 | 1265 | | { |
| | 0 | 1266 | | return (uint)handle.RowId; |
| | | 1267 | | } |
| | | 1268 | | |
| | 0 | 1269 | | return CalculateMemberRefTreatmentAndRowId(handle); |
| | 0 | 1270 | | } |
| | | 1271 | | |
| | | 1272 | | public MethodSpecification GetMethodSpecification(MethodSpecificationHandle handle) |
| | 0 | 1273 | | { |
| | 0 | 1274 | | return new MethodSpecification(this, handle); |
| | 0 | 1275 | | } |
| | | 1276 | | |
| | | 1277 | | public Parameter GetParameter(ParameterHandle handle) |
| | 0 | 1278 | | { |
| | 0 | 1279 | | return new Parameter(this, handle); |
| | 0 | 1280 | | } |
| | | 1281 | | |
| | | 1282 | | public GenericParameter GetGenericParameter(GenericParameterHandle handle) |
| | 0 | 1283 | | { |
| | 0 | 1284 | | return new GenericParameter(this, handle); |
| | 0 | 1285 | | } |
| | | 1286 | | |
| | | 1287 | | public GenericParameterConstraint GetGenericParameterConstraint(GenericParameterConstraintHandle handle) |
| | 0 | 1288 | | { |
| | 0 | 1289 | | return new GenericParameterConstraint(this, handle); |
| | 0 | 1290 | | } |
| | | 1291 | | |
| | | 1292 | | public ManifestResource GetManifestResource(ManifestResourceHandle handle) |
| | 0 | 1293 | | { |
| | 0 | 1294 | | return new ManifestResource(this, handle); |
| | 0 | 1295 | | } |
| | | 1296 | | |
| | | 1297 | | public AssemblyFile GetAssemblyFile(AssemblyFileHandle handle) |
| | 0 | 1298 | | { |
| | 0 | 1299 | | return new AssemblyFile(this, handle); |
| | 0 | 1300 | | } |
| | | 1301 | | |
| | | 1302 | | public StandaloneSignature GetStandaloneSignature(StandaloneSignatureHandle handle) |
| | 0 | 1303 | | { |
| | 0 | 1304 | | return new StandaloneSignature(this, handle); |
| | 0 | 1305 | | } |
| | | 1306 | | |
| | | 1307 | | public TypeSpecification GetTypeSpecification(TypeSpecificationHandle handle) |
| | 0 | 1308 | | { |
| | 0 | 1309 | | return new TypeSpecification(this, handle); |
| | 0 | 1310 | | } |
| | | 1311 | | |
| | | 1312 | | public ModuleReference GetModuleReference(ModuleReferenceHandle handle) |
| | 0 | 1313 | | { |
| | 0 | 1314 | | return new ModuleReference(this, handle); |
| | 0 | 1315 | | } |
| | | 1316 | | |
| | | 1317 | | public InterfaceImplementation GetInterfaceImplementation(InterfaceImplementationHandle handle) |
| | 0 | 1318 | | { |
| | 0 | 1319 | | return new InterfaceImplementation(this, handle); |
| | 0 | 1320 | | } |
| | | 1321 | | |
| | | 1322 | | internal TypeDefinitionHandle GetDeclaringType(MethodDefinitionHandle methodDef) |
| | 0 | 1323 | | { |
| | | 1324 | | int methodRowId; |
| | 0 | 1325 | | if (UseMethodPtrTable) |
| | 0 | 1326 | | { |
| | 0 | 1327 | | methodRowId = MethodPtrTable.GetRowIdForMethodDefRow(methodDef.RowId); |
| | 0 | 1328 | | } |
| | | 1329 | | else |
| | 0 | 1330 | | { |
| | 0 | 1331 | | methodRowId = methodDef.RowId; |
| | 0 | 1332 | | } |
| | | 1333 | | |
| | 0 | 1334 | | return TypeDefTable.FindTypeContainingMethod(methodRowId, MethodDefTable.NumberOfRows); |
| | 0 | 1335 | | } |
| | | 1336 | | |
| | | 1337 | | internal TypeDefinitionHandle GetDeclaringType(FieldDefinitionHandle fieldDef) |
| | 0 | 1338 | | { |
| | | 1339 | | int fieldRowId; |
| | 0 | 1340 | | if (UseFieldPtrTable) |
| | 0 | 1341 | | { |
| | 0 | 1342 | | fieldRowId = FieldPtrTable.GetRowIdForFieldDefRow(fieldDef.RowId); |
| | 0 | 1343 | | } |
| | | 1344 | | else |
| | 0 | 1345 | | { |
| | 0 | 1346 | | fieldRowId = fieldDef.RowId; |
| | 0 | 1347 | | } |
| | | 1348 | | |
| | 0 | 1349 | | return TypeDefTable.FindTypeContainingField(fieldRowId, FieldTable.NumberOfRows); |
| | 0 | 1350 | | } |
| | | 1351 | | |
| | | 1352 | | internal TypeDefinitionHandle GetDeclaringType(EventDefinitionHandle eventDef) |
| | 0 | 1353 | | { |
| | 0 | 1354 | | if (UseEventPtrTable) |
| | 0 | 1355 | | { |
| | 0 | 1356 | | eventDef = EventPtrTable.GetEventFor(eventDef.RowId); |
| | 0 | 1357 | | } |
| | | 1358 | | |
| | 0 | 1359 | | return EventMapTable.FindTypeContainingEvent(eventDef.RowId, EventTable.NumberOfRows); |
| | 0 | 1360 | | } |
| | | 1361 | | |
| | | 1362 | | internal TypeDefinitionHandle GetDeclaringType(PropertyDefinitionHandle propertyDef) |
| | 0 | 1363 | | { |
| | 0 | 1364 | | if (UsePropertyPtrTable) |
| | 0 | 1365 | | { |
| | 0 | 1366 | | propertyDef = PropertyPtrTable.GetPropertyFor(propertyDef.RowId); |
| | 0 | 1367 | | } |
| | | 1368 | | |
| | 0 | 1369 | | return PropertyMapTable.FindTypeContainingProperty(propertyDef.RowId, PropertyTable.NumberOfRows); |
| | 0 | 1370 | | } |
| | | 1371 | | |
| | | 1372 | | public string GetString(DocumentNameBlobHandle handle) |
| | 0 | 1373 | | { |
| | 0 | 1374 | | return BlobHeap.GetDocumentName(handle); |
| | 0 | 1375 | | } |
| | | 1376 | | |
| | | 1377 | | public Document GetDocument(DocumentHandle handle) |
| | 0 | 1378 | | { |
| | 0 | 1379 | | return new Document(this, handle); |
| | 0 | 1380 | | } |
| | | 1381 | | |
| | | 1382 | | public MethodDebugInformation GetMethodDebugInformation(MethodDebugInformationHandle handle) |
| | 0 | 1383 | | { |
| | 0 | 1384 | | return new MethodDebugInformation(this, handle); |
| | 0 | 1385 | | } |
| | | 1386 | | |
| | | 1387 | | public MethodDebugInformation GetMethodDebugInformation(MethodDefinitionHandle handle) |
| | 0 | 1388 | | { |
| | 0 | 1389 | | return new MethodDebugInformation(this, MethodDebugInformationHandle.FromRowId(handle.RowId)); |
| | 0 | 1390 | | } |
| | | 1391 | | |
| | | 1392 | | public LocalScope GetLocalScope(LocalScopeHandle handle) |
| | 0 | 1393 | | { |
| | 0 | 1394 | | return new LocalScope(this, handle); |
| | 0 | 1395 | | } |
| | | 1396 | | |
| | | 1397 | | public LocalVariable GetLocalVariable(LocalVariableHandle handle) |
| | 0 | 1398 | | { |
| | 0 | 1399 | | return new LocalVariable(this, handle); |
| | 0 | 1400 | | } |
| | | 1401 | | |
| | | 1402 | | public LocalConstant GetLocalConstant(LocalConstantHandle handle) |
| | 0 | 1403 | | { |
| | 0 | 1404 | | return new LocalConstant(this, handle); |
| | 0 | 1405 | | } |
| | | 1406 | | |
| | | 1407 | | public ImportScope GetImportScope(ImportScopeHandle handle) |
| | 0 | 1408 | | { |
| | 0 | 1409 | | return new ImportScope(this, handle); |
| | 0 | 1410 | | } |
| | | 1411 | | |
| | | 1412 | | public CustomDebugInformation GetCustomDebugInformation(CustomDebugInformationHandle handle) |
| | 0 | 1413 | | { |
| | 0 | 1414 | | return new CustomDebugInformation(this, handle); |
| | 0 | 1415 | | } |
| | | 1416 | | |
| | | 1417 | | public CustomDebugInformationHandleCollection GetCustomDebugInformation(EntityHandle handle) |
| | 0 | 1418 | | { |
| | 0 | 1419 | | return new CustomDebugInformationHandleCollection(this, handle); |
| | 0 | 1420 | | } |
| | | 1421 | | |
| | | 1422 | | public LocalScopeHandleCollection GetLocalScopes(MethodDefinitionHandle handle) |
| | 0 | 1423 | | { |
| | 0 | 1424 | | return new LocalScopeHandleCollection(this, handle.RowId); |
| | 0 | 1425 | | } |
| | | 1426 | | |
| | | 1427 | | public LocalScopeHandleCollection GetLocalScopes(MethodDebugInformationHandle handle) |
| | 0 | 1428 | | { |
| | 0 | 1429 | | return new LocalScopeHandleCollection(this, handle.RowId); |
| | 0 | 1430 | | } |
| | | 1431 | | |
| | | 1432 | | #endregion |
| | | 1433 | | |
| | | 1434 | | #region Nested Types |
| | | 1435 | | |
| | | 1436 | | private void InitializeNestedTypesMap() |
| | 0 | 1437 | | { |
| | 0 | 1438 | | var groupedNestedTypes = new Dictionary<TypeDefinitionHandle, ImmutableArray<TypeDefinitionHandle>.Builder>( |
| | | 1439 | | |
| | 0 | 1440 | | int numberOfNestedTypes = NestedClassTable.NumberOfRows; |
| | 0 | 1441 | | ImmutableArray<TypeDefinitionHandle>.Builder? builder = null; |
| | 0 | 1442 | | TypeDefinitionHandle previousEnclosingClass = default(TypeDefinitionHandle); |
| | | 1443 | | |
| | 0 | 1444 | | for (int i = 1; i <= numberOfNestedTypes; i++) |
| | 0 | 1445 | | { |
| | 0 | 1446 | | TypeDefinitionHandle enclosingClass = NestedClassTable.GetEnclosingClass(i); |
| | | 1447 | | |
| | 0 | 1448 | | Debug.Assert(!enclosingClass.IsNil); |
| | | 1449 | | |
| | 0 | 1450 | | if (enclosingClass != previousEnclosingClass) |
| | 0 | 1451 | | { |
| | 0 | 1452 | | if (!groupedNestedTypes.TryGetValue(enclosingClass, out builder)) |
| | 0 | 1453 | | { |
| | 0 | 1454 | | builder = ImmutableArray.CreateBuilder<TypeDefinitionHandle>(); |
| | 0 | 1455 | | groupedNestedTypes.Add(enclosingClass, builder); |
| | 0 | 1456 | | } |
| | | 1457 | | |
| | 0 | 1458 | | previousEnclosingClass = enclosingClass; |
| | 0 | 1459 | | } |
| | | 1460 | | else |
| | 0 | 1461 | | { |
| | 0 | 1462 | | Debug.Assert(builder == groupedNestedTypes[enclosingClass]); |
| | 0 | 1463 | | } |
| | | 1464 | | |
| | 0 | 1465 | | builder.Add(NestedClassTable.GetNestedClass(i)); |
| | 0 | 1466 | | } |
| | | 1467 | | |
| | 0 | 1468 | | var nestedTypesMap = new Dictionary<TypeDefinitionHandle, ImmutableArray<TypeDefinitionHandle>>(); |
| | 0 | 1469 | | foreach (var group in groupedNestedTypes) |
| | 0 | 1470 | | { |
| | 0 | 1471 | | nestedTypesMap.Add(group.Key, group.Value.ToImmutable()); |
| | 0 | 1472 | | } |
| | | 1473 | | |
| | 0 | 1474 | | _lazyNestedTypesMap = nestedTypesMap; |
| | 0 | 1475 | | } |
| | | 1476 | | |
| | | 1477 | | /// <summary> |
| | | 1478 | | /// Returns an array of types nested in the specified type. |
| | | 1479 | | /// </summary> |
| | | 1480 | | internal ImmutableArray<TypeDefinitionHandle> GetNestedTypes(TypeDefinitionHandle typeDef) |
| | 0 | 1481 | | { |
| | 0 | 1482 | | if (_lazyNestedTypesMap == null) |
| | 0 | 1483 | | { |
| | 0 | 1484 | | InitializeNestedTypesMap(); |
| | 0 | 1485 | | Debug.Assert(_lazyNestedTypesMap != null); |
| | 0 | 1486 | | } |
| | | 1487 | | |
| | | 1488 | | ImmutableArray<TypeDefinitionHandle> nestedTypes; |
| | 0 | 1489 | | if (_lazyNestedTypesMap.TryGetValue(typeDef, out nestedTypes)) |
| | 0 | 1490 | | { |
| | 0 | 1491 | | return nestedTypes; |
| | | 1492 | | } |
| | | 1493 | | |
| | 0 | 1494 | | return ImmutableArray<TypeDefinitionHandle>.Empty; |
| | 0 | 1495 | | } |
| | | 1496 | | #endregion |
| | | 1497 | | } |
| | | 1498 | | } |
| | | 1499 | | |