Skip to content

Commit

Permalink
Merge pull request #337 from CursedLand/development
Browse files Browse the repository at this point in the history
Callbacks implementation for MemberCloner
  • Loading branch information
Washi1337 authored Aug 10, 2022
2 parents fb63f8c + 80d5d8b commit 17383f0
Show file tree
Hide file tree
Showing 10 changed files with 193 additions and 3 deletions.
24 changes: 24 additions & 0 deletions src/AsmResolver.DotNet/Cloning/CallbackCloneListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;

namespace AsmResolver.DotNet.Cloning
{
/// <summary>
/// This implementation that calls the <see cref="MemberClonerListener.OnClonedMember"/> to a callback action.
/// </summary>
public class CallbackCloneListener : MemberClonerListener
{

private readonly Action<IMetadataMember, IMetadataMember> _callback;

/// <summary>
/// Creates a new instance of the <see cref="CallbackCloneListener"/> class.
/// </summary>
/// <param name="callback">The Callback used.</param>
public CallbackCloneListener(Action<IMetadataMember, IMetadataMember> callback) =>
_callback = callback;

/// <inheritdoc/>
public override void OnClonedMember(IMetadataMember original, IMetadataMember cloned) =>
_callback(original, cloned);
}
}
45 changes: 45 additions & 0 deletions src/AsmResolver.DotNet/Cloning/IMemberClonerListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
namespace AsmResolver.DotNet.Cloning
{
/// <summary>
/// <see cref="MemberCloner"/> Callback listener that receives calls after cloning process.
/// </summary>
public interface IMemberClonerListener
{
/// <summary>
/// This function is called for every member that got cloned.
/// </summary>
/// <param name="original">The original member.</param>
/// <param name="cloned">The cloned member.</param>
public void OnClonedMember(IMetadataMember original, IMetadataMember cloned);
/// <summary>
/// This function is called for every type that got cloned.
/// </summary>
/// <param name="original">The original type.</param>
/// <param name="cloned">The cloned type.</param>
public void OnClonedType(TypeDefinition original, TypeDefinition cloned);
/// <summary>
/// This function is called for every method that got cloned.
/// </summary>
/// <param name="original">The original method.</param>
/// <param name="cloned">The cloned method.</param>
public void OnClonedMethod(MethodDefinition original, MethodDefinition cloned);
/// <summary>
/// This function is called for every field that got cloned.
/// </summary>
/// <param name="original">The original field.</param>
/// <param name="cloned">The cloned field.</param>
public void OnClonedField(FieldDefinition original, FieldDefinition cloned);
/// <summary>
/// This function is called for every property that got cloned.
/// </summary>
/// <param name="original">The original property.</param>
/// <param name="cloned">The cloned property.</param>
public void OnClonedProperty(PropertyDefinition original, PropertyDefinition cloned);
/// <summary>
/// This function is called for every event that got cloned.
/// </summary>
/// <param name="original">The original event.</param>
/// <param name="cloned">The cloned event.</param>
public void OnClonedEvent(EventDefinition original, EventDefinition cloned);
}
}
5 changes: 5 additions & 0 deletions src/AsmResolver.DotNet/Cloning/MemberCloner.Fields.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ private static FieldDefinition CreateFieldStub(MemberCloneContext context, Field
private void DeepCopyFields(MemberCloneContext context)
{
foreach (var field in _fieldsToClone)
{
DeepCopyField(context, field);
var clonedMember = (FieldDefinition)context.ClonedMembers[field];
_clonerListener.OnClonedMember(field, clonedMember);
_clonerListener.OnClonedField(field, clonedMember);
}
}

private void DeepCopyField(MemberCloneContext context, FieldDefinition field)
Expand Down
5 changes: 5 additions & 0 deletions src/AsmResolver.DotNet/Cloning/MemberCloner.Methods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ private static ParameterDefinition CloneParameterDefinition(MemberCloneContext c
private void DeepCopyMethods(MemberCloneContext context)
{
foreach (var method in _methodsToClone)
{
DeepCopyMethod(context, method);
var clonedMember = (MethodDefinition)context.ClonedMembers[method];
_clonerListener.OnClonedMember(method, clonedMember);
_clonerListener.OnClonedMethod(method, clonedMember);
}
}

private void DeepCopyMethod(MemberCloneContext context, MethodDefinition method)
Expand Down
6 changes: 6 additions & 0 deletions src/AsmResolver.DotNet/Cloning/MemberCloner.Semantics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ private void DeepCopyProperties(MemberCloneContext context)
{
declaringType.Properties.Add(clonedProperty);
}
var clonedMember = clonedProperty;
_clonerListener.OnClonedMember(property, clonedMember);
_clonerListener.OnClonedProperty(property, clonedMember);
}
}

Expand Down Expand Up @@ -51,6 +54,9 @@ private void DeepCopyEvents(MemberCloneContext context)
{
declaringType.Events.Add(clonedEvent);
}
var clonedMember = clonedEvent;
_clonerListener.OnClonedMember(@event, clonedMember);
_clonerListener.OnClonedEvent(@event, clonedMember);
}
}

Expand Down
27 changes: 25 additions & 2 deletions src/AsmResolver.DotNet/Cloning/MemberCloner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace AsmResolver.DotNet.Cloning
/// </remarks>
public partial class MemberCloner
{
private readonly IMemberClonerListener _clonerListener;
private readonly Func<MemberCloneContext, CloneContextAwareReferenceImporter>? _importerFactory;
private readonly ModuleDefinition _targetModule;

Expand All @@ -30,18 +31,35 @@ public partial class MemberCloner
/// Creates a new instance of the <see cref="MemberCloner"/> class.
/// </summary>
/// <param name="targetModule">The target module to copy the members into.</param>
public MemberCloner(ModuleDefinition targetModule) : this(targetModule, null) { }
public MemberCloner(ModuleDefinition targetModule) : this(targetModule, (original, cloned) => { }) { }

/// <summary>
/// Creates a new instance of the <see cref="MemberCloner"/> class.
/// </summary>
/// <param name="targetModule">The target module to copy the members into.</param>
/// <param name="callback">The callback used in the cloner listener.</param>
public MemberCloner(ModuleDefinition targetModule, Action<IMetadataMember, IMetadataMember> callback) : this(targetModule, new CallbackCloneListener(callback)) { }

/// <summary>
/// Creates a new instance of the <see cref="MemberCloner"/> class.
/// </summary>
/// <param name="targetModule">The target module to copy the members into.</param>
/// <param name="listener">The callback listener used in the cloner.</param>
public MemberCloner(ModuleDefinition targetModule, IMemberClonerListener listener) : this(targetModule, null, listener) { }

/// <summary>
/// Creates a new instance of the <see cref="MemberCloner"/> class.
/// </summary>
/// <param name="targetModule">The target module to copy the members into.</param>
/// <param name="importerFactory">The factory for creating the reference importer</param>
/// <param name="clonerListener">The listener used in the cloner.</param>
public MemberCloner(ModuleDefinition targetModule,
Func<MemberCloneContext, CloneContextAwareReferenceImporter>? importerFactory)
Func<MemberCloneContext, CloneContextAwareReferenceImporter>? importerFactory,
IMemberClonerListener clonerListener)
{
_targetModule = targetModule ?? throw new ArgumentNullException(nameof(targetModule));
_importerFactory = importerFactory;
_clonerListener = clonerListener;
}

/// <summary>
Expand Down Expand Up @@ -272,7 +290,12 @@ private void DeepCopyMembers(MemberCloneContext context)
private void DeepCopyTypes(MemberCloneContext context)
{
foreach (var type in _typesToClone)
{
DeepCopyType(context, type);
var clonedMember = (TypeDefinition)context.ClonedMembers[type];
_clonerListener.OnClonedMember(type, clonedMember);
_clonerListener.OnClonedType(type, clonedMember);
}
}

private void DeepCopyType(MemberCloneContext context, TypeDefinition type)
Expand Down
19 changes: 19 additions & 0 deletions src/AsmResolver.DotNet/Cloning/MemberClonerListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace AsmResolver.DotNet.Cloning
{
/// <inheritdoc/>
public abstract class MemberClonerListener : IMemberClonerListener
{
/// <inheritdoc/>
public virtual void OnClonedMember(IMetadataMember original, IMetadataMember cloned) { }
/// <inheritdoc/>
public virtual void OnClonedEvent(EventDefinition original, EventDefinition cloned) { }
/// <inheritdoc/>
public virtual void OnClonedField(FieldDefinition original, FieldDefinition cloned) { }
/// <inheritdoc/>
public virtual void OnClonedMethod(MethodDefinition original, MethodDefinition cloned) { }
/// <inheritdoc/>
public virtual void OnClonedProperty(PropertyDefinition original, PropertyDefinition cloned) { }
/// <inheritdoc/>
public virtual void OnClonedType(TypeDefinition original, TypeDefinition cloned) { }
}
}
52 changes: 51 additions & 1 deletion test/AsmResolver.DotNet.Tests/Cloning/MetadataClonerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using AsmResolver.DotNet.TestCases.Methods;
using AsmResolver.DotNet.TestCases.Types;
using AsmResolver.PE.DotNet.Cil;
using AsmResolver.Tests.Listeners;
using AsmResolver.Tests.Runners;
using Xunit;

Expand Down Expand Up @@ -329,5 +330,54 @@ public void CloneInterfaceImplementations()
originalTypeDef.Interfaces.Select(t => t.Interface.FullName),
clonedType.Interfaces.Select(t => t.Interface.FullName));
}

[Fact]
public void CloneCallbackResult()
{
var sourceModule = ModuleDefinition.FromFile(typeof(Miscellaneous).Assembly.Location);
var type = sourceModule.TopLevelTypes.First(t => t.Name == nameof(Miscellaneous));

var targetModule = PrepareTempModule();

var reverseMethodsNames = (IMetadataMember original, IMetadataMember cloned) => {
if (cloned is MethodDefinition clonedDescriptor && original is MethodDefinition originalDescriptor)
clonedDescriptor.Name = new string(originalDescriptor.Name.Reverse().ToArray());
};

var result = new MemberCloner(targetModule, reverseMethodsNames)
.Include(type)
.Clone();

var clonedType = result.GetClonedMember(type);

Assert.Equal(
type.Methods.Select(m => m.Name.Reverse().ToArray()),
clonedType.Methods.Select(m => m.Name.ToArray()));

}

[Fact]
public void CloneCustomListenerResult()
{

var sourceModule = ModuleDefinition.FromFile(typeof(Miscellaneous).Assembly.Location);
var type = sourceModule.TopLevelTypes.First(t => t.Name == nameof(Miscellaneous));

var targetModule = PrepareTempModule();

var result = new MemberCloner(targetModule, new CustomMemberClonerListener())
.Include(type)
.Clone();

var clonedType = result.GetClonedMember(type);

Assert.Equal(
type.Methods.Select(m => $"Method_{m.Name}"),
clonedType.Methods.Select(m => m.Name.ToString()));

}

}
}
}
1 change: 1 addition & 0 deletions test/AsmResolver.Tests/AsmResolver.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\AsmResolver.DotNet\AsmResolver.DotNet.csproj" />
<ProjectReference Include="..\..\src\AsmResolver.PE.File\AsmResolver.PE.File.csproj" />
<ProjectReference Include="..\..\src\AsmResolver\AsmResolver.csproj" />
</ItemGroup>
Expand Down
12 changes: 12 additions & 0 deletions test/AsmResolver.Tests/Listeners/CustomMemberClonerListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Cloning;
using System;

namespace AsmResolver.Tests.Listeners
{
public class CustomMemberClonerListener : MemberClonerListener
{
public override void OnClonedMethod(MethodDefinition original, MethodDefinition cloned) =>
cloned.Name = $"Method_{original.Name}";
}
}

0 comments on commit 17383f0

Please sign in to comment.