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

Let MemberCloner keep track of included types by value #493

Merged
merged 2 commits into from
Nov 12, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System;

namespace AsmResolver.DotNet.Cloning
{
/// <summary>
Expand Down Expand Up @@ -49,11 +51,16 @@ public override IMethodDefOrRef ImportMethod(IMethodDefOrRef method)
/// <inheritdoc />
protected override ITypeDefOrRef ImportType(TypeReference type)
{
return type.Namespace == "System"
&& type.Name == nameof(System.Object)
&& (type.Scope?.GetAssembly()?.IsCorLib ?? false)
? _context.Module.CorLibTypeFactory.Object.Type
: base.ImportType(type);
// Special case for System.Object.
if (type.IsTypeOf(nameof(System), nameof(Object)) && (type.Scope?.GetAssembly()?.IsCorLib ?? false))
return _context.Module.CorLibTypeFactory.Object.Type;

// Rare case where a type reference could point to one of the included type definitions
// (e.g., in custom attributes type arguments, see https://github.com/Washi1337/AsmResolver/issues/482).
if (_context.ClonedTypes.TryGetValue(type, out var clonedType))
return (ITypeDefOrRef) clonedType;

return base.ImportType(type);
}
}
}
13 changes: 13 additions & 0 deletions src/AsmResolver.DotNet/Cloning/MemberCloneContext.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using AsmResolver.DotNet.Signatures;

namespace AsmResolver.DotNet.Cloning
{
Expand Down Expand Up @@ -49,5 +50,17 @@ public CloneContextAwareReferenceImporter Importer
{
get;
} = new Dictionary<IMemberDescriptor, IMemberDescriptor>();

/// <summary>
/// Gets a mapping of original types to their cloned counterparts.
/// </summary>
/// <remarks>
/// This dictionary performs lookups based on value using a <see cref="SignatureComparer"/> instead of object
/// identity, and can thus be used to translate type references to included type definitions.
/// </remarks>
public IDictionary<ITypeDescriptor, ITypeDescriptor> ClonedTypes
{
get;
} = new Dictionary<ITypeDescriptor, ITypeDescriptor>(SignatureComparer.Default);
}
}
12 changes: 11 additions & 1 deletion src/AsmResolver.DotNet/Cloning/MemberCloner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using AsmResolver.DotNet.Signatures;
using AsmResolver.DotNet.Signatures.Marshal;
using AsmResolver.DotNet.Signatures.Security;
using AsmResolver.DotNet.Signatures.Types;
using AsmResolver.PE.DotNet.Metadata.Tables;

namespace AsmResolver.DotNet.Cloning
Expand Down Expand Up @@ -320,6 +321,7 @@ private static void CreateTypeStub(MemberCloneContext context, TypeDefinition ty

var typeStub = new TypeDefinition(type.Namespace, type.Name, type.Attributes);
context.ClonedMembers.Add(type, typeStub);
context.ClonedTypes.Add(type, typeStub);
}

private void DeepCopyMembers(MemberCloneContext context)
Expand Down Expand Up @@ -442,11 +444,19 @@ private static CustomAttributeArgument CloneCustomAttributeArgument(MemberCloneC

// Copy all elements.
for (int i = 0; i < argument.Elements.Count; i++)
clonedArgument.Elements.Add(argument.Elements[i]);
clonedArgument.Elements.Add(CloneElement(context, argument.Elements[i]));

return clonedArgument;
}

private static object? CloneElement(MemberCloneContext context, object? element)
{
if (element is TypeSignature type)
return context.Importer.ImportTypeSignature(type);

return element;
}

private static ImplementationMap? CloneImplementationMap(MemberCloneContext context, ImplementationMap? map)
{
if (map is null)
Expand Down
20 changes: 10 additions & 10 deletions src/AsmResolver.DotNet/Signatures/Types/CorLibTypeFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,6 @@ namespace AsmResolver.DotNet.Signatures.Types
/// </summary>
public class CorLibTypeFactory
{
/// <summary>
/// Creates a new type factory that references mscorlib 4.0.0.0.
/// </summary>
/// <returns>The factory.</returns>
public static CorLibTypeFactory CreateMscorlib40TypeFactory(ModuleDefinition module)
{
var importer = new ReferenceImporter(module);
return new CorLibTypeFactory(importer.ImportScope(KnownCorLibs.MsCorLib_v4_0_0_0));
}

private CorLibTypeSignature? _void;
private CorLibTypeSignature? _boolean;
private CorLibTypeSignature? _char;
Expand Down Expand Up @@ -146,6 +136,16 @@ public IResolutionScope CorLibScope
/// Gets the element type signature for <see cref="System.Object"/>.
/// </summary>
public CorLibTypeSignature Object => GetOrCreateCorLibTypeSignature(ref _object, ElementType.Object, nameof(Object));

/// <summary>
/// Creates a new type factory that references mscorlib 4.0.0.0.
/// </summary>
/// <returns>The factory.</returns>
public static CorLibTypeFactory CreateMscorlib40TypeFactory(ModuleDefinition module)
{
var importer = new ReferenceImporter(module);
return new CorLibTypeFactory(importer.ImportScope(KnownCorLibs.MsCorLib_v4_0_0_0));
}

/// <summary>
/// Transforms the provided type descriptor to a common object runtime type signature.
Expand Down
28 changes: 28 additions & 0 deletions test/AsmResolver.DotNet.Tests/Cloning/MetadataClonerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.Reflection;
using AsmResolver.DotNet.Cloning;
using AsmResolver.DotNet.Signatures;
using AsmResolver.DotNet.Signatures.Types;
using AsmResolver.DotNet.TestCases.CustomAttributes;
using AsmResolver.DotNet.TestCases.Events;
using AsmResolver.DotNet.TestCases.Fields;
using AsmResolver.DotNet.TestCases.Generics;
Expand Down Expand Up @@ -408,5 +410,31 @@ public void CloneAndInjectAndAssignToken()
Assert.All(result.ClonedTopLevelTypes, t => Assert.Contains(t, targetModule.TopLevelTypes));
Assert.All(result.ClonedMembers, m => Assert.NotEqual(0u, ((IMetadataMember) m).MetadataToken.Rid));
}

[Fact]
public void CloneIncludedTypeArgument()
{
// https://github.com/Washi1337/AsmResolver/issues/482

var sourceModule = ModuleDefinition.FromFile(typeof(CustomAttributesTestClass).Assembly.Location);
var targetModule = PrepareTempModule();

var type = sourceModule.LookupMember<TypeDefinition>(typeof(TestEnum).MetadataToken);
var method = sourceModule.LookupMember<MethodDefinition>(typeof(CustomAttributesTestClass)
.GetMethod(nameof(CustomAttributesTestClass.FIxedLocalTypeArgument))!
.MetadataToken);

var result = new MemberCloner(targetModule)
.Include(type)
.Include(method)
.AddListener(new InjectTypeClonerListener(targetModule))
.Clone();

var newType = result.GetClonedMember(type);
var newMethod = result.GetClonedMember(method);

var newArgument = Assert.IsAssignableFrom<ITypeDescriptor>(newMethod.CustomAttributes[0].Signature!.FixedArguments[0].Element);
Assert.Equal(newType, newArgument, _signatureComparer);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ public void FixedComplexTypeArgument()
{
}

[TestCase(typeof(TestEnum))]
public void FIxedLocalTypeArgument()
{
}

[TestCase(2, "Fixed arg", TestEnum.Value3)]
public void FixedMultipleArguments()
{
Expand Down