Skip to content

Commit

Permalink
Solve same-assembly usage problem by moving to generic stub method
Browse files Browse the repository at this point in the history
  • Loading branch information
mikhailshilkov committed May 7, 2016
1 parent 1ddfcbf commit 94f6ad8
Show file tree
Hide file tree
Showing 19 changed files with 92 additions and 284 deletions.
1 change: 1 addition & 0 deletions AssemblyToProcess/AssemblyToProcess.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<Compile Include="PropertyCasing.cs" />
<Compile Include="PropertiesOfSameType.cs" />
<Compile Include="PrimitiveValues.cs" />
<Compile Include="InAssemblyUsage.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
9 changes: 9 additions & 0 deletions AssemblyToProcess/InAssemblyUsage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace AssemblyToProcess
{
public class InAssemblyUsage
{
public PrimitiveValues ChangeIntTo3(PrimitiveValues p) => p.With(3);

public PropertiesOfSameType ChangeValue1To33(PropertiesOfSameType p) => p.WithValue1(33);
}
}
5 changes: 3 additions & 2 deletions AssemblyToProcess/PrimitiveValues.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

using System;

namespace AssemblyToProcess
{
public class PrimitiveValues
Expand All @@ -16,6 +17,6 @@ public PrimitiveValues(int value1, string value2, long value3)

public long Value3 { get; }

public PrimitiveValues With(object value) => this;
public PrimitiveValues With<T>(T value) => this;
}
}
6 changes: 3 additions & 3 deletions AssemblyToProcess/PropertiesOfSameType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ public PropertiesOfSameType(int? value1, int? value2, int? value3)

public int? Value3 { get; }

public PropertiesOfSameType WithValue1(object value) => this;
public PropertiesOfSameType WithValue1(int? value) => this;

public PropertiesOfSameType WithValue2(object value) => this;
public PropertiesOfSameType WithValue2(int? value) => this;

public PropertiesOfSameType WithValue3(object value) => this;
public PropertiesOfSameType WithValue3(int? value) => this;
}
}
1 change: 0 additions & 1 deletion Content/ToBeDeleted.txt

This file was deleted.

1 change: 0 additions & 1 deletion NuGet/Fody_ToBeDeleted.txt

This file was deleted.

1 change: 0 additions & 1 deletion NuGet/Nuget.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
<MakeDir Directories="$(SolutionDir)NuGetBuild" />
<Copy SourceFiles="$(SolutionDir)NuGet\With.Fody.nuspec" DestinationFolder="$(SolutionDir)NuGetBuild" />
<Copy SourceFiles="$(SolutionDir)With.Fody\bin\$(ConfigurationName)\With.Fody.dll" DestinationFolder="$(SolutionDir)NuGetBuild" />
<Copy SourceFiles="$(SolutionDir)NuGet\Fody_ToBeDeleted.txt" DestinationFolder="$(SolutionDir)NuGetBuild\Content" />
<Copy SourceFiles="$(ProjectDir)install.ps1" DestinationFolder="$(SolutionDir)NuGetBuild\Tools" />
<Copy SourceFiles="$(ProjectDir)uninstall.ps1" DestinationFolder="$(SolutionDir)NuGetBuild\Tools" />
<PepitaPackage.CreatePackageTask NuGetBuildDirectory="$(SolutionDir)NuGetBuild" MetadataAssembly="$(SolutionDir)With.Fody\bin\$(ConfigurationName)\With.Fody.dll" />
Expand Down
22 changes: 0 additions & 22 deletions NuGet/WithFodyAddin.Fody.nuspec

This file was deleted.

13 changes: 0 additions & 13 deletions NuGet/install.ps1
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
param($installPath, $toolsPath, $package, $project)


function RemoveForceProjectLevelHack($project)
{
Write-Host "RemoveForceProjectLevelHack"
Foreach ($item in $project.ProjectItems)
{
if ($item.Name -eq "Fody_ToBeDeleted.txt")
{
$item.Delete()
}
}
}

function FlushVariables()
{
Expand Down Expand Up @@ -87,8 +76,6 @@ function UnlockWeaversXml($project)

UnlockWeaversXml($project)

RemoveForceProjectLevelHack $project

Update-FodyConfig $package.Id.Replace(".Fody", "") $project

Fix-ReferencesCopyLocal $package $project
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ https://nuget.org/packages/With.Fody/
public OtherClass C2 { get; }

// Needed for IntelliSense/Resharper support
public MyClass With(int value) => this;
public MyClass With<T>(T value) => this;
// If two properties have same type, we need to append the property name to With
public MyClass WithC1(OtherClass value) => this;
public MyClass WithC2(OtherClass value) => this;
Expand Down
20 changes: 16 additions & 4 deletions Tests/WeaverTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,26 @@ public void UnusualPropertyCasing_WithIsInjectedAnyway()
Assert.AreEqual("World", result2.vaLue2);
}

[Test]
public void InAssemblyUsage_Works()
{
var type = assembly.GetType("AssemblyToProcess.PrimitiveValues");
var instance = (dynamic)Activator.CreateInstance(type, new object[] { 1, "Hello", (long)234234 });
var runner = (dynamic)Activator.CreateInstance(assembly.GetType("AssemblyToProcess.InAssemblyUsage"));
var result = runner.ChangeIntTo3(instance);
Assert.AreEqual(3, result.Value1);

type = assembly.GetType("AssemblyToProcess.PropertiesOfSameType");
instance = (dynamic)Activator.CreateInstance(type, new object[] { 1, 2, 3 });
result = runner.ChangeValue1To33(instance);
Assert.AreEqual(33, result.Value1);
}

[Test]
public void OriginalWithMethodIsRemoved()
{
var type1 = assembly.GetType("AssemblyToProcess.PrimitiveValues");
Assert.False(type1.GetMethods().Any(m => m.Name == "With" && m.GetParameters()[0].ParameterType == typeof(object)));

var type2 = assembly.GetType("AssemblyToProcess.PropertiesOfSameType");
Assert.False(type2.GetMethods().Any(m => m.Name.StartsWith("With") && m.GetParameters()[0].ParameterType == typeof(object)));
Assert.False(type1.GetMethods().Any(m => m.Name == "With" && m.IsGenericMethod));
}

#if(DEBUG)
Expand Down
4 changes: 2 additions & 2 deletions With.Fody/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

[assembly: AssemblyTitle("With.Fody")]
[assembly: AssemblyProduct("With.Fody")]
[assembly: AssemblyVersion("0.2.0")]
[assembly: AssemblyFileVersion("0.2.0")]
[assembly: AssemblyVersion("0.3.0")]
[assembly: AssemblyFileVersion("0.3.0")]
66 changes: 57 additions & 9 deletions With.Fody/ModuleWeaver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ public void Execute()
{
foreach (var type in ModuleDefinition.Types.Where(CanHaveWith))
{
RemoveWith(type);
AddWith(type);
RemoveGenericWith(type);
LogInfo($"Added method 'With' to type '{type.Name}'.");
}
}
Expand Down Expand Up @@ -51,9 +51,9 @@ private bool CanHaveWith(TypeDefinition type)
return parameters.All(par => type.Properties.Any(pro => string.Compare(par.Name, pro.Name, StringComparison.InvariantCultureIgnoreCase) == 0));
}

private void RemoveWith(TypeDefinition type)
private void RemoveGenericWith(TypeDefinition type)
{
foreach (var method in type.GetMethods().Where(m => m.Name.StartsWith("With")).ToArray())
foreach (var method in type.GetMethods().Where(m => m.Name == "With" && m.HasGenericParameters).ToArray())
{
type.Methods.Remove(method);
}
Expand All @@ -65,15 +65,30 @@ private void AddWith(TypeDefinition type)
foreach (var property in ctor.Parameters)
{
var parameterName = property.Name;
var getter = type.GetMethods().First(m => string.Compare(m.Name, $"get_{property.Name}", StringComparison.InvariantCultureIgnoreCase) == 0);
var getter = type.Methods.First(m => string.Compare(m.Name, $"get_{property.Name}", StringComparison.InvariantCultureIgnoreCase) == 0);

string propertyName = ToPropertyName(property.Name);
var methodName = ctor.Parameters.Except(new[] { property }).Any(p => p.ParameterType.FullName == property.ParameterType.FullName)
? $"With{propertyName}" : "With";
var method = new MethodDefinition(methodName, MethodAttributes.Public, type);
method.Parameters.Add(new ParameterDefinition(parameterName, ParameterAttributes.None, getter.ReturnType));
MethodDefinition method;
bool existing = false;
if (ctor.Parameters.Except(new[] { property }).Any(p => p.ParameterType.FullName == property.ParameterType.FullName))
{
var methodName = $"With{propertyName}";
method = type.Methods.FirstOrDefault(m => m.Name == methodName);
if (method == null)
continue;
}
else
{
method = new MethodDefinition("With", MethodAttributes.Public, type);
method.Parameters.Add(new ParameterDefinition(parameterName, ParameterAttributes.None, getter.ReturnType));
type.Methods.Add(method);
}

var processor = method.Body.GetILProcessor();
foreach (var i in processor.Body.Instructions.ToArray())
{
processor.Remove(i);
}
foreach (var parameter in ctor.Parameters)
{
if (parameter.Name == parameterName)
Expand All @@ -89,10 +104,43 @@ private void AddWith(TypeDefinition type)
}
processor.Emit(OpCodes.Newobj, ctor);
processor.Emit(OpCodes.Ret);
type.Methods.Add(method);
method.Body.OptimizeMacros();

this.ReplaceCalls(type, method, getter.ReturnType);
}
}

private void ReplaceCalls(TypeDefinition withType, MethodDefinition newMethod, TypeReference argumentType)
{
foreach (var type in ModuleDefinition.Types)
{
foreach (var method in type.Methods.Where(x => x.Body != null))
{
var calls = method.Body.Instructions.Where(i => i.OpCode == OpCodes.Callvirt);
foreach (var call in calls)
{
var originalMethodReference = (MethodReference)call.Operand;
if (originalMethodReference.IsGenericInstance)
{
var genericMethodReference = originalMethodReference as GenericInstanceMethod;
var originalMethodDefinition = originalMethodReference.Resolve();
var declaringTypeReference = originalMethodReference.DeclaringType;
var declaringTypeDefinition = declaringTypeReference.Resolve();

if (declaringTypeDefinition.FullName == withType.FullName
&& originalMethodDefinition.Name == newMethod.Name
&& genericMethodReference.GenericArguments[0] == argumentType)
{
call.Operand = ModuleDefinition.ImportReference(newMethod);
LogInfo($"Found a call");
}
}
}
}
}
}


private string ToPropertyName(string fieldName)
{
return Char.ToUpperInvariant(fieldName[0]) + fieldName.Substring(1);
Expand Down
51 changes: 0 additions & 51 deletions WithFodyAddin.sln

This file was deleted.

3 changes: 0 additions & 3 deletions WithFodyAddin.sln.DotSettings

This file was deleted.

6 changes: 0 additions & 6 deletions WithFodyAddin/AssemblyInfo.cs

This file was deleted.

Loading

0 comments on commit 94f6ad8

Please sign in to comment.