Skip to content

Commit

Permalink
#10 Support for Dictionary, List and HashSet derivations
Browse files Browse the repository at this point in the history
  • Loading branch information
greuelpirat committed Jul 21, 2019
1 parent 4ad84af commit b3e3059
Show file tree
Hide file tree
Showing 12 changed files with 236 additions and 81 deletions.
23 changes: 23 additions & 0 deletions AssemblyToProcess/DerivedClasses.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Collections.Generic;
using DeepCopy;

namespace AssemblyToProcess
{
[AddDeepCopyConstructor]
public class DictionaryClass : Dictionary<string, SomeObject>
{
public SomeObject SomeProperty { get; set; }
}

[AddDeepCopyConstructor]
public class ListClass : List<SomeObject>
{
public SomeObject SomeProperty { get; set; }
}

[AddDeepCopyConstructor]
public class SetClass : HashSet<SomeObject>
{
public SomeObject SomeProperty { get; set; }
}
}
7 changes: 5 additions & 2 deletions DeepCopy.Fody/Copy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ IEnumerable<Instruction> Getter() => new[]
return instructions;
}

private IEnumerable<Instruction> CopyValue(TypeReference type, Func<IEnumerable<Instruction>> getterBuilder, Instruction followUp, bool nullableCheck = true)
private IEnumerable<Instruction> CopyValue(TypeReference type, Func<IEnumerable<Instruction>> getterBuilder, Instruction followUp,
bool nullableCheck = true)
{
var list = new List<Instruction>();
list.AddRange(getterBuilder.Invoke());
Expand All @@ -107,8 +108,10 @@ private IEnumerable<Instruction> CopyValue(TypeReference type, Func<IEnumerable<
list.Add(Instruction.Create(OpCodes.Call, StringCopy()));
else if (IsCopyConstructorAvailable(type, out var constructor))
list.Add(Instruction.Create(OpCodes.Newobj, constructor));
else if (type.Resolve().MetadataToken == TypeSystem.ObjectDefinition.MetadataToken)
throw new NotSupportedException(type);
else
throw new CopyConstructorRequiredException(type);
throw new NoCopyConstructorFoundException(type);

return list;
}
Expand Down
10 changes: 0 additions & 10 deletions DeepCopy.Fody/CopyConstructorRequiredException.cs

This file was deleted.

29 changes: 19 additions & 10 deletions DeepCopy.Fody/CopyDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ public partial class ModuleWeaver
{
private IEnumerable<Instruction> CopyDictionary(PropertyDefinition property)
{
var typeDictionary = property.PropertyType.Resolve();
return CopyDictionary(property.PropertyType, property);
}

private IEnumerable<Instruction> CopyDictionary(TypeReference type, PropertyDefinition property)
{
var typeDictionary = type.Resolve();
var typeInstance = (TypeReference) typeDictionary;
var typesArguments = property.PropertyType.SolveGenericArguments().Cast<TypeReference>().ToArray();
var typesArguments = type.SolveGenericArguments().Cast<TypeReference>().ToArray();
var typeKeyValuePair = ImportType(typeof(KeyValuePair<,>), typesArguments);

var methodGetEnumerator = ImportMethod(ImportType(typeof(IEnumerable<>), typeKeyValuePair), nameof(IEnumerable.GetEnumerator), typeKeyValuePair);
Expand All @@ -30,19 +35,23 @@ private IEnumerable<Instruction> CopyDictionary(PropertyDefinition property)
if (IsType(typeDictionary, typeof(IDictionary<,>)))
typeInstance = ImportType(typeof(Dictionary<,>), typesArguments);
else
throw new NotSupportedException(property.FullName);
throw new NotSupportedException(type);
}
else if (!typeDictionary.HasDefaultConstructor())
throw new NotSupportedException(property.FullName);
throw new NotSupportedException(type);

var constructor = ModuleDefinition.ImportReference(NewConstructor(typeInstance).MakeGeneric(typesArguments));
var list = new List<Instruction>();
list.Add(Instruction.Create(OpCodes.Ldarg_0));
list.Add(Instruction.Create(OpCodes.Newobj, constructor));
list.Add(property.MakeSet());
if (property != null)
{
list.Add(Instruction.Create(OpCodes.Ldarg_0));
list.Add(Instruction.Create(OpCodes.Newobj, constructor));
list.Add(property.MakeSet());
}

list.Add(Instruction.Create(OpCodes.Ldarg_1));
list.Add(Instruction.Create(OpCodes.Callvirt, property.GetMethod));
if (property != null)
list.Add(Instruction.Create(OpCodes.Callvirt, property.GetMethod));
list.Add(Instruction.Create(OpCodes.Callvirt, methodGetEnumerator));
list.Add(Instruction.Create(OpCodes.Stloc, varEnumerator));

Expand All @@ -57,7 +66,8 @@ private IEnumerable<Instruction> CopyDictionary(PropertyDefinition property)
list.Add(Instruction.Create(OpCodes.Stloc, varKeyValuePair));

list.Add(Instruction.Create(OpCodes.Ldarg_0));
list.Add(Instruction.Create(OpCodes.Call, property.GetMethod));
if (property != null)
list.Add(Instruction.Create(OpCodes.Call, property.GetMethod));

IEnumerable<Instruction> GetterKey() => new[]
{
Expand All @@ -74,7 +84,6 @@ IEnumerable<Instruction> GetterValue() => new[]
var setItem = Instruction.Create(OpCodes.Callvirt, ImportMethod(typeDictionary, "set_Item", typesArguments));
var getValue = CopyValue(typesArguments[1], GetterValue, setItem).ToList();
list.AddRange(CopyValue(typesArguments[0], GetterKey, getValue.First(), false));
//list.AddRange(GetterKey());
list.AddRange(getValue);
list.Add(setItem);

Expand Down
51 changes: 31 additions & 20 deletions DeepCopy.Fody/CopyList.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
Expand All @@ -8,30 +7,39 @@ namespace DeepCopy.Fody
public partial class ModuleWeaver
{
private IEnumerable<Instruction> CopyList(PropertyDefinition property)
{
return CopyList(property.PropertyType, property);
}

private IEnumerable<Instruction> CopyList(TypeReference type, PropertyDefinition property)
{
var loopStart = Instruction.Create(OpCodes.Nop);
var conditionStart = Instruction.Create(OpCodes.Ldloc, IndexVariable);

var listType = property.PropertyType.Resolve();
var listType = type.Resolve();
var instanceType = (TypeReference) listType;
var argumentType = property.PropertyType.SolveGenericArgument();
var argumentType = type.SolveGenericArgument();

if (listType.IsInterface)
{
if (IsType(listType, typeof(IList<>)))
instanceType = ModuleDefinition.ImportReference(typeof(List<>)).MakeGeneric(argumentType);
else
throw new NotSupportedException(property.FullName);
throw new NotSupportedException(property);
}
else if (!listType.HasDefaultConstructor())
throw new NotSupportedException(property.FullName);
throw new NotSupportedException(property);

var listConstructor = ModuleDefinition.ImportReference(NewConstructor(instanceType).MakeGeneric(argumentType));

var list = new List<Instruction>();
list.Add(Instruction.Create(OpCodes.Ldarg_0));
list.Add(Instruction.Create(OpCodes.Newobj, listConstructor));
list.Add(property.MakeSet());
if (property != null)
{
list.Add(Instruction.Create(OpCodes.Ldarg_0));
list.Add(Instruction.Create(OpCodes.Newobj, listConstructor));
list.Add(property.MakeSet());
}

list.Add(Instruction.Create(OpCodes.Ldc_I4_0));
list.Add(Instruction.Create(OpCodes.Stloc, IndexVariable));
list.Add(Instruction.Create(OpCodes.Br_S, conditionStart));
Expand All @@ -48,7 +56,8 @@ private IEnumerable<Instruction> CopyList(PropertyDefinition property)
// condition
list.Add(conditionStart);
list.Add(Instruction.Create(OpCodes.Ldarg_1));
list.Add(Instruction.Create(OpCodes.Callvirt, property.GetMethod));
if (property != null)
list.Add(Instruction.Create(OpCodes.Callvirt, property.GetMethod));
list.Add(Instruction.Create(OpCodes.Callvirt, ImportMethod(listType, "get_Count", argumentType)));
list.Add(Instruction.Create(OpCodes.Clt));
list.Add(Instruction.Create(OpCodes.Stloc, BooleanVariable));
Expand All @@ -62,19 +71,21 @@ private IEnumerable<Instruction> CopyList(PropertyDefinition property)

private IEnumerable<Instruction> CopyListItem(PropertyDefinition property, TypeDefinition listType, TypeDefinition argumentType)
{
var list = new List<Instruction>
{
Instruction.Create(OpCodes.Ldarg_0),
Instruction.Create(OpCodes.Call, property.GetMethod)
};
var list = new List<Instruction>();
list.Add(Instruction.Create(OpCodes.Ldarg_0));
if (property != null)
list.Add(Instruction.Create(OpCodes.Call, property.GetMethod));

IEnumerable<Instruction> Getter() => new[]
IEnumerable<Instruction> Getter()
{
Instruction.Create(OpCodes.Ldarg_1),
Instruction.Create(OpCodes.Callvirt, property.GetMethod),
Instruction.Create(OpCodes.Ldloc, IndexVariable),
Instruction.Create(OpCodes.Callvirt, ImportMethod(listType, "get_Item", argumentType))
};
var getter = new List<Instruction>();
getter.Add(Instruction.Create(OpCodes.Ldarg_1));
if (property != null)
getter.Add(Instruction.Create(OpCodes.Callvirt, property.GetMethod));
getter.Add(Instruction.Create(OpCodes.Ldloc, IndexVariable));
getter.Add(Instruction.Create(OpCodes.Callvirt, ImportMethod(listType, "get_Item", argumentType)));
return getter;
}

var add = Instruction.Create(OpCodes.Callvirt, ImportMethod(listType, "Add", argumentType));
list.AddRange(CopyValue(argumentType, Getter, add));
Expand Down
28 changes: 19 additions & 9 deletions DeepCopy.Fody/CopySet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ public partial class ModuleWeaver
{
private IEnumerable<Instruction> CopySet(PropertyDefinition property)
{
var typeSet = property.PropertyType.Resolve();
return CopySet(property.PropertyType, property);
}

private IEnumerable<Instruction> CopySet(TypeReference type, PropertyDefinition property)
{
var typeSet = type.Resolve();
var typeInstance = (TypeReference) typeSet;
var typeArgument = property.PropertyType.SolveGenericArgument();
var typeArgument = type.SolveGenericArgument();

var methodGetEnumerator = ImportMethod(ImportType(typeof(IEnumerable<>), typeArgument), nameof(IEnumerable.GetEnumerator), typeArgument);
var typeEnumerator = ImportType(methodGetEnumerator.ReturnType, typeArgument);
Expand All @@ -28,19 +33,23 @@ private IEnumerable<Instruction> CopySet(PropertyDefinition property)
if (IsType(typeSet, typeof(ISet<>)))
typeInstance = ImportType(typeof(HashSet<>), typeArgument);
else
throw new NotSupportedException(property.FullName);
throw new NotSupportedException(property);
}
else if (!typeSet.HasDefaultConstructor())
throw new NotSupportedException(property.FullName);
throw new NotSupportedException(property);

var constructor = ModuleDefinition.ImportReference(NewConstructor(typeInstance).MakeGeneric(typeArgument));
var list = new List<Instruction>();
list.Add(Instruction.Create(OpCodes.Ldarg_0));
list.Add(Instruction.Create(OpCodes.Newobj, constructor));
list.Add(property.MakeSet());
if (property != null)
{
list.Add(Instruction.Create(OpCodes.Ldarg_0));
list.Add(Instruction.Create(OpCodes.Newobj, constructor));
list.Add(property.MakeSet());
}

list.Add(Instruction.Create(OpCodes.Ldarg_1));
list.Add(Instruction.Create(OpCodes.Callvirt, property.GetMethod));
if (property != null)
list.Add(Instruction.Create(OpCodes.Callvirt, property.GetMethod));
list.Add(Instruction.Create(OpCodes.Callvirt, methodGetEnumerator));
list.Add(Instruction.Create(OpCodes.Stloc, varEnumerator));

Expand All @@ -55,7 +64,8 @@ private IEnumerable<Instruction> CopySet(PropertyDefinition property)
list.Add(Instruction.Create(OpCodes.Stloc, varCurrent));

list.Add(Instruction.Create(OpCodes.Ldarg_0));
list.Add(Instruction.Create(OpCodes.Callvirt, property.GetMethod));
if (property != null)
list.Add(Instruction.Create(OpCodes.Callvirt, property.GetMethod));

IEnumerable<Instruction> Getter() => new[]
{
Expand Down
2 changes: 1 addition & 1 deletion DeepCopy.Fody/DeepCopyMethodExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ private void BuildMultiTypeSwitchMethodBody(MethodDefinition method, TypeDefinit
else
{
if (!IsCopyConstructorAvailable(baseType, out var constructor))
throw new CopyConstructorRequiredException(baseType);
throw new NoCopyConstructorFoundException(baseType);

processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Newobj, constructor);
Expand Down
26 changes: 26 additions & 0 deletions DeepCopy.Fody/Exceptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Fody;
using Mono.Cecil;

namespace DeepCopy.Fody
{
public class NotSupportedException : DeepCopyException
{
public NotSupportedException(MemberReference type)
: base($"{type.FullName} is not supported") { }
}

public class NoCopyConstructorFoundException : DeepCopyException
{
public NoCopyConstructorFoundException(MemberReference type)
: base($"No copy constructor for {type.FullName} found") { }
}

public abstract class DeepCopyException : WeavingException
{
protected DeepCopyException(string message) : base(message) { }

public MemberReference ProcessingType { private get; set; }

public override string Message => (ProcessingType == null ? "" : $"{ProcessingType.FullName} -> ") + base.Message;
}
}
Loading

0 comments on commit b3e3059

Please sign in to comment.