Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve duplicate metadata members when required #253

Merged
merged 2 commits into from
Mar 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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