// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using dnlib.DotNet.MD;
namespace dnlib.DotNet.Writer {
///
/// Preserves metadata tokens
///
sealed class PreserveTokensMetadata : Metadata {
readonly ModuleDefMD mod;
readonly Rows typeRefInfos = new Rows();
readonly Dictionary typeToRid = new Dictionary();
MemberDefDict fieldDefInfos;
MemberDefDict methodDefInfos;
MemberDefDict paramDefInfos;
readonly Rows memberRefInfos = new Rows();
readonly Rows standAloneSigInfos = new Rows();
MemberDefDict eventDefInfos;
MemberDefDict propertyDefInfos;
readonly Rows typeSpecInfos = new Rows();
readonly Rows methodSpecInfos = new Rows();
readonly Dictionary callConvTokenToSignature = new Dictionary();
[DebuggerDisplay("{Rid} -> {NewRid} {Def}")]
sealed class MemberDefInfo where T : IMDTokenProvider {
public readonly T Def;
///
/// Its real rid
///
public uint Rid;
///
/// Its logical rid or real rid. If the ptr table exists (eg. MethodPtr), then it's
/// an index into it, else it's the real rid.
///
public uint NewRid;
public MemberDefInfo(T def, uint rid) {
Def = def;
Rid = rid;
NewRid = rid;
}
}
[DebuggerDisplay("Count = {Count}")]
sealed class MemberDefDict where T : IMDTokenProvider {
readonly Type defMDType;
uint userRid = 0x01000000;
uint newRid = 1;
int numDefMDs;
int numDefUsers;
int tableSize;
bool wasSorted;
readonly bool preserveRids;
readonly bool enableRidToInfo;
readonly Dictionary> defToInfo = new Dictionary>();
Dictionary> ridToInfo;
readonly List> defs = new List>();
List> sortedDefs;
readonly Dictionary collectionPositions = new Dictionary();
///
/// Gets total number of defs in the list. It does not necessarily return
/// the table size. Use for that.
///
public int Count => defs.Count;
///
/// Gets the number of rows that need to be created in the table
///
public int TableSize => tableSize;
///
/// Returns true if the ptr table (eg. MethodPtr) is needed
///
public bool NeedPtrTable => preserveRids && !wasSorted;
public MemberDefDict(Type defMDType, bool preserveRids)
: this(defMDType, preserveRids, false) {
}
public MemberDefDict(Type defMDType, bool preserveRids, bool enableRidToInfo) {
this.defMDType = defMDType;
this.preserveRids = preserveRids;
this.enableRidToInfo = enableRidToInfo;
}
public uint Rid(T def) => defToInfo[def].Rid;
public bool TryGetRid(T def, out uint rid) {
if (def == null || !defToInfo.TryGetValue(def, out var info)) {
rid = 0;
return false;
}
rid = info.Rid;
return true;
}
///
/// Sorts the table
///
/// Comparer
public void Sort(Comparison> comparer) {
if (!preserveRids) {
// It's already sorted
sortedDefs = defs;
return;
}
sortedDefs = new List>(defs);
sortedDefs.Sort(comparer);
wasSorted = true;
for (int i = 0; i < sortedDefs.Count; i++) {
var def = sortedDefs[i];
uint newRid = (uint)i + 1;
def.NewRid = newRid;
if (def.Rid != newRid)
wasSorted = false;
}
}
public MemberDefInfo Get(int i) => defs[i];
public MemberDefInfo GetSorted(int i) => sortedDefs[i];
public MemberDefInfo GetByRid(uint rid) {
ridToInfo.TryGetValue(rid, out var info);
return info;
}
///
/// Adds a def. must be called after adding the last def.
///
/// The def
/// Collection position
public void Add(T def, int collPos) {
uint rid;
if (def.GetType() == defMDType) {
numDefMDs++;
rid = preserveRids ? def.Rid : newRid++;
}
else {
numDefUsers++;
rid = preserveRids ? userRid++ : newRid++;
}
var info = new MemberDefInfo(def, rid);
defToInfo[def] = info;
defs.Add(info);
collectionPositions.Add(def, collPos);
}
///
/// Must be called after 'ing the last def
///
public void SortDefs() {
// It's already sorted if we don't preserve rids
if (preserveRids) {
// Sort all def MDs before user defs
defs.Sort((a, b) => a.Rid.CompareTo(b.Rid));
// Fix user created defs' rids
uint newRid = numDefMDs == 0 ? 1 : defs[numDefMDs - 1].Rid + 1;
for (int i = numDefMDs; i < defs.Count; i++)
defs[i].Rid = newRid++;
// Now we know total table size
tableSize = (int)newRid - 1;
}
else
tableSize = defs.Count;
if (enableRidToInfo) {
ridToInfo = new Dictionary>(defs.Count);
foreach (var info in defs)
ridToInfo.Add(info.Rid, info);
}
if ((uint)tableSize > 0x00FFFFFF)
throw new ModuleWriterException("Table is too big");
}
public int GetCollectionPosition(T def) => collectionPositions[def];
}
protected override int NumberOfMethods => methodDefInfos.Count;
public PreserveTokensMetadata(ModuleDef module, UniqueChunkList constants, MethodBodyChunks methodBodies, NetResources netResources, MetadataOptions options, DebugMetadataKind debugKind, bool isStandaloneDebugMetadata)
: base(module, constants, methodBodies, netResources, options, debugKind, isStandaloneDebugMetadata) {
mod = module as ModuleDefMD;
if (mod is null)
throw new ModuleWriterException("Not a ModuleDefMD");
}
///
public override uint GetRid(TypeRef tr) {
typeRefInfos.TryGetRid(tr, out uint rid);
return rid;
}
///
public override uint GetRid(TypeDef td) {
if (td is null) {
Error("TypeDef is null");
return 0;
}
if (typeToRid.TryGetValue(td, out uint rid))
return rid;
Error("TypeDef '{0}' (0x{1:X8}) is not defined in this module '{2}'. A type was removed that is still referenced by this module.", td, td.MDToken.Raw, module);
return 0;
}
///
public override uint GetRid(FieldDef fd) {
if (fieldDefInfos.TryGetRid(fd, out uint rid))
return rid;
if (fd is null)
Error("Field is null");
else
Error("Field '{0}' (0x{1:X8}) is not defined in this module '{2}'. A field was removed that is still referenced by this module.", fd, fd.MDToken.Raw, module);
return 0;
}
///
public override uint GetRid(MethodDef md) {
if (methodDefInfos.TryGetRid(md, out uint rid))
return rid;
if (md is null)
Error("Method is null");
else
Error("Method '{0}' (0x{1:X8}) is not defined in this module '{2}'. A method was removed that is still referenced by this module.", md, md.MDToken.Raw, module);
return 0;
}
///
public override uint GetRid(ParamDef pd) {
if (paramDefInfos.TryGetRid(pd, out uint rid))
return rid;
if (pd is null)
Error("Param is null");
else
Error("Param '{0}' (0x{1:X8}) is not defined in this module '{2}'. A parameter was removed that is still referenced by this module.", pd, pd.MDToken.Raw, module);
return 0;
}
///
public override uint GetRid(MemberRef mr) {
memberRefInfos.TryGetRid(mr, out uint rid);
return rid;
}
///
public override uint GetRid(StandAloneSig sas) {
standAloneSigInfos.TryGetRid(sas, out uint rid);
return rid;
}
///
public override uint GetRid(EventDef ed) {
if (eventDefInfos.TryGetRid(ed, out uint rid))
return rid;
if (ed is null)
Error("Event is null");
else
Error("Event '{0}' (0x{1:X8}) is not defined in this module '{2}'. An event was removed that is still referenced by this module.", ed, ed.MDToken.Raw, module);
return 0;
}
///
public override uint GetRid(PropertyDef pd) {
if (propertyDefInfos.TryGetRid(pd, out uint rid))
return rid;
if (pd is null)
Error("Property is null");
else
Error("Property '{0}' (0x{1:X8}) is not defined in this module '{2}'. A property was removed that is still referenced by this module.", pd, pd.MDToken.Raw, module);
return 0;
}
///
public override uint GetRid(TypeSpec ts) {
typeSpecInfos.TryGetRid(ts, out uint rid);
return rid;
}
///
public override uint GetRid(MethodSpec ms) {
methodSpecInfos.TryGetRid(ms, out uint rid);
return rid;
}
///
protected override void Initialize() {
fieldDefInfos = new MemberDefDict(typeof(FieldDefMD), PreserveFieldRids);
methodDefInfos = new MemberDefDict(typeof(MethodDefMD), PreserveMethodRids, true);
paramDefInfos = new MemberDefDict(typeof(ParamDefMD), PreserveParamRids);
eventDefInfos = new MemberDefDict(typeof(EventDefMD), PreserveEventRids);
propertyDefInfos = new MemberDefDict(typeof(PropertyDefMD), PreservePropertyRids);
CreateEmptyTableRows();
}
///
protected override TypeDef[] GetAllTypeDefs() {
if (!PreserveTypeDefRids) {
var types2 = module.GetTypes().ToArray();
InitializeTypeToRid(types2);
return types2;
}
var typeToIndex = new Dictionary();
var types = new List();
uint index = 0;
const uint IS_TYPEDEFMD = 0x80000000;
const uint INDEX_BITS = 0x00FFFFFF;
foreach (var type in module.GetTypes()) {
if (type is null)
continue;
types.Add(type);
uint val = (uint)index++;
if (type.GetType() == typeof(TypeDefMD))
val |= IS_TYPEDEFMD;
typeToIndex[type] = val;
}
var globalType = types[0];
types.Sort((a, b) => {
if (a == b)
return 0;
// Make sure the global type is always sorted first, even if it's
// a TypeDefUser
if (a == globalType)
return -1;
if (b == globalType)
return 1;
// Sort all TypeDefMDs before all TypeDefUsers
uint ai = typeToIndex[a];
uint bi = typeToIndex[b];
bool amd = (ai & IS_TYPEDEFMD) != 0;
bool bmd = (bi & IS_TYPEDEFMD) != 0;
if (amd == bmd) { // Both are TypeDefMDs or both are TypeDefUsers
// If TypeDefMDs, only compare rids since rids are preserved
if (amd)
return a.Rid.CompareTo(b.Rid);
// If TypeDefUsers, rids aren't preserved so compare by index
return (ai & INDEX_BITS).CompareTo(bi & INDEX_BITS);
}
if (amd)
return -1;
return 1;
});
// Some of the original types may have been removed. Create dummy types
// so TypeDef rids can be preserved.
var newTypes = new List(types.Count);
uint prevRid = 1;
newTypes.Add(globalType);
for (int i = 1; i < types.Count; i++) {
var type = types[i];
// TypeDefUsers were sorted last so when we reach one, we can stop
if (type.GetType() != typeof(TypeDefMD)) {
while (i < types.Count)
newTypes.Add(types[i++]);
break;
}
uint currRid = type.Rid;
int extraTypes = (int)(currRid - prevRid - 1);
if (extraTypes != 0) { // always >= 0 since currRid > prevRid
// At least one type has been removed. Create dummy types.
for (int j = 0; j < extraTypes; j++)
newTypes.Add(new TypeDefUser("dummy", Guid.NewGuid().ToString("B"), module.CorLibTypes.Object.TypeDefOrRef));
}
newTypes.Add(type);
prevRid = currRid;
}
var newTypesArray = newTypes.ToArray();
InitializeTypeToRid(newTypesArray);
return newTypesArray;
}
void InitializeTypeToRid(TypeDef[] types) {
uint rid = 1;
foreach (var type in types) {
if (type is null)
continue;
if (typeToRid.ContainsKey(type))
continue;
typeToRid[type] = rid++;
}
}
///
protected override void AllocateTypeDefRids() {
foreach (var type in allTypeDefs) {
uint rid = tablesHeap.TypeDefTable.Create(new RawTypeDefRow());
if (typeToRid[type] != rid)
throw new ModuleWriterException("Got a different rid than expected");
}
}
///
/// Reserves rows in TypeRef, MemberRef, StandAloneSig,
/// TypeSpec and MethodSpec where we will store the original rows
/// to make sure they get the same rid. Any user created rows will be stored at
/// the end of each table.
///
void CreateEmptyTableRows() {
uint rows;
if (PreserveTypeRefRids) {
rows = mod.TablesStream.TypeRefTable.Rows;
for (uint i = 0; i < rows; i++)
tablesHeap.TypeRefTable.Create(new RawTypeRefRow());
}
if (PreserveMemberRefRids) {
rows = mod.TablesStream.MemberRefTable.Rows;
for (uint i = 0; i < rows; i++)
tablesHeap.MemberRefTable.Create(new RawMemberRefRow());
}
if (PreserveStandAloneSigRids) {
rows = mod.TablesStream.StandAloneSigTable.Rows;
for (uint i = 0; i < rows; i++)
tablesHeap.StandAloneSigTable.Create(new RawStandAloneSigRow());
}
if (PreserveTypeSpecRids) {
rows = mod.TablesStream.TypeSpecTable.Rows;
for (uint i = 0; i < rows; i++)
tablesHeap.TypeSpecTable.Create(new RawTypeSpecRow());
}
if (PreserveMethodSpecRids) {
rows = mod.TablesStream.MethodSpecTable.Rows;
for (uint i = 0; i < rows; i++)
tablesHeap.MethodSpecTable.Create(new RawMethodSpecRow());
}
}
///
/// Adds any non-referenced rows that haven't been added yet but are present in
/// the original file. If there are any non-referenced rows, it's usually a sign
/// that an obfuscator has encrypted one or more methods or that it has added
/// some rows it uses to decrypt something.
///
void InitializeUninitializedTableRows() {
InitializeTypeRefTableRows();
InitializeMemberRefTableRows();
InitializeStandAloneSigTableRows();
InitializeTypeSpecTableRows();
InitializeMethodSpecTableRows();
}
bool initdTypeRef = false;
void InitializeTypeRefTableRows() {
if (!PreserveTypeRefRids || initdTypeRef)
return;
initdTypeRef = true;
uint rows = mod.TablesStream.TypeRefTable.Rows;
for (uint rid = 1; rid <= rows; rid++)
AddTypeRef(mod.ResolveTypeRef(rid));
tablesHeap.TypeRefTable.ReAddRows();
}
bool initdMemberRef = false;
void InitializeMemberRefTableRows() {
if (!PreserveMemberRefRids || initdMemberRef)
return;
initdMemberRef = true;
uint rows = mod.TablesStream.MemberRefTable.Rows;
for (uint rid = 1; rid <= rows; rid++) {
if (tablesHeap.MemberRefTable[rid].Class != 0)
continue;
AddMemberRef(mod.ResolveMemberRef(rid), true);
}
tablesHeap.MemberRefTable.ReAddRows();
}
bool initdStandAloneSig = false;
void InitializeStandAloneSigTableRows() {
if (!PreserveStandAloneSigRids || initdStandAloneSig)
return;
initdStandAloneSig = true;
uint rows = mod.TablesStream.StandAloneSigTable.Rows;
for (uint rid = 1; rid <= rows; rid++) {
if (tablesHeap.StandAloneSigTable[rid].Signature != 0)
continue;
AddStandAloneSig(mod.ResolveStandAloneSig(rid), true);
}
tablesHeap.StandAloneSigTable.ReAddRows();
}
bool initdTypeSpec = false;
void InitializeTypeSpecTableRows() {
if (!PreserveTypeSpecRids || initdTypeSpec)
return;
initdTypeSpec = true;
uint rows = mod.TablesStream.TypeSpecTable.Rows;
for (uint rid = 1; rid <= rows; rid++) {
if (tablesHeap.TypeSpecTable[rid].Signature != 0)
continue;
AddTypeSpec(mod.ResolveTypeSpec(rid), true);
}
tablesHeap.TypeSpecTable.ReAddRows();
}
bool initdMethodSpec = false;
void InitializeMethodSpecTableRows() {
if (!PreserveMethodSpecRids || initdMethodSpec)
return;
initdMethodSpec = true;
uint rows = mod.TablesStream.MethodSpecTable.Rows;
for (uint rid = 1; rid <= rows; rid++) {
if (tablesHeap.MethodSpecTable[rid].Method != 0)
continue;
AddMethodSpec(mod.ResolveMethodSpec(rid), true);
}
tablesHeap.MethodSpecTable.ReAddRows();
}
///
protected override void AllocateMemberDefRids() {
FindMemberDefs();
const int numEvents = 5;
RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 0.0 / numEvents);
for (int i = 1; i <= fieldDefInfos.TableSize; i++) {
if ((uint)i != tablesHeap.FieldTable.Create(new RawFieldRow()))
throw new ModuleWriterException("Invalid field rid");
}
for (int i = 1; i <= methodDefInfos.TableSize; i++) {
if ((uint)i != tablesHeap.MethodTable.Create(new RawMethodRow()))
throw new ModuleWriterException("Invalid method rid");
}
for (int i = 1; i <= paramDefInfos.TableSize; i++) {
if ((uint)i != tablesHeap.ParamTable.Create(new RawParamRow()))
throw new ModuleWriterException("Invalid param rid");
}
for (int i = 1; i <= eventDefInfos.TableSize; i++) {
if ((uint)i != tablesHeap.EventTable.Create(new RawEventRow()))
throw new ModuleWriterException("Invalid event rid");
}
for (int i = 1; i <= propertyDefInfos.TableSize; i++) {
if ((uint)i != tablesHeap.PropertyTable.Create(new RawPropertyRow()))
throw new ModuleWriterException("Invalid property rid");
}
SortFields();
SortMethods();
SortParameters();
SortEvents();
SortProperties();
RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 1.0 / numEvents);
if (fieldDefInfos.NeedPtrTable) {
for (int i = 0; i < fieldDefInfos.Count; i++) {
var info = fieldDefInfos.GetSorted(i);
if ((uint)i + 1 != tablesHeap.FieldPtrTable.Add(new RawFieldPtrRow(info.Rid)))
throw new ModuleWriterException("Invalid field ptr rid");
}
ReUseDeletedFieldRows();
}
if (methodDefInfos.NeedPtrTable) {
for (int i = 0; i < methodDefInfos.Count; i++) {
var info = methodDefInfos.GetSorted(i);
if ((uint)i + 1 != tablesHeap.MethodPtrTable.Add(new RawMethodPtrRow(info.Rid)))
throw new ModuleWriterException("Invalid method ptr rid");
}
ReUseDeletedMethodRows();
}
if (paramDefInfos.NeedPtrTable) {
// NOTE: peverify does not support the ParamPtr table. It's a bug.
for (int i = 0; i < paramDefInfos.Count; i++) {
var info = paramDefInfos.GetSorted(i);
if ((uint)i + 1 != tablesHeap.ParamPtrTable.Add(new RawParamPtrRow(info.Rid)))
throw new ModuleWriterException("Invalid param ptr rid");
}
ReUseDeletedParamRows();
}
if (eventDefInfos.NeedPtrTable) {
for (int i = 0; i < eventDefInfos.Count; i++) {
var info = eventDefInfos.GetSorted(i);
if ((uint)i + 1 != tablesHeap.EventPtrTable.Add(new RawEventPtrRow(info.Rid)))
throw new ModuleWriterException("Invalid event ptr rid");
}
}
if (propertyDefInfos.NeedPtrTable) {
for (int i = 0; i < propertyDefInfos.Count; i++) {
var info = propertyDefInfos.GetSorted(i);
if ((uint)i + 1 != tablesHeap.PropertyPtrTable.Add(new RawPropertyPtrRow(info.Rid)))
throw new ModuleWriterException("Invalid property ptr rid");
}
}
RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 2.0 / numEvents);
InitializeMethodAndFieldList();
InitializeParamList();
InitializeEventMap();
InitializePropertyMap();
RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 3.0 / numEvents);
// We must re-use deleted event/property rows after we've initialized
// the event/prop map tables.
if (eventDefInfos.NeedPtrTable)
ReUseDeletedEventRows();
if (propertyDefInfos.NeedPtrTable)
ReUseDeletedPropertyRows();
RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 4.0 / numEvents);
InitializeTypeRefTableRows();
InitializeTypeSpecTableRows();
InitializeMemberRefTableRows();
InitializeMethodSpecTableRows();
}
///
/// Re-uses all Field rows which aren't owned by any type due to the fields
/// having been deleted by the user. The reason we must do this is that the
/// FieldPtr and Field tables must be the same size.
///
void ReUseDeletedFieldRows() {
if (tablesHeap.FieldPtrTable.IsEmpty)
return;
if (fieldDefInfos.TableSize == tablesHeap.FieldPtrTable.Rows)
return;
var hasOwner = new bool[fieldDefInfos.TableSize];
for (int i = 0; i < fieldDefInfos.Count; i++)
hasOwner[(int)fieldDefInfos.Get(i).Rid - 1] = true;
CreateDummyPtrTableType();
uint fieldSig = GetSignature(new FieldSig(module.CorLibTypes.Byte));
for (int i = 0; i < hasOwner.Length; i++) {
if (hasOwner[i])
continue;
uint frid = (uint)i + 1;
var frow = new RawFieldRow((ushort)(FieldAttributes.Public | FieldAttributes.Static), stringsHeap.Add($"f{frid:X6}"), fieldSig);
tablesHeap.FieldTable[frid] = frow;
tablesHeap.FieldPtrTable.Create(new RawFieldPtrRow(frid));
}
if (fieldDefInfos.TableSize != tablesHeap.FieldPtrTable.Rows)
throw new ModuleWriterException("Didn't create all dummy fields");
}
///
/// Re-uses all Method rows which aren't owned by any type due to the methods
/// having been deleted by the user. The reason we must do this is that the
/// MethodPtr and Method tables must be the same size.
///
void ReUseDeletedMethodRows() {
if (tablesHeap.MethodPtrTable.IsEmpty)
return;
if (methodDefInfos.TableSize == tablesHeap.MethodPtrTable.Rows)
return;
var hasOwner = new bool[methodDefInfos.TableSize];
for (int i = 0; i < methodDefInfos.Count; i++)
hasOwner[(int)methodDefInfos.Get(i).Rid - 1] = true;
CreateDummyPtrTableType();
uint methodSig = GetSignature(MethodSig.CreateInstance(module.CorLibTypes.Void));
for (int i = 0; i < hasOwner.Length; i++) {
if (hasOwner[i])
continue;
uint mrid = (uint)i + 1;
var mrow = new RawMethodRow(0, (ushort)(MethodImplAttributes.IL | MethodImplAttributes.Managed),
(ushort)(MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Abstract),
stringsHeap.Add($"m{mrid:X6}"), methodSig, (uint)paramDefInfos.Count);
tablesHeap.MethodTable[mrid] = mrow;
tablesHeap.MethodPtrTable.Create(new RawMethodPtrRow(mrid));
}
if (methodDefInfos.TableSize != tablesHeap.MethodPtrTable.Rows)
throw new ModuleWriterException("Didn't create all dummy methods");
}
///
/// Re-uses all Param rows which aren't owned by any type due to the params
/// having been deleted by the user. The reason we must do this is that the
/// ParamPtr and Param tables must be the same size.
/// This method must be called after since
/// this method will create more methods at the end of the Method table.
///
void ReUseDeletedParamRows() {
if (tablesHeap.ParamPtrTable.IsEmpty)
return;
if (paramDefInfos.TableSize == tablesHeap.ParamPtrTable.Rows)
return;
var hasOwner = new bool[paramDefInfos.TableSize];
for (int i = 0; i < paramDefInfos.Count; i++)
hasOwner[(int)paramDefInfos.Get(i).Rid - 1] = true;
CreateDummyPtrTableType();
// For each param, attach it to a new method. Another alternative would be to create
// one (or a few) methods with tons of parameters.
uint methodSig = GetSignature(MethodSig.CreateInstance(module.CorLibTypes.Void));
for (int i = 0; i < hasOwner.Length; i++) {
if (hasOwner[i])
continue;
uint prid = (uint)i + 1;
var prow = new RawParamRow(0, 0, stringsHeap.Add($"p{prid:X6}"));
tablesHeap.ParamTable[prid] = prow;
uint ptrRid = tablesHeap.ParamPtrTable.Create(new RawParamPtrRow(prid));
var mrow = new RawMethodRow(0,
(ushort)(MethodImplAttributes.IL | MethodImplAttributes.Managed),
(ushort)(MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Abstract),
stringsHeap.Add($"mp{prid:X6}"),
methodSig,
ptrRid);
uint mrid = tablesHeap.MethodTable.Create(mrow);
if (tablesHeap.MethodPtrTable.Rows > 0)
tablesHeap.MethodPtrTable.Create(new RawMethodPtrRow(mrid));
}
if (paramDefInfos.TableSize != tablesHeap.ParamPtrTable.Rows)
throw new ModuleWriterException("Didn't create all dummy params");
}
///
/// Re-uses all Event rows which aren't owned by any type due to the events
/// having been deleted by the user. The reason we must do this is that the
/// EventPtr and Event tables must be the same size.
///
void ReUseDeletedEventRows() {
if (tablesHeap.EventPtrTable.IsEmpty)
return;
if (eventDefInfos.TableSize == tablesHeap.EventPtrTable.Rows)
return;
var hasOwner = new bool[eventDefInfos.TableSize];
for (int i = 0; i < eventDefInfos.Count; i++)
hasOwner[(int)eventDefInfos.Get(i).Rid - 1] = true;
uint typeRid = CreateDummyPtrTableType();
tablesHeap.EventMapTable.Create(new RawEventMapRow(typeRid, (uint)tablesHeap.EventPtrTable.Rows + 1));
uint eventType = AddTypeDefOrRef(module.CorLibTypes.Object.TypeDefOrRef);
for (int i = 0; i < hasOwner.Length; i++) {
if (hasOwner[i])
continue;
uint erid = (uint)i + 1;
var frow = new RawEventRow(0, stringsHeap.Add($"E{erid:X6}"), eventType);
tablesHeap.EventTable[erid] = frow;
tablesHeap.EventPtrTable.Create(new RawEventPtrRow(erid));
}
if (eventDefInfos.TableSize != tablesHeap.EventPtrTable.Rows)
throw new ModuleWriterException("Didn't create all dummy events");
}
///
/// Re-uses all Property rows which aren't owned by any type due to the properties
/// having been deleted by the user. The reason we must do this is that the
/// PropertyPtr and Property tables must be the same size.
///
void ReUseDeletedPropertyRows() {
if (tablesHeap.PropertyPtrTable.IsEmpty)
return;
if (propertyDefInfos.TableSize == tablesHeap.PropertyPtrTable.Rows)
return;
var hasOwner = new bool[propertyDefInfos.TableSize];
for (int i = 0; i < propertyDefInfos.Count; i++)
hasOwner[(int)propertyDefInfos.Get(i).Rid - 1] = true;
uint typeRid = CreateDummyPtrTableType();
tablesHeap.PropertyMapTable.Create(new RawPropertyMapRow(typeRid, (uint)tablesHeap.PropertyPtrTable.Rows + 1));
uint propertySig = GetSignature(PropertySig.CreateStatic(module.CorLibTypes.Object));
for (int i = 0; i < hasOwner.Length; i++) {
if (hasOwner[i])
continue;
uint prid = (uint)i + 1;
var frow = new RawPropertyRow(0, stringsHeap.Add($"P{prid:X6}"), propertySig);
tablesHeap.PropertyTable[prid] = frow;
tablesHeap.PropertyPtrTable.Create(new RawPropertyPtrRow(prid));
}
if (propertyDefInfos.TableSize != tablesHeap.PropertyPtrTable.Rows)
throw new ModuleWriterException("Didn't create all dummy properties");
}
///
/// Creates a dummy TypeDef at the end of the TypeDef table that will own
/// dummy methods and fields. These dummy methods and fields are only created if the size
/// of the ptr table is less than the size of the non-ptr table (eg. size MethodPtr table
/// is less than size Method table). The only reason the ptr table would be smaller than
/// the non-ptr table is when some field/method has been deleted and we must preserve
/// all method/field rids.
///
uint CreateDummyPtrTableType() {
if (dummyPtrTableTypeRid != 0)
return dummyPtrTableTypeRid;
var flags = TypeAttributes.NotPublic | TypeAttributes.AutoLayout |
TypeAttributes.Class | TypeAttributes.Abstract | TypeAttributes.AnsiClass;
int numFields = fieldDefInfos.NeedPtrTable ? fieldDefInfos.Count : fieldDefInfos.TableSize;
int numMethods = methodDefInfos.NeedPtrTable ? methodDefInfos.Count : methodDefInfos.TableSize;
var row = new RawTypeDefRow((uint)flags,
stringsHeap.Add(Guid.NewGuid().ToString("B")),
stringsHeap.Add("dummy_ptr"),
AddTypeDefOrRef(module.CorLibTypes.Object.TypeDefOrRef),
(uint)numFields + 1,
(uint)numMethods + 1);
dummyPtrTableTypeRid = tablesHeap.TypeDefTable.Create(row);
if (dummyPtrTableTypeRid == 1)
throw new ModuleWriterException("Dummy ptr type is the first type");
return dummyPtrTableTypeRid;
}
uint dummyPtrTableTypeRid;
void FindMemberDefs() {
int count;
var added = new Dictionary