// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
using dnlib.IO;
using dnlib.PE;
namespace dnlib.DotNet.MD {
///
/// Common base class for #~ and #- metadata classes
///
abstract class MetadataBase : Metadata {
///
/// The PE image
///
protected IPEImage peImage;
///
/// The .NET header
///
protected ImageCor20Header cor20Header;
///
/// The MD header
///
protected MetadataHeader mdHeader;
///
/// The #Strings stream
///
protected StringsStream stringsStream;
///
/// The #US stream
///
protected USStream usStream;
///
/// The #Blob stream
///
protected BlobStream blobStream;
///
/// The #GUID stream
///
protected GuidStream guidStream;
///
/// The #~ or #- stream
///
protected TablesStream tablesStream;
///
/// The #Pdb stream
///
protected PdbStream pdbStream;
///
/// All the streams that are present in the PE image
///
protected IList allStreams;
public override bool IsStandalonePortablePdb => isStandalonePortablePdb;
/// true if this is standalone Portable PDB metadata
protected readonly bool isStandalonePortablePdb;
uint[] fieldRidToTypeDefRid;
uint[] methodRidToTypeDefRid;
uint[] eventRidToTypeDefRid;
uint[] propertyRidToTypeDefRid;
uint[] gpRidToOwnerRid;
uint[] gpcRidToOwnerRid;
uint[] paramRidToOwnerRid;
Dictionary> typeDefRidToNestedClasses;
StrongBox nonNestedTypes;
DataReaderFactory mdReaderFactoryToDisposeLater;
///
/// Sorts a table by key column
///
protected sealed class SortedTable {
RowInfo[] rows;
///
/// Remembers rid and key
///
[DebuggerDisplay("{rid} {key}")]
readonly struct RowInfo : IComparable {
public readonly uint rid;
public readonly uint key;
///
/// Constructor
///
/// Row ID
/// Key
public RowInfo(uint rid, uint key) {
this.rid = rid;
this.key = key;
}
public int CompareTo(RowInfo other) {
if (key < other.key)
return -1;
if (key > other.key)
return 1;
return rid.CompareTo(other.rid);
}
}
///
/// Constructor
///
/// The MD table
/// Index of key column
public SortedTable(MDTable mdTable, int keyColIndex) {
InitializeKeys(mdTable, keyColIndex);
Array.Sort(rows);
}
void InitializeKeys(MDTable mdTable, int keyColIndex) {
var keyColumn = mdTable.TableInfo.Columns[keyColIndex];
Debug.Assert(keyColumn.Size == 2 || keyColumn.Size == 4);
rows = new RowInfo[mdTable.Rows + 1];
if (mdTable.Rows == 0)
return;
var reader = mdTable.DataReader;
reader.Position = (uint)keyColumn.Offset;
uint increment = (uint)(mdTable.TableInfo.RowSize - keyColumn.Size);
for (uint i = 1; i <= mdTable.Rows; i++) {
rows[i] = new RowInfo(i, keyColumn.Unsafe_Read24(ref reader));
if (i < mdTable.Rows)
reader.Position += increment;
}
}
///
/// Binary searches for a row with a certain key
///
/// The key
/// The row or 0 if not found
int BinarySearch(uint key) {
int lo = 1, hi = rows.Length - 1;
while (lo <= hi && hi != -1) {
int curr = (lo + hi) / 2;
uint key2 = rows[curr].key;
if (key == key2)
return curr;
if (key2 > key)
hi = curr - 1;
else
lo = curr + 1;
}
return 0;
}
///
/// Find all rids that contain
///
/// The key
/// A new instance
public RidList FindAllRows(uint key) {
int startIndex = BinarySearch(key);
if (startIndex == 0)
return RidList.Empty;
int endIndex = startIndex + 1;
for (; startIndex > 1; startIndex--) {
if (key != rows[startIndex - 1].key)
break;
}
for (; endIndex < rows.Length; endIndex++) {
if (key != rows[endIndex].key)
break;
}
var list = new List(endIndex - startIndex);
for (int i = startIndex; i < endIndex; i++)
list.Add(rows[i].rid);
return RidList.Create(list);
}
}
SortedTable eventMapSortedTable;
SortedTable propertyMapSortedTable;
public override ImageCor20Header ImageCor20Header => cor20Header;
public override uint Version => ((uint)mdHeader.MajorVersion << 16) | mdHeader.MinorVersion;
public override string VersionString => mdHeader.VersionString;
public override IPEImage PEImage => peImage;
public override MetadataHeader MetadataHeader => mdHeader;
public override StringsStream StringsStream => stringsStream;
public override USStream USStream => usStream;
public override BlobStream BlobStream => blobStream;
public override GuidStream GuidStream => guidStream;
public override TablesStream TablesStream => tablesStream;
public override PdbStream PdbStream => pdbStream;
public override IList AllStreams => allStreams;
///
/// Constructor
///
/// The PE image
/// The .NET header
/// The MD header
protected MetadataBase(IPEImage peImage, ImageCor20Header cor20Header, MetadataHeader mdHeader) {
try {
allStreams = new List();
this.peImage = peImage;
this.cor20Header = cor20Header;
this.mdHeader = mdHeader;
isStandalonePortablePdb = false;
}
catch {
if (peImage is not null)
peImage.Dispose();
throw;
}
}
internal MetadataBase(MetadataHeader mdHeader, bool isStandalonePortablePdb) {
allStreams = new List();
peImage = null;
cor20Header = null;
this.mdHeader = mdHeader;
this.isStandalonePortablePdb = isStandalonePortablePdb;
}
///
/// Initializes the metadata, tables, streams
///
public void Initialize(DataReaderFactory mdReaderFactory) {
mdReaderFactoryToDisposeLater = mdReaderFactory;
uint metadataBaseOffset;
if (peImage is not null) {
Debug.Assert(mdReaderFactory is null);
Debug.Assert(cor20Header is not null);
metadataBaseOffset = (uint)peImage.ToFileOffset(cor20Header.Metadata.VirtualAddress);
mdReaderFactory = peImage.DataReaderFactory;
}
else {
Debug.Assert(mdReaderFactory is not null);
metadataBaseOffset = 0;
}
InitializeInternal(mdReaderFactory, metadataBaseOffset);
if (tablesStream is null)
throw new BadImageFormatException("Missing MD stream");
if (isStandalonePortablePdb && pdbStream is null)
throw new BadImageFormatException("Missing #Pdb stream");
InitializeNonExistentHeaps();
}
///
/// Creates empty heap objects if they're not present in the metadata
///
protected void InitializeNonExistentHeaps() {
if (stringsStream is null)
stringsStream = new StringsStream();
if (usStream is null)
usStream = new USStream();
if (blobStream is null)
blobStream = new BlobStream();
if (guidStream is null)
guidStream = new GuidStream();
}
///
/// Called by
///
protected abstract void InitializeInternal(DataReaderFactory mdReaderFactory, uint metadataBaseOffset);
public override RidList GetTypeDefRidList() => RidList.Create(1, tablesStream.TypeDefTable.Rows);
public override RidList GetExportedTypeRidList() => RidList.Create(1, tablesStream.ExportedTypeTable.Rows);
///
/// Binary searches the table for a rid whose key column at index
/// is equal to .
///
/// Table to search
/// Key column index
/// Key
/// The rid of the found row, or 0 if none found
protected abstract uint BinarySearch(MDTable tableSource, int keyColIndex, uint key);
///
/// Finds all rows owned by in table
/// whose index is
///
/// Table to search
/// Key column index
/// Key
/// A instance
protected RidList FindAllRows(MDTable tableSource, int keyColIndex, uint key) {
uint startRid = BinarySearch(tableSource, keyColIndex, key);
if (tableSource.IsInvalidRID(startRid))
return RidList.Empty;
uint endRid = startRid + 1;
var column = tableSource.TableInfo.Columns[keyColIndex];
for (; startRid > 1; startRid--) {
if (!tablesStream.TryReadColumn24(tableSource, startRid - 1, column, out uint key2))
break; // Should never happen since startRid is valid
if (key != key2)
break;
}
for (; endRid <= tableSource.Rows; endRid++) {
if (!tablesStream.TryReadColumn24(tableSource, endRid, column, out uint key2))
break; // Should never happen since endRid is valid
if (key != key2)
break;
}
return RidList.Create(startRid, endRid - startRid);
}
///
/// Finds all rows owned by in table
/// whose index is . Should be called if
/// could be unsorted.
///
/// Table to search
/// Key column index
/// Key
/// A instance
protected virtual RidList FindAllRowsUnsorted(MDTable tableSource, int keyColIndex, uint key) => FindAllRows(tableSource, keyColIndex, key);
public override RidList GetInterfaceImplRidList(uint typeDefRid) => FindAllRowsUnsorted(tablesStream.InterfaceImplTable, 0, typeDefRid);
public override RidList GetGenericParamRidList(Table table, uint rid) {
if (!CodedToken.TypeOrMethodDef.Encode(new MDToken(table, rid), out uint codedToken))
return RidList.Empty;
return FindAllRowsUnsorted(tablesStream.GenericParamTable, 2, codedToken);
}
public override RidList GetGenericParamConstraintRidList(uint genericParamRid) =>
FindAllRowsUnsorted(tablesStream.GenericParamConstraintTable, 0, genericParamRid);
public override RidList GetCustomAttributeRidList(Table table, uint rid) {
if (!CodedToken.HasCustomAttribute.Encode(new MDToken(table, rid), out uint codedToken))
return RidList.Empty;
return FindAllRowsUnsorted(tablesStream.CustomAttributeTable, 0, codedToken);
}
public override RidList GetDeclSecurityRidList(Table table, uint rid) {
if (!CodedToken.HasDeclSecurity.Encode(new MDToken(table, rid), out uint codedToken))
return RidList.Empty;
return FindAllRowsUnsorted(tablesStream.DeclSecurityTable, 1, codedToken);
}
public override RidList GetMethodSemanticsRidList(Table table, uint rid) {
if (!CodedToken.HasSemantic.Encode(new MDToken(table, rid), out uint codedToken))
return RidList.Empty;
return FindAllRowsUnsorted(tablesStream.MethodSemanticsTable, 2, codedToken);
}
public override RidList GetMethodImplRidList(uint typeDefRid) => FindAllRowsUnsorted(tablesStream.MethodImplTable, 0, typeDefRid);
public override uint GetClassLayoutRid(uint typeDefRid) {
var list = FindAllRowsUnsorted(tablesStream.ClassLayoutTable, 2, typeDefRid);
return list.Count == 0 ? 0 : list[0];
}
public override uint GetFieldLayoutRid(uint fieldRid) {
var list = FindAllRowsUnsorted(tablesStream.FieldLayoutTable, 1, fieldRid);
return list.Count == 0 ? 0 : list[0];
}
public override uint GetFieldMarshalRid(Table table, uint rid) {
if (!CodedToken.HasFieldMarshal.Encode(new MDToken(table, rid), out uint codedToken))
return 0;
var list = FindAllRowsUnsorted(tablesStream.FieldMarshalTable, 0, codedToken);
return list.Count == 0 ? 0 : list[0];
}
public override uint GetFieldRVARid(uint fieldRid) {
var list = FindAllRowsUnsorted(tablesStream.FieldRVATable, 1, fieldRid);
return list.Count == 0 ? 0 : list[0];
}
public override uint GetImplMapRid(Table table, uint rid) {
if (!CodedToken.MemberForwarded.Encode(new MDToken(table, rid), out uint codedToken))
return 0;
var list = FindAllRowsUnsorted(tablesStream.ImplMapTable, 1, codedToken);
return list.Count == 0 ? 0 : list[0];
}
public override uint GetNestedClassRid(uint typeDefRid) {
var list = FindAllRowsUnsorted(tablesStream.NestedClassTable, 0, typeDefRid);
return list.Count == 0 ? 0 : list[0];
}
public override uint GetEventMapRid(uint typeDefRid) {
// The EventMap and PropertyMap tables can only be trusted to be sorted if it's
// an NGen image and it's the normal #- stream. The IsSorted bit must not be used
// to check whether the tables are sorted. See coreclr: md/inc/metamodel.h / IsVerified()
if (eventMapSortedTable is null)
Interlocked.CompareExchange(ref eventMapSortedTable, new SortedTable(tablesStream.EventMapTable, 0), null);
var list = eventMapSortedTable.FindAllRows(typeDefRid);
return list.Count == 0 ? 0 : list[0];
}
public override uint GetPropertyMapRid(uint typeDefRid) {
// Always unsorted, see comment in GetEventMapRid() above
if (propertyMapSortedTable is null)
Interlocked.CompareExchange(ref propertyMapSortedTable, new SortedTable(tablesStream.PropertyMapTable, 0), null);
var list = propertyMapSortedTable.FindAllRows(typeDefRid);
return list.Count == 0 ? 0 : list[0];
}
public override uint GetConstantRid(Table table, uint rid) {
if (!CodedToken.HasConstant.Encode(new MDToken(table, rid), out uint codedToken))
return 0;
var list = FindAllRowsUnsorted(tablesStream.ConstantTable, 2, codedToken);
return list.Count == 0 ? 0 : list[0];
}
public override uint GetOwnerTypeOfField(uint fieldRid) {
if (fieldRidToTypeDefRid is null)
InitializeInverseFieldOwnerRidList();
uint index = fieldRid - 1;
if (index >= fieldRidToTypeDefRid.LongLength)
return 0;
return fieldRidToTypeDefRid[index];
}
void InitializeInverseFieldOwnerRidList() {
if (fieldRidToTypeDefRid is not null)
return;
var newFieldRidToTypeDefRid = new uint[tablesStream.FieldTable.Rows];
var ownerList = GetTypeDefRidList();
for (int i = 0; i < ownerList.Count; i++) {
var ownerRid = ownerList[i];
var fieldList = GetFieldRidList(ownerRid);
for (int j = 0; j < fieldList.Count; j++) {
uint ridIndex = fieldList[j] - 1;
if (newFieldRidToTypeDefRid[ridIndex] != 0)
continue;
newFieldRidToTypeDefRid[ridIndex] = ownerRid;
}
}
Interlocked.CompareExchange(ref fieldRidToTypeDefRid, newFieldRidToTypeDefRid, null);
}
public override uint GetOwnerTypeOfMethod(uint methodRid) {
if (methodRidToTypeDefRid is null)
InitializeInverseMethodOwnerRidList();
uint index = methodRid - 1;
if (index >= methodRidToTypeDefRid.LongLength)
return 0;
return methodRidToTypeDefRid[index];
}
void InitializeInverseMethodOwnerRidList() {
if (methodRidToTypeDefRid is not null)
return;
var newMethodRidToTypeDefRid = new uint[tablesStream.MethodTable.Rows];
var ownerList = GetTypeDefRidList();
for (int i = 0; i < ownerList.Count; i++) {
var ownerRid = ownerList[i];
var methodList = GetMethodRidList(ownerRid);
for (int j = 0; j < methodList.Count; j++) {
uint ridIndex = methodList[j] - 1;
if (newMethodRidToTypeDefRid[ridIndex] != 0)
continue;
newMethodRidToTypeDefRid[ridIndex] = ownerRid;
}
}
Interlocked.CompareExchange(ref methodRidToTypeDefRid, newMethodRidToTypeDefRid, null);
}
public override uint GetOwnerTypeOfEvent(uint eventRid) {
if (eventRidToTypeDefRid is null)
InitializeInverseEventOwnerRidList();
uint index = eventRid - 1;
if (index >= eventRidToTypeDefRid.LongLength)
return 0;
return eventRidToTypeDefRid[index];
}
void InitializeInverseEventOwnerRidList() {
if (eventRidToTypeDefRid is not null)
return;
var newEventRidToTypeDefRid = new uint[tablesStream.EventTable.Rows];
var ownerList = GetTypeDefRidList();
for (int i = 0; i < ownerList.Count; i++) {
var ownerRid = ownerList[i];
var eventList = GetEventRidList(GetEventMapRid(ownerRid));
for (int j = 0; j < eventList.Count; j++) {
uint ridIndex = eventList[j] - 1;
if (newEventRidToTypeDefRid[ridIndex] != 0)
continue;
newEventRidToTypeDefRid[ridIndex] = ownerRid;
}
}
Interlocked.CompareExchange(ref eventRidToTypeDefRid, newEventRidToTypeDefRid, null);
}
public override uint GetOwnerTypeOfProperty(uint propertyRid) {
if (propertyRidToTypeDefRid is null)
InitializeInversePropertyOwnerRidList();
uint index = propertyRid - 1;
if (index >= propertyRidToTypeDefRid.LongLength)
return 0;
return propertyRidToTypeDefRid[index];
}
void InitializeInversePropertyOwnerRidList() {
if (propertyRidToTypeDefRid is not null)
return;
var newPropertyRidToTypeDefRid = new uint[tablesStream.PropertyTable.Rows];
var ownerList = GetTypeDefRidList();
for (int i = 0; i < ownerList.Count; i++) {
var ownerRid = ownerList[i];
var propertyList = GetPropertyRidList(GetPropertyMapRid(ownerRid));
for (int j = 0; j < propertyList.Count; j++) {
uint ridIndex = propertyList[j] - 1;
if (newPropertyRidToTypeDefRid[ridIndex] != 0)
continue;
newPropertyRidToTypeDefRid[ridIndex] = ownerRid;
}
}
Interlocked.CompareExchange(ref propertyRidToTypeDefRid, newPropertyRidToTypeDefRid, null);
}
public override uint GetOwnerOfGenericParam(uint gpRid) {
// Don't use GenericParam.Owner column. If the GP table is sorted, it's
// possible to have two blocks of GPs with the same owner. Only one of the
// blocks is the "real" generic params for the owner. Of course, rarely
// if ever will this occur, but could happen if some obfuscator has
// added it.
if (gpRidToOwnerRid is null)
InitializeInverseGenericParamOwnerRidList();
uint index = gpRid - 1;
if (index >= gpRidToOwnerRid.LongLength)
return 0;
return gpRidToOwnerRid[index];
}
void InitializeInverseGenericParamOwnerRidList() {
if (gpRidToOwnerRid is not null)
return;
var gpTable = tablesStream.GenericParamTable;
var newGpRidToOwnerRid = new uint[gpTable.Rows];
// Find all owners by reading the GenericParam.Owner column
var ownerCol = gpTable.TableInfo.Columns[2];
var ownersDict = new Dictionary();
for (uint rid = 1; rid <= gpTable.Rows; rid++) {
if (!tablesStream.TryReadColumn24(gpTable, rid, ownerCol, out uint owner))
continue;
ownersDict[owner] = true;
}
// Now that we have the owners, find all the generic params they own. An obfuscated
// module could have 2+ owners pointing to the same generic param row.
var owners = new List(ownersDict.Keys);
owners.Sort();
for (int i = 0; i < owners.Count; i++) {
if (!CodedToken.TypeOrMethodDef.Decode(owners[i], out uint ownerToken))
continue;
var ridList = GetGenericParamRidList(MDToken.ToTable(ownerToken), MDToken.ToRID(ownerToken));
for (int j = 0; j < ridList.Count; j++) {
uint ridIndex = ridList[j] - 1;
if (newGpRidToOwnerRid[ridIndex] != 0)
continue;
newGpRidToOwnerRid[ridIndex] = owners[i];
}
}
Interlocked.CompareExchange(ref gpRidToOwnerRid, newGpRidToOwnerRid, null);
}
public override uint GetOwnerOfGenericParamConstraint(uint gpcRid) {
// Don't use GenericParamConstraint.Owner column for the same reason
// as described in GetOwnerOfGenericParam().
if (gpcRidToOwnerRid is null)
InitializeInverseGenericParamConstraintOwnerRidList();
uint index = gpcRid - 1;
if (index >= gpcRidToOwnerRid.LongLength)
return 0;
return gpcRidToOwnerRid[index];
}
void InitializeInverseGenericParamConstraintOwnerRidList() {
if (gpcRidToOwnerRid is not null)
return;
var gpcTable = tablesStream.GenericParamConstraintTable;
var newGpcRidToOwnerRid = new uint[gpcTable.Rows];
var ownerCol = gpcTable.TableInfo.Columns[0];
var ownersDict = new Dictionary();
for (uint rid = 1; rid <= gpcTable.Rows; rid++) {
if (!tablesStream.TryReadColumn24(gpcTable, rid, ownerCol, out uint owner))
continue;
ownersDict[owner] = true;
}
var owners = new List(ownersDict.Keys);
owners.Sort();
for (int i = 0; i < owners.Count; i++) {
uint ownerToken = owners[i];
var ridList = GetGenericParamConstraintRidList(ownerToken);
for (int j = 0; j < ridList.Count; j++) {
uint ridIndex = ridList[j] - 1;
if (newGpcRidToOwnerRid[ridIndex] != 0)
continue;
newGpcRidToOwnerRid[ridIndex] = ownerToken;
}
}
Interlocked.CompareExchange(ref gpcRidToOwnerRid, newGpcRidToOwnerRid, null);
}
public override uint GetOwnerOfParam(uint paramRid) {
if (paramRidToOwnerRid is null)
InitializeInverseParamOwnerRidList();
uint index = paramRid - 1;
if (index >= paramRidToOwnerRid.LongLength)
return 0;
return paramRidToOwnerRid[index];
}
void InitializeInverseParamOwnerRidList() {
if (paramRidToOwnerRid is not null)
return;
var newParamRidToOwnerRid = new uint[tablesStream.ParamTable.Rows];
var table = tablesStream.MethodTable;
for (uint rid = 1; rid <= table.Rows; rid++) {
var ridList = GetParamRidList(rid);
for (int j = 0; j < ridList.Count; j++) {
uint ridIndex = ridList[j] - 1;
if (newParamRidToOwnerRid[ridIndex] != 0)
continue;
newParamRidToOwnerRid[ridIndex] = rid;
}
}
Interlocked.CompareExchange(ref paramRidToOwnerRid, newParamRidToOwnerRid, null);
}
public override RidList GetNestedClassRidList(uint typeDefRid) {
if (typeDefRidToNestedClasses is null)
InitializeNestedClassesDictionary();
if (typeDefRidToNestedClasses.TryGetValue(typeDefRid, out var ridList))
return RidList.Create(ridList);
return RidList.Empty;
}
void InitializeNestedClassesDictionary() {
var table = tablesStream.NestedClassTable;
var destTable = tablesStream.TypeDefTable;
Dictionary validTypeDefRids = null;
var typeDefRidList = GetTypeDefRidList();
if ((uint)typeDefRidList.Count != destTable.Rows) {
validTypeDefRids = new Dictionary(typeDefRidList.Count);
for (int i = 0; i < typeDefRidList.Count; i++)
validTypeDefRids[typeDefRidList[i]] = true;
}
var nestedRidsDict = new Dictionary((int)table.Rows);
var nestedRids = new List((int)table.Rows); // Need it so we add the rids in correct order
for (uint rid = 1; rid <= table.Rows; rid++) {
if (validTypeDefRids is not null && !validTypeDefRids.ContainsKey(rid))
continue;
if (!tablesStream.TryReadNestedClassRow(rid, out var row))
continue; // Should never happen since rid is valid
if (!destTable.IsValidRID(row.NestedClass) || !destTable.IsValidRID(row.EnclosingClass))
continue;
if (nestedRidsDict.ContainsKey(row.NestedClass))
continue;
nestedRidsDict[row.NestedClass] = true;
nestedRids.Add(row.NestedClass);
}
var newTypeDefRidToNestedClasses = new Dictionary>();
int count = nestedRids.Count;
for (int i = 0; i < count; i++) {
var nestedRid = nestedRids[i];
if (!tablesStream.TryReadNestedClassRow(GetNestedClassRid(nestedRid), out var row))
continue;
if (!newTypeDefRidToNestedClasses.TryGetValue(row.EnclosingClass, out var ridList))
newTypeDefRidToNestedClasses[row.EnclosingClass] = ridList = new List();
ridList.Add(nestedRid);
}
var newNonNestedTypes = new List((int)(destTable.Rows - nestedRidsDict.Count));
for (uint rid = 1; rid <= destTable.Rows; rid++) {
if (validTypeDefRids is not null && !validTypeDefRids.ContainsKey(rid))
continue;
if (nestedRidsDict.ContainsKey(rid))
continue;
newNonNestedTypes.Add(rid);
}
Interlocked.CompareExchange(ref nonNestedTypes, new StrongBox(RidList.Create(newNonNestedTypes)), null);
// Initialize this one last since it's tested by the callers of this method
Interlocked.CompareExchange(ref typeDefRidToNestedClasses, newTypeDefRidToNestedClasses, null);
}
public override RidList GetNonNestedClassRidList() {
// Check typeDefRidToNestedClasses and not nonNestedTypes since
// InitializeNestedClassesDictionary() writes to typeDefRidToNestedClasses last.
if (typeDefRidToNestedClasses is null)
InitializeNestedClassesDictionary();
return nonNestedTypes.Value;
}
public override RidList GetLocalScopeRidList(uint methodRid) => FindAllRows(tablesStream.LocalScopeTable, 0, methodRid);
public override uint GetStateMachineMethodRid(uint methodRid) {
var list = FindAllRows(tablesStream.StateMachineMethodTable, 0, methodRid);
return list.Count == 0 ? 0 : list[0];
}
public override RidList GetCustomDebugInformationRidList(Table table, uint rid) {
if (!CodedToken.HasCustomDebugInformation.Encode(new MDToken(table, rid), out uint codedToken))
return RidList.Empty;
return FindAllRows(tablesStream.CustomDebugInformationTable, 0, codedToken);
}
public override void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Dispose method
///
/// true if called by
protected virtual void Dispose(bool disposing) {
if (!disposing)
return;
peImage?.Dispose();
stringsStream?.Dispose();
usStream?.Dispose();
blobStream?.Dispose();
guidStream?.Dispose();
tablesStream?.Dispose();
var as2 = allStreams;
if (as2 is not null) {
foreach (var stream in as2)
stream?.Dispose();
}
mdReaderFactoryToDisposeLater?.Dispose();
peImage = null;
cor20Header = null;
mdHeader = null;
stringsStream = null;
usStream = null;
blobStream = null;
guidStream = null;
tablesStream = null;
allStreams = null;
fieldRidToTypeDefRid = null;
methodRidToTypeDefRid = null;
typeDefRidToNestedClasses = null;
mdReaderFactoryToDisposeLater = null;
}
}
}