public int GetHashCode(RawTypeDefRow obj) => (int)obj.Flags + rol(obj.Name, 3) + rol(obj.Namespace, 7) + rol(obj.Extends, 11) + rol(obj.FieldList, 15) + rol(obj.MethodList, 19);
public bool Equals(RawTypeDefRow x, RawTypeDefRow y) => x.Flags == y.Flags && x.Name == y.Name && x.Namespace == y.Namespace && x.Extends == y.Extends && x.FieldList == y.FieldList && x.MethodList == y.MethodList;
void DeleteTypeDef(MetadataEditor mdEditor, TypeDef nonNestedTypeDef) { Debug.Assert(nonNestedTypeDef.DeclaringType == null); var dict = new Dictionary <TypeDef, (TypeDef Type, uint TypeRefRid, RawTypeRefRow TypeRefRow)>(); foreach (var type in MDPatcherUtils.GetMetadataTypes(nonNestedTypeDef)) { var typeRefRid = mdEditor.TablesHeap.TypeRefTable.Create(); remappedTypeTokens.Add(type.MDToken.Raw, new MDToken(Table.TypeRef, typeRefRid).Raw); // Remove the type by renaming it and making it private/internal var tdRow = mdEditor.TablesHeap.TypeDefTable.Get(type.Rid); var flags = tdRow.Flags; if ((flags & 7) <= 1) { flags = flags & ~7U; // NotPublic } else { flags = (flags & ~7U) | 3; // NestedPrivate } var deletedType = (Type : type, TypeRefRid : typeRefRid, TypeRefRow : new RawTypeRefRow(0, tdRow.Name, tdRow.Namespace)); tdRow = new RawTypeDefRow(flags, mdEditor.StringsHeap.Create(Guid.NewGuid().ToString()), mdEditor.StringsHeap.Create(string.Empty), tdRow.Extends, tdRow.FieldList, tdRow.MethodList); mdEditor.TablesHeap.TypeDefTable.Set(type.Rid, ref tdRow); dict.Add(type, deletedType); } remappedTypeTokens.SetReadOnly(); foreach (var kv in dict) { var deletedType = kv.Value; uint resolutionScope; if (deletedType.Type.DeclaringType != null) { var declType = dict[deletedType.Type.DeclaringType]; resolutionScope = CodedToken.ResolutionScope.Encode(new MDToken(Table.TypeRef, declType.TypeRefRid)); } else { resolutionScope = CodedToken.ResolutionScope.Encode(new MDToken(Table.AssemblyRef, GetOrCreateTempAssemblyRid())); } deletedType.TypeRefRow = new RawTypeRefRow(resolutionScope, deletedType.TypeRefRow.Name, deletedType.TypeRefRow.Namespace); mdEditor.TablesHeap.TypeRefTable.Set(deletedType.TypeRefRid, ref deletedType.TypeRefRow); } }
/// <summary> /// Reads a raw <c>TypeDef</c> row or returns false if the row doesn't exist /// </summary> /// <param name="rid">Row ID</param> /// <param name="row">Row data</param> /// <returns></returns> public bool TryReadTypeDefRow(uint rid, out RawTypeDefRow row) { var table = TypeDefTable; if (table.IsInvalidRID(rid)) { row = default; return(false); } var reader = table.DataReader; reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; row = new RawTypeDefRow( reader.Unsafe_ReadUInt32(), table.Column1.Unsafe_Read24(ref reader), table.Column2.Unsafe_Read24(ref reader), table.Column3.Unsafe_Read24(ref reader), table.Column4.Unsafe_Read24(ref reader), table.Column5.Unsafe_Read24(ref reader)); return(true); }
/// <summary> /// Patches all columns and blob signatures that reference a moved TypeDef so they reference the new TypeRef /// </summary> void PatchTypeTokenReferences(TypeDef nonNestedEditedType) { if (remappedTypeTokens.Count == 0) { return; } // NOTE: We don't patch the following: // - Method bodies // - All MemberRefs referenced by method bodies // - StandAloneSig.Signature. It's only used by method bodies // - MethodSpec.Instantiation. It's only used by method bodies // - Custom attribute blobs. Could reference the edited type but not likely to cause a problem. // - Marshal blobs. Could reference the edited type but not likely to cause a problem. // The MD writer doesn't write the FieldMarshal table. var tablesHeap = mdEditor.TablesHeap; var typeSigDict = new Dictionary <uint, uint>(); var callConvSigDict = new Dictionary <uint, uint>(); MDTable table; byte * p; int rowSize; ColumnInfo column, column2; uint i, codedToken, newToken, sig, newSig, rid; MDToken token; // Patch the TypeDef table { table = mdEditor.RealMetadata.TablesStream.TypeDefTable; p = peFile + (int)table.StartOffset; rowSize = (int)table.RowSize; column = table.Columns[3]; p += column.Offset; for (i = 0; i < table.Rows; i++, p += rowSize) { codedToken = column.Size == 2 ? *(ushort *)p : *(uint *)p; if (!CodedToken.TypeDefOrRef.Decode(codedToken, out token)) { continue; } if (remappedTypeTokens.TryGetValue(token.Raw, out newToken)) { var row = tablesHeap.TypeDefTable.Get(i + 1); row = new RawTypeDefRow(row.Flags, row.Name, row.Namespace, CodedToken.TypeDefOrRef.Encode(newToken), row.FieldList, row.MethodList); tablesHeap.TypeDefTable.Set(i + 1, ref row); } } } // Patch the Field table { table = mdEditor.RealMetadata.TablesStream.FieldTable; p = peFile + (int)table.StartOffset; rowSize = (int)table.RowSize; column = table.Columns[2]; p += column.Offset; for (i = 0; i < table.Rows; i++, p += rowSize) { sig = column.Size == 2 ? *(ushort *)p : *(uint *)p; newSig = PatchCallingConventionSignature(callConvSigDict, sig); if (newSig != sig) { var row = tablesHeap.FieldTable.Get(i + 1); row = new RawFieldRow(row.Flags, row.Name, newSig); tablesHeap.FieldTable.Set(i + 1, ref row); } } } // Patch the Method table { table = mdEditor.RealMetadata.TablesStream.MethodTable; p = peFile + (int)table.StartOffset; rowSize = (int)table.RowSize; column = table.Columns[4]; p += column.Offset; for (i = 0; i < table.Rows; i++, p += rowSize) { sig = column.Size == 2 ? *(ushort *)p : *(uint *)p; newSig = PatchCallingConventionSignature(callConvSigDict, sig); if (newSig != sig) { var row = tablesHeap.MethodTable.Get(i + 1); row = new RawMethodRow(row.RVA, row.ImplFlags, row.Flags, row.Name, newSig, row.ParamList); tablesHeap.MethodTable.Set(i + 1, ref row); } } } // Patch the InterfaceImpl table { table = mdEditor.RealMetadata.TablesStream.InterfaceImplTable; p = peFile + (int)table.StartOffset; rowSize = (int)table.RowSize; column = table.Columns[1]; p += column.Offset; for (i = 0; i < table.Rows; i++, p += rowSize) { codedToken = column.Size == 2 ? *(ushort *)p : *(uint *)p; if (!CodedToken.TypeDefOrRef.Decode(codedToken, out token)) { continue; } if (remappedTypeTokens.TryGetValue(token.Raw, out newToken)) { var row = tablesHeap.InterfaceImplTable.Get(i + 1); row = new RawInterfaceImplRow(row.Class, CodedToken.TypeDefOrRef.Encode(newToken)); tablesHeap.InterfaceImplTable.Set(i + 1, ref row); } } } //TODO: PERF: If needed, this table could mostly be skipped. We only need to update refs from: // CustomAttribute.Type, MethodImpl.MethodBody, MethodImpl.MethodDeclaration // CustomAttribute.Type almost never references the edited type, and if it does, the // edited type, or any of its nested types, is an attribute. Quick test: this block // took ~25% of the total time (patch file, write new MD, test file was dnSpy.exe = 3.3MB). // Patch the MemberRef table { table = mdEditor.RealMetadata.TablesStream.MemberRefTable; p = peFile + (int)table.StartOffset; rowSize = (int)table.RowSize; column = table.Columns[0]; column2 = table.Columns[2]; for (i = 0; i < table.Rows; i++, p += rowSize) { codedToken = column.Size == 2 ? *(ushort *)p : *(uint *)p; if (!CodedToken.MemberRefParent.Decode(codedToken, out token)) { continue; } rid = i + 1; RawMemberRefRow row; switch (token.Table) { case Table.TypeRef: case Table.TypeDef: case Table.TypeSpec: if (remappedTypeTokens.TryGetValue(token.Raw, out newToken)) { row = tablesHeap.MemberRefTable.Get(rid); row = new RawMemberRefRow(CodedToken.MemberRefParent.Encode(newToken), row.Name, row.Signature); tablesHeap.MemberRefTable.Set(rid, ref row); } break; case Table.ModuleRef: if (nonNestedEditedType.IsGlobalModuleType && CheckResolutionScopeIsSameModule(token, nonNestedEditedType.Module)) { if (remappedTypeTokens.TryGetValue(nonNestedEditedType.MDToken.Raw, out newToken)) { row = tablesHeap.MemberRefTable.Get(rid); row = new RawMemberRefRow(CodedToken.MemberRefParent.Encode(newToken), row.Name, row.Signature); tablesHeap.MemberRefTable.Set(rid, ref row); } } break; case Table.Method: break; default: Debug.Fail("Impossible"); break; } sig = column2.Size == 2 ? *(ushort *)(p + column2.Offset) : *(uint *)(p + column2.Offset); newSig = PatchCallingConventionSignature(callConvSigDict, sig); if (sig != newSig) { row = tablesHeap.MemberRefTable.Get(rid); row = new RawMemberRefRow(row.Class, row.Name, newSig); tablesHeap.MemberRefTable.Set(rid, ref row); } } } // Patch the Event table { table = mdEditor.RealMetadata.TablesStream.EventTable; p = peFile + (int)table.StartOffset; rowSize = (int)table.RowSize; column = table.Columns[2]; p += column.Offset; for (i = 0; i < table.Rows; i++, p += rowSize) { codedToken = column.Size == 2 ? *(ushort *)p : *(uint *)p; if (!CodedToken.TypeDefOrRef.Decode(codedToken, out token)) { continue; } if (remappedTypeTokens.TryGetValue(token.Raw, out newToken)) { var row = tablesHeap.EventTable.Get(i + 1); row = new RawEventRow(row.EventFlags, row.Name, CodedToken.TypeDefOrRef.Encode(newToken)); tablesHeap.EventTable.Set(i + 1, ref row); } } } // Patch the Property table { table = mdEditor.RealMetadata.TablesStream.PropertyTable; p = peFile + (int)table.StartOffset; rowSize = (int)table.RowSize; column = table.Columns[2]; p += column.Offset; for (i = 0; i < table.Rows; i++, p += rowSize) { sig = column.Size == 2 ? *(ushort *)p : *(uint *)p; newSig = PatchCallingConventionSignature(callConvSigDict, sig); if (newSig != sig) { var row = tablesHeap.PropertyTable.Get(i + 1); row = new RawPropertyRow(row.PropFlags, row.Name, newSig); tablesHeap.PropertyTable.Set(i + 1, ref row); } } } // Patch the TypeSpec table { table = mdEditor.RealMetadata.TablesStream.TypeSpecTable; p = peFile + (int)table.StartOffset; rowSize = (int)table.RowSize; column = table.Columns[0]; for (i = 0; i < table.Rows; i++, p += rowSize) { sig = column.Size == 2 ? *(ushort *)p : *(uint *)p; newSig = PatchTypeSignature(typeSigDict, sig); if (newSig != sig) { var row = new RawTypeSpecRow(newSig); tablesHeap.TypeSpecTable.Set(i + 1, ref row); } } } // Patch the GenericParam table if (mdEditor.RealMetadata.TablesStream.GenericParamTable.Columns.Count == 5) { table = mdEditor.RealMetadata.TablesStream.GenericParamTable; p = peFile + (int)table.StartOffset; rowSize = (int)table.RowSize; column = table.Columns[4]; p += column.Offset; for (i = 0; i < table.Rows; i++, p += rowSize) { codedToken = column.Size == 2 ? *(ushort *)p : *(uint *)p; if (!CodedToken.TypeDefOrRef.Decode(codedToken, out token)) { continue; } if (remappedTypeTokens.TryGetValue(token.Raw, out newToken)) { var row = tablesHeap.GenericParamTable.Get(i + 1); row = new RawGenericParamRow(row.Number, row.Flags, row.Owner, row.Name, CodedToken.TypeDefOrRef.Encode(newToken)); tablesHeap.GenericParamTable.Set(i + 1, ref row); } } } // Patch the GenericParamConstraint table { table = mdEditor.RealMetadata.TablesStream.GenericParamConstraintTable; p = peFile + (int)table.StartOffset; rowSize = (int)table.RowSize; column = table.Columns[1]; p += column.Offset; for (i = 0; i < table.Rows; i++, p += rowSize) { codedToken = column.Size == 2 ? *(ushort *)p : *(uint *)p; if (!CodedToken.TypeDefOrRef.Decode(codedToken, out token)) { continue; } if (remappedTypeTokens.TryGetValue(token.Raw, out newToken)) { var row = tablesHeap.GenericParamConstraintTable.Get(i + 1); row = new RawGenericParamConstraintRow(row.Owner, CodedToken.TypeDefOrRef.Encode(newToken)); tablesHeap.GenericParamConstraintTable.Set(i + 1, ref row); } } } }
static uint ReadTypeDefColumnMethod(ref RawTypeDefRow row, int index) => row[index];
/// <inheritdoc/> protected override void AllocateMemberDefRids() { int numTypes = allTypeDefs.Length; int typeNum = 0; int notifyNum = 0; const int numNotifyEvents = 5; int notifyAfter = numTypes / numNotifyEvents; uint fieldListRid = 1, methodListRid = 1; uint eventListRid = 1, propertyListRid = 1; uint paramListRid = 1; int count; foreach (var type in allTypeDefs) { if (typeNum++ == notifyAfter && notifyNum < numNotifyEvents) { RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, (double)typeNum / numTypes); notifyNum++; notifyAfter = (int)((double)numTypes / numNotifyEvents * (notifyNum + 1)); } if (type == null) { continue; } uint typeRid = GetRid(type); var typeRow = tablesHeap.TypeDefTable[typeRid]; typeRow = new RawTypeDefRow(typeRow.Flags, typeRow.Name, typeRow.Namespace, typeRow.Extends, fieldListRid, methodListRid); tablesHeap.TypeDefTable[typeRid] = typeRow; var fields = type.Fields; count = fields.Count; for (int i = 0; i < count; i++) { var field = fields[i]; if (field == null) { continue; } uint rid = fieldListRid++; if (rid != tablesHeap.FieldTable.Create(new RawFieldRow())) { throw new ModuleWriterException("Invalid field rid"); } fieldDefInfos.Add(field, rid); } var methods = type.Methods; count = methods.Count; for (int i = 0; i < count; i++) { var method = methods[i]; if (method == null) { continue; } uint rid = methodListRid++; var row = new RawMethodRow(0, 0, 0, 0, 0, paramListRid); if (rid != tablesHeap.MethodTable.Create(row)) { throw new ModuleWriterException("Invalid method rid"); } methodDefInfos.Add(method, rid); foreach (var pd in Sort(method.ParamDefs)) { if (pd == null) { continue; } uint pdRid = paramListRid++; if (pdRid != tablesHeap.ParamTable.Create(new RawParamRow())) { throw new ModuleWriterException("Invalid param rid"); } paramDefInfos.Add(pd, pdRid); } } if (!IsEmpty(type.Events)) { uint eventMapRid = tablesHeap.EventMapTable.Create(new RawEventMapRow(typeRid, eventListRid)); eventMapInfos.Add(type, eventMapRid); var events = type.Events; count = events.Count; for (int i = 0; i < count; i++) { var evt = events[i]; if (evt == null) { continue; } uint rid = eventListRid++; if (rid != tablesHeap.EventTable.Create(new RawEventRow())) { throw new ModuleWriterException("Invalid event rid"); } eventDefInfos.Add(evt, rid); } } if (!IsEmpty(type.Properties)) { uint propertyMapRid = tablesHeap.PropertyMapTable.Create(new RawPropertyMapRow(typeRid, propertyListRid)); propertyMapInfos.Add(type, propertyMapRid); var properties = type.Properties; count = properties.Count; for (int i = 0; i < count; i++) { var prop = properties[i]; if (prop == null) { continue; } uint rid = propertyListRid++; if (rid != tablesHeap.PropertyTable.Create(new RawPropertyRow())) { throw new ModuleWriterException("Invalid property rid"); } propertyDefInfos.Add(prop, rid); } } } }