diff --git a/src/AsmResolver.DotNet/Cloning/MemberCloner.Semantics.cs b/src/AsmResolver.DotNet/Cloning/MemberCloner.Semantics.cs
index 53140cfca..bc8564ed0 100644
--- a/src/AsmResolver.DotNet/Cloning/MemberCloner.Semantics.cs
+++ b/src/AsmResolver.DotNet/Cloning/MemberCloner.Semantics.cs
@@ -17,9 +17,10 @@ private void DeepCopyProperties(MemberCloneContext context)
{
declaringType.Properties.Add(clonedProperty);
}
- var clonedMember = clonedProperty;
- _listeners.OnClonedMember(property, clonedMember);
- _listeners.OnClonedProperty(property, clonedMember);
+
+ context.ClonedMembers.Add(property, clonedProperty);
+ _listeners.OnClonedMember(property, clonedProperty);
+ _listeners.OnClonedProperty(property, clonedProperty);
}
}
@@ -54,9 +55,10 @@ private void DeepCopyEvents(MemberCloneContext context)
{
declaringType.Events.Add(clonedEvent);
}
- var clonedMember = clonedEvent;
- _listeners.OnClonedMember(@event, clonedMember);
- _listeners.OnClonedEvent(@event, clonedMember);
+
+ context.ClonedMembers.Add(@event, clonedEvent);
+ _listeners.OnClonedMember(@event, clonedEvent);
+ _listeners.OnClonedEvent(@event, clonedEvent);
}
}
@@ -82,9 +84,11 @@ private static void CloneSemantics(MemberCloneContext context, IHasSemantics sem
{
foreach (var semantics in semanticsProvider.Semantics)
{
- clonedProvider.Semantics.Add(new MethodSemantics(
- (MethodDefinition) context.ClonedMembers[semantics.Method!],
- semantics.Attributes));
+ if (context.ClonedMembers.TryGetValue(semantics.Method!, out var m)
+ && m is MethodDefinition semanticMethod)
+ {
+ clonedProvider.Semantics.Add(new MethodSemantics(semanticMethod, semantics.Attributes));
+ }
}
}
}
diff --git a/src/AsmResolver.DotNet/Cloning/MemberCloner.cs b/src/AsmResolver.DotNet/Cloning/MemberCloner.cs
index 60f1d1227..7e238ba74 100644
--- a/src/AsmResolver.DotNet/Cloning/MemberCloner.cs
+++ b/src/AsmResolver.DotNet/Cloning/MemberCloner.cs
@@ -245,9 +245,19 @@ public MemberCloner Include(FieldDefinition field)
///
/// The property to include.
/// The metadata cloner that the property is added to.
- public MemberCloner Include(PropertyDefinition property)
+ public MemberCloner Include(PropertyDefinition property) => Include(property, true);
+
+ ///
+ /// Adds a single property to the list of members to clone.
+ ///
+ /// The property to include.
+ /// Indicates the attached semantic methods (getters and setters) should be included.
+ /// The metadata cloner that the property is added to.
+ public MemberCloner Include(PropertyDefinition property, bool recursive)
{
_propertiesToClone.Add(property);
+ if (recursive)
+ IncludeSemantics(property);
return this;
}
@@ -256,12 +266,31 @@ public MemberCloner Include(PropertyDefinition property)
///
/// The event to include.
/// The metadata cloner that the event is added to.
- public MemberCloner Include(EventDefinition @event)
+ public MemberCloner Include(EventDefinition @event) => Include(@event, true);
+
+ ///
+ /// Adds a single event to the list of members to clone.
+ ///
+ /// The event to include.
+ /// Indicates the attached semantic methods (add, remove, fire) should be included.
+ /// The metadata cloner that the property is added to.
+ public MemberCloner Include(EventDefinition @event, bool recursive)
{
_eventsToClone.Add(@event);
+ if (recursive)
+ IncludeSemantics(@event);
return this;
}
+ private void IncludeSemantics(IHasSemantics member)
+ {
+ foreach (var semantic in member.Semantics)
+ {
+ if (semantic.Method is not null)
+ _methodsToClone.Add(semantic.Method);
+ }
+ }
+
///
/// Adds a member cloner listener to the cloner.
///
diff --git a/test/AsmResolver.DotNet.Tests/Cloning/MetadataClonerTest.cs b/test/AsmResolver.DotNet.Tests/Cloning/MetadataClonerTest.cs
index 8e0da015f..9585779ee 100644
--- a/test/AsmResolver.DotNet.Tests/Cloning/MetadataClonerTest.cs
+++ b/test/AsmResolver.DotNet.Tests/Cloning/MetadataClonerTest.cs
@@ -9,6 +9,7 @@
using AsmResolver.DotNet.TestCases.Fields;
using AsmResolver.DotNet.TestCases.Generics;
using AsmResolver.DotNet.TestCases.Methods;
+using AsmResolver.DotNet.TestCases.Properties;
using AsmResolver.DotNet.TestCases.Types;
using AsmResolver.PE.DotNet.Cil;
using AsmResolver.Tests.Listeners;
@@ -38,7 +39,7 @@ private static ModuleDefinition PrepareTempModule()
private static TypeDefinition CloneType(Type type, out TypeDefinition originalTypeDef)
{
var sourceModule = ModuleDefinition.FromFile(type.Module.Assembly.Location, TestReaderParameters);
- originalTypeDef= (TypeDefinition) sourceModule.LookupMember(type.MetadataToken);
+ originalTypeDef= sourceModule.LookupMember(type.MetadataToken);
var targetModule = PrepareTempModule();
@@ -60,7 +61,7 @@ private static TypeDefinition CloneType(Type type, out TypeDefinition originalTy
private static MethodDefinition CloneMethod(MethodBase methodBase, out MethodDefinition originalMethodDef)
{
var sourceModule = ModuleDefinition.FromFile(methodBase.Module.Assembly.Location, TestReaderParameters);
- originalMethodDef = (MethodDefinition) sourceModule.LookupMember(methodBase.MetadataToken);
+ originalMethodDef = sourceModule.LookupMember(methodBase.MetadataToken);
var targetModule = PrepareTempModule();
@@ -68,8 +69,7 @@ private static MethodDefinition CloneMethod(MethodBase methodBase, out MethodDef
.Include(originalMethodDef)
.Clone();
-
- var clonedMethod = (MethodDefinition) result.ClonedMembers.First();
+ var clonedMethod = result.ClonedMembers.OfType().First();
Assert.True(result.ContainsClonedMember(originalMethodDef));
Assert.Equal(clonedMethod, result.GetClonedMember(originalMethodDef));
@@ -77,6 +77,25 @@ private static MethodDefinition CloneMethod(MethodBase methodBase, out MethodDef
return clonedMethod;
}
+ private static PropertyDefinition CloneProperty(PropertyInfo property, out PropertyDefinition originalProperty)
+ {
+ var sourceModule = ModuleDefinition.FromFile(property.Module.Assembly.Location, TestReaderParameters);
+ originalProperty = sourceModule.LookupMember(property.MetadataToken);
+
+ var targetModule = PrepareTempModule();
+
+ var result = new MemberCloner(targetModule)
+ .Include(originalProperty)
+ .Clone();
+
+ var clonedProperty = result.ClonedMembers.OfType().First();
+
+ Assert.True(result.ContainsClonedMember(originalProperty));
+ Assert.Equal(clonedProperty, result.GetClonedMember(originalProperty));
+
+ return clonedProperty;
+ }
+
private static FieldDefinition CloneInitializerField(FieldInfo field, out FieldDefinition originalFieldDef)
{
var sourceModule = ModuleDefinition.FromFile(field.Module.Assembly.Location, TestReaderParameters);
@@ -255,6 +274,18 @@ public void ReferencesToMethodSpecs()
Assert.NotSame(originalSpec.Module, newSpec.Module);
}
+ [Fact]
+ public void CloneSemantics()
+ {
+ var clonedProperty = CloneProperty(typeof(SingleProperty).GetProperty(nameof(SingleProperty.IntProperty)), out var originalProperty);
+
+ Assert.Equal(originalProperty.Name, clonedProperty.Name);
+ Assert.Equal(
+ originalProperty.Semantics.Select(x => x.Method!.Name),
+ clonedProperty.Semantics.Select(x => x.Method!.Name)
+ );
+ }
+
[Fact]
public void CloneImplMap()
{