Skip to content

Commit

Permalink
Merge pull request #253 from Washi1337/issue/preserve-duplicate-metad…
Browse files Browse the repository at this point in the history
…ata-members

Preserve duplicate metadata members when required
  • Loading branch information
Washi1337 authored Mar 1, 2022
2 parents b1cc981 + 9b8ab64 commit 63b77c3
Show file tree
Hide file tree
Showing 9 changed files with 349 additions and 58 deletions.
125 changes: 110 additions & 15 deletions src/AsmResolver.DotNet/Builder/DotNetDirectoryBuffer.TokenProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,29 @@ public partial class DotNetDirectoryBuffer : IMetadataTokenProvider
public uint GetUserStringIndex(string value) => Metadata.UserStringsStream.GetStringIndex(value);

/// <inheritdoc />
public MetadataToken GetTypeReferenceToken(TypeReference? type)
public MetadataToken GetTypeReferenceToken(TypeReference? type) => AddTypeReference(type, false);

/// <summary>
/// Adds a type reference to the buffer.
/// </summary>
/// <param name="type">The reference to add.</param>
/// <param name="allowDuplicates">
/// <c>true</c> if the row is always to be added to the end of the buffer, <c>false</c> if a duplicated row
/// is supposed to be removed and the token of the original should be returned instead.
/// </param>
/// <returns>The newly assigned metadata token.</returns>
public MetadataToken AddTypeReference(TypeReference? type, bool allowDuplicates)
{
if (!AssertIsImported(type))
return MetadataToken.Zero;

var table = Metadata.TablesStream.GetTable<TypeReferenceRow>(TableIndex.TypeRef);
var table = Metadata.TablesStream.GetDistinctTable<TypeReferenceRow>(TableIndex.TypeRef);
var row = new TypeReferenceRow(
AddResolutionScope(type.Scope),
Metadata.StringsStream.GetStringIndex(type.Name),
Metadata.StringsStream.GetStringIndex(type.Namespace));

var token = table.Add(row);
var token = table.Add(row, allowDuplicates);
_tokenMapping.Register(type, token);
AddCustomAttributes(token, type);
return token;
Expand Down Expand Up @@ -90,45 +101,87 @@ public MetadataToken GetEventDefinitionToken(EventDefinition? @event)

/// <inheritdoc />
public MetadataToken GetMemberReferenceToken(MemberReference? member)
{
return AddMemberReference(member, false);
}

/// <summary>
/// Adds a member reference to the buffer.
/// </summary>
/// <param name="member">The reference to add.</param>
/// <param name="allowDuplicates">
/// <c>true</c> if the row is always to be added to the end of the buffer, <c>false</c> if a duplicated row
/// is supposed to be removed and the token of the original should be returned instead.
/// </param>
/// <returns>The newly assigned metadata token.</returns>
public MetadataToken AddMemberReference(MemberReference? member, bool allowDuplicates)
{
if (!AssertIsImported(member))
return MetadataToken.Zero;

var table = Metadata.TablesStream.GetTable<MemberReferenceRow>(TableIndex.MemberRef);
var table = Metadata.TablesStream.GetDistinctTable<MemberReferenceRow>(TableIndex.MemberRef);
var row = new MemberReferenceRow(
AddMemberRefParent(member.Parent),
Metadata.StringsStream.GetStringIndex(member.Name),
Metadata.BlobStream.GetBlobIndex(this, member.Signature, ErrorListener));

var token = table.Add(row);
var token = table.Add(row, allowDuplicates);
_tokenMapping.Register(member, token);
AddCustomAttributes(token, member);
return token;
}

/// <inheritdoc />
public MetadataToken GetStandAloneSignatureToken(StandAloneSignature? signature)
{
return AddStandAloneSignature(signature, false);
}

/// <summary>
/// Adds a stand-alone signature to the buffer.
/// </summary>
/// <param name="signature">The signature to add.</param>
/// <param name="allowDuplicates">
/// <c>true</c> if the row is always to be added to the end of the buffer, <c>false</c> if a duplicated row
/// is supposed to be removed and the token of the original should be returned instead.
/// </param>
/// <returns>The newly assigned metadata token.</returns>
public MetadataToken AddStandAloneSignature(StandAloneSignature? signature, bool allowDuplicates)
{
if (signature is null)
return MetadataToken.Zero;

var table = Metadata.TablesStream.GetTable<StandAloneSignatureRow>(TableIndex.StandAloneSig);
var table = Metadata.TablesStream.GetDistinctTable<StandAloneSignatureRow>(TableIndex.StandAloneSig);
var row = new StandAloneSignatureRow(
Metadata.BlobStream.GetBlobIndex(this, signature.Signature, ErrorListener));

var token = table.Add(row);
var token = table.Add(row, allowDuplicates);
_tokenMapping.Register(signature, token);
AddCustomAttributes(token, signature);
return token;
}

/// <inheritdoc />
public MetadataToken GetAssemblyReferenceToken(AssemblyReference? assembly)
{
return AddAssemblyReference(assembly, false);
}

/// <summary>
/// Adds an assembly reference to the buffer.
/// </summary>
/// <param name="assembly">The reference to add.</param>
/// <param name="allowDuplicates">
/// <c>true</c> if the row is always to be added to the end of the buffer, <c>false</c> if a duplicated row
/// is supposed to be removed and the token of the original should be returned instead.
/// </param>
/// <returns>The newly assigned metadata token.</returns>
public MetadataToken AddAssemblyReference(AssemblyReference? assembly, bool allowDuplicates)
{
if (assembly is null || !AssertIsImported(assembly))
return MetadataToken.Zero;

var table = Metadata.TablesStream.GetTable<AssemblyReferenceRow>(TableIndex.AssemblyRef);
var table = Metadata.TablesStream.GetDistinctTable<AssemblyReferenceRow>(TableIndex.AssemblyRef);

var row = new AssemblyReferenceRow((ushort) assembly.Version.Major,
(ushort) assembly.Version.Minor,
Expand All @@ -140,7 +193,7 @@ public MetadataToken GetAssemblyReferenceToken(AssemblyReference? assembly)
Metadata.StringsStream.GetStringIndex(assembly.Culture),
Metadata.BlobStream.GetBlobIndex(assembly.HashValue));

var token = table.Add(row);
var token = table.Add(row, allowDuplicates);
AddCustomAttributes(token, assembly);
return token;
}
Expand All @@ -151,45 +204,87 @@ public MetadataToken GetAssemblyReferenceToken(AssemblyReference? assembly)
/// <param name="reference">The reference to add.</param>
/// <returns>The new metadata token assigned to the module reference.</returns>
public MetadataToken GetModuleReferenceToken(ModuleReference? reference)
{
return AddModuleReference(reference, false);
}

/// <summary>
/// Adds a module reference to the buffer.
/// </summary>
/// <param name="reference">The reference to add.</param>
/// <param name="allowDuplicates">
/// <c>true</c> if the row is always to be added to the end of the buffer, <c>false</c> if a duplicated row
/// is supposed to be removed and the token of the original should be returned instead.
/// </param>
/// <returns>The newly assigned metadata token.</returns>
public MetadataToken AddModuleReference(ModuleReference? reference, bool allowDuplicates)
{
if (!AssertIsImported(reference))
return MetadataToken.Zero;

var table = Metadata.TablesStream.GetTable<ModuleReferenceRow>(TableIndex.ModuleRef);
var table = Metadata.TablesStream.GetDistinctTable<ModuleReferenceRow>(TableIndex.ModuleRef);

var row = new ModuleReferenceRow(Metadata.StringsStream.GetStringIndex(reference.Name));
var token = table.Add(row);
var token = table.Add(row, allowDuplicates);
AddCustomAttributes(token, reference);
return token;
}

/// <inheritdoc />
public MetadataToken GetTypeSpecificationToken(TypeSpecification? type)
{
return AddTypeSpecification(type, false);
}

/// <summary>
/// Adds a type specification to the buffer.
/// </summary>
/// <param name="type">The specification to add.</param>
/// <param name="allowDuplicates">
/// <c>true</c> if the row is always to be added to the end of the buffer, <c>false</c> if a duplicated row
/// is supposed to be removed and the token of the original should be returned instead.
/// </param>
/// <returns>The newly assigned metadata token.</returns>
public MetadataToken AddTypeSpecification(TypeSpecification? type, bool allowDuplicates)
{
if (!AssertIsImported(type))
return MetadataToken.Zero;

var table = Metadata.TablesStream.GetTable<TypeSpecificationRow>(TableIndex.TypeSpec);
var table = Metadata.TablesStream.GetDistinctTable<TypeSpecificationRow>(TableIndex.TypeSpec);
var row = new TypeSpecificationRow(Metadata.BlobStream.GetBlobIndex(this, type.Signature, ErrorListener));

var token = table.Add(row);
var token = table.Add(row, allowDuplicates);
_tokenMapping.Register(type, token);
AddCustomAttributes(token, type);
return token;
}

/// <inheritdoc />
public MetadataToken GetMethodSpecificationToken(MethodSpecification? method)
{
return AddMethodSpecification(method, false);
}

/// <summary>
/// Adds a method specification to the buffer.
/// </summary>
/// <param name="method">The specification to add.</param>
/// <param name="allowDuplicates">
/// <c>true</c> if the row is always to be added to the end of the buffer, <c>false</c> if a duplicated row
/// is supposed to be removed and the token of the original should be returned instead.
/// </param>
/// <returns>The newly assigned metadata token.</returns>
public MetadataToken AddMethodSpecification(MethodSpecification? method, bool allowDuplicates)
{
if (!AssertIsImported(method))
return MetadataToken.Zero;

var table = Metadata.TablesStream.GetTable<MethodSpecificationRow>(TableIndex.MethodSpec);
var table = Metadata.TablesStream.GetDistinctTable<MethodSpecificationRow>(TableIndex.MethodSpec);
var row = new MethodSpecificationRow(
AddMethodDefOrRef(method.Method),
Metadata.BlobStream.GetBlobIndex(this, method.Signature, ErrorListener));

var token = table.Add(row);
var token = table.Add(row, allowDuplicates);
_tokenMapping.Register(method, token);
AddCustomAttributes(token, method);
return token;
Expand Down
45 changes: 33 additions & 12 deletions src/AsmResolver.DotNet/Builder/DotNetDirectoryFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public virtual DotNetDirectoryBuildResult CreateDotNetDirectory(

// When specified, import existing AssemblyRef, ModuleRef, TypeRef and MemberRef prior to adding any other
// member reference or definition, to ensure that they are assigned their original RIDs.
ImportBasicTablesIntoTableBuffersIfSpecified(module, buffer);
ImportBasicTablesIfSpecified(module, buffer);

// Define all types defined in the module.
buffer.DefineTypes(discoveryResult.Types);
Expand All @@ -102,7 +102,7 @@ public virtual DotNetDirectoryBuildResult CreateDotNetDirectory(

// Import remaining preservable tables (Type specs, method specs, signatures etc).
// We do this before finalizing any member to ensure that they are assigned their original RIDs.
ImportRemainingTablesIntoTableBuffersIfSpecified(module, buffer);
ImportRemainingTablesIfSpecified(module, buffer);

// Finalize member definitions.
buffer.FinalizeTypes();
Expand Down Expand Up @@ -197,7 +197,7 @@ private IMetadataBuffer CreateMetadataBuffer(ModuleDefinition module)
return metadataBuffer;
}

private void ImportBasicTablesIntoTableBuffersIfSpecified(ModuleDefinition module, DotNetDirectoryBuffer buffer)
private void ImportBasicTablesIfSpecified(ModuleDefinition module, DotNetDirectoryBuffer buffer)
{
if (module.DotNetDirectory is null)
return;
Expand All @@ -210,37 +210,58 @@ private void ImportBasicTablesIntoTableBuffersIfSpecified(ModuleDefinition modul
// and type reference tokens are still preserved, we need to prioritize these.

if ((MetadataBuilderFlags & MetadataBuilderFlags.PreserveAssemblyReferenceIndices) != 0)
ImportTableIntoTableBuffers<AssemblyReference>(module, TableIndex.AssemblyRef, buffer.GetAssemblyReferenceToken);
{
ImportTables<AssemblyReference>(module, TableIndex.AssemblyRef,
r => buffer.AddAssemblyReference(r, true));
}

if ((MetadataBuilderFlags & MetadataBuilderFlags.PreserveModuleReferenceIndices) != 0)
ImportTableIntoTableBuffers<ModuleReference>(module, TableIndex.ModuleRef, buffer.GetModuleReferenceToken);
{
ImportTables<ModuleReference>(module, TableIndex.ModuleRef,
r => buffer.AddModuleReference(r, true));
}

if ((MetadataBuilderFlags & MetadataBuilderFlags.PreserveTypeReferenceIndices) != 0)
ImportTableIntoTableBuffers<TypeReference>(module, TableIndex.TypeRef, buffer.GetTypeReferenceToken);
{
ImportTables<TypeReference>(module, TableIndex.TypeRef,
r => buffer.AddTypeReference(r, true));
}
}

private void ImportTypeSpecsAndMemberRefsIfSpecified(ModuleDefinition module, DotNetDirectoryBuffer buffer)
{
if ((MetadataBuilderFlags & MetadataBuilderFlags.PreserveTypeSpecificationIndices) != 0)
ImportTableIntoTableBuffers<TypeSpecification>(module, TableIndex.TypeSpec, buffer.GetTypeSpecificationToken);
{
ImportTables<TypeSpecification>(module, TableIndex.TypeSpec,
s => buffer.AddTypeSpecification(s, true));
}

if ((MetadataBuilderFlags & MetadataBuilderFlags.PreserveMemberReferenceIndices) != 0)
ImportTableIntoTableBuffers<MemberReference>(module, TableIndex.MemberRef, buffer.GetMemberReferenceToken);
{
ImportTables<MemberReference>(module, TableIndex.MemberRef,
r => buffer.AddMemberReference(r, true));
}
}

private void ImportRemainingTablesIntoTableBuffersIfSpecified(ModuleDefinition module, DotNetDirectoryBuffer buffer)
private void ImportRemainingTablesIfSpecified(ModuleDefinition module, DotNetDirectoryBuffer buffer)
{
if (module.DotNetDirectory is null)
return;

if ((MetadataBuilderFlags & MetadataBuilderFlags.PreserveStandAloneSignatureIndices) != 0)
ImportTableIntoTableBuffers<StandAloneSignature>(module, TableIndex.StandAloneSig, buffer.GetStandAloneSignatureToken);
{
ImportTables<StandAloneSignature>(module, TableIndex.StandAloneSig,
s => buffer.AddStandAloneSignature(s, true));
}

if ((MetadataBuilderFlags & MetadataBuilderFlags.PreserveMethodSpecificationIndices) != 0)
ImportTableIntoTableBuffers<MethodSpecification>(module, TableIndex.MethodSpec, buffer.GetMethodSpecificationToken);
{
ImportTables<MethodSpecification>(module, TableIndex.MethodSpec,
s => buffer.AddMethodSpecification(s, true));
}
}

private static void ImportTableIntoTableBuffers<TMember>(ModuleDefinition module, TableIndex tableIndex,
private static void ImportTables<TMember>(ModuleDefinition module, TableIndex tableIndex,
Func<TMember, MetadataToken> importAction)
{
int count = module.DotNetDirectory!.Metadata
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,27 @@ public TRow this[uint rid]
public ref TRow GetRowRef(uint rid) => ref _underlyingBuffer.GetRowRef(rid);

/// <inheritdoc />
public MetadataToken Add(in TRow row)
public MetadataToken Add(in TRow row) => Add(row, false);

/// <summary>
/// Adds a row to the metadata table buffer.
/// </summary>
/// <param name="row">The row to add.</param>
/// <param name="allowDuplicates">
/// <c>true</c> if the row is always to be added to the end of the buffer, <c>false</c> if a duplicated row
/// is supposed to be removed and the token of the original should be returned instead.</param>
/// <returns>The metadata token that this row was assigned to.</returns>
public MetadataToken Add(in TRow row, bool allowDuplicates)
{
if (!_entries.TryGetValue(row, out var token))
{
token = _underlyingBuffer.Add(in row);
_entries.Add(row, token);
}
else if (allowDuplicates)
{
token = _underlyingBuffer.Add(in row);
}

return token;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,18 @@ public IMetadataTableBuffer<TRow> GetTable<TRow>(TableIndex table)
return (IMetadataTableBuffer<TRow>) _tableBuffers[(int) table];
}

/// <summary>
/// Gets a table buffer by its table index.
/// </summary>
/// <param name="table">The index of the table to get.</param>
/// <typeparam name="TRow">The type of rows the table stores.</typeparam>
/// <returns>The metadata table.</returns>
public DistinctMetadataTableBuffer<TRow> GetDistinctTable<TRow>(TableIndex table)
where TRow : struct, IMetadataRow
{
return (DistinctMetadataTableBuffer<TRow>) _tableBuffers[(int) table];
}

/// <summary>
/// Gets a table buffer by its table index.
/// </summary>
Expand Down
Loading

0 comments on commit 63b77c3

Please sign in to comment.