Skip to content

Commit

Permalink
Adding support for amending constructors
Browse files Browse the repository at this point in the history
  • Loading branch information
jamiemthomas committed Apr 14, 2011
1 parent a45f981 commit 95a0e74
Show file tree
Hide file tree
Showing 9 changed files with 351 additions and 48 deletions.
55 changes: 49 additions & 6 deletions Afterthought.Amender/AssemblyAmender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -491,8 +491,35 @@ void AmendConstructor(IConstructorAmendment constructorAmendment, MethodBody met
// Create an IL generator to amend the operations
var il = new ILAmender(host, methodBody);

// Emit the original method operations
il.EmitUntilReturn();
// Determine if the constructor is being specifically amended or if there are just property initializers
if (constructorAmendment != null)
{
// Before
if (constructorAmendment.Before != null)
CallMethodDelegate(methodBody, constructorAmendment.Before, true, il, null);

// Implementation
if (constructorAmendment.Implementation != null)
{
// Clear the original method body
il.Operations.Clear();
CallMethodDelegate(methodBody, constructorAmendment.Implementation, false, il, null);
}

// Emit the original method operations if the method implementation was not overriden
if (constructorAmendment.ConstructorInfo != null && constructorAmendment.Implementation == null)
il.EmitUntilReturn();

// After
if (constructorAmendment.After != null)
CallMethodDelegate(methodBody, constructorAmendment.After, false, il, null);

// Or emit a return for new/overriden methods
if (constructorAmendment.Implementation != null)
il.Emit(OperationCode.Ret);
}
else
il.EmitUntilReturn();

// Emit property initializers if the current constructor calls a base constructor or inherits from System.Object
if (GetCurrentType().BaseClasses.Any(b => TypeHelper.TypesAreEquivalent(b, host.PlatformType.SystemObject)) ||
Expand Down Expand Up @@ -1191,7 +1218,22 @@ IMethodDefinition ResolveMethod(ITypeDefinition declaringType, System.Reflection
/// <returns></returns>
internal static bool AreEquivalent(INamedTypeDefinition typeDef, Type type)
{
return typeDef.ToString() + (typeDef.MangleName ? "`" + typeDef.GenericParameterCount : "") == type.FullName;

return GetTypeName(typeDef) == type.FullName;
}

/// <summary>
/// Gets the name of the specified <see cref="INamedTypeDefinition"/> consistent with the naming
/// conventions used by the System.Reflection namespace.
/// </summary>
/// <param name="typeDef"></param>
/// <returns></returns>
static string GetTypeName(INamedTypeDefinition typeDef)
{
if (typeDef is INestedTypeDefinition)
return GetTypeName((INamedTypeDefinition)((INestedTypeDefinition)typeDef).ContainingTypeDefinition) + "+" + typeDef.Name.Value + (typeDef.MangleName ? "`" + typeDef.GenericParameterCount : "");
else
return typeDef.ToString() + (typeDef.MangleName ? "`" + typeDef.GenericParameterCount : "");
}

/// <summary>
Expand Down Expand Up @@ -1284,13 +1326,14 @@ GenericMethod ResolvePropertyDelegate(IPropertyDefinition propertyDef, System.Re

IMethodDefinition ResolveMethodDelegate(ITypeReference instanceType, System.Reflection.MethodInfo method)
{
var genericType = ResolveType(method.DeclaringType.GetGenericTypeDefinition());
var declaringType = ResolveType(method.DeclaringType.IsGenericType ? method.DeclaringType.GetGenericTypeDefinition() : method.DeclaringType);

// Create a concrete type instance
var concreteType = GenericTypeInstance.GetGenericTypeInstance(genericType, new ITypeReference[] { instanceType }, host.InternFactory);
if (declaringType.IsGeneric)
declaringType = GenericTypeInstance.GetGenericTypeInstance(declaringType, new ITypeReference[] { instanceType }, host.InternFactory);

// Then get the Amendment method
return concreteType.Methods.Where(m => m.Name.Value == method.Name && m.ParameterCount == method.GetParameters().Length).FirstOrDefault();
return declaringType.Methods.Where(m => m.Name.Value == method.Name && m.ParameterCount == method.GetParameters().Length).FirstOrDefault();
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
<Compile Include="IMath.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Calculator.cs" />
<Compile Include="Test.cs" />
<Compile Include="TestAmendment.cs" />
</ItemGroup>
<ItemGroup>
Expand All @@ -64,6 +65,7 @@
<PropertyGroup>
</PropertyGroup>
<PropertyGroup>
<PostBuildEvent>$(SolutionDir)Afterthought.Amender/bin/debug/Afterthought.Amender.exe "$(TargetPath)"</PostBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>
6 changes: 6 additions & 0 deletions Afterthought.UnitTest.Target/Calculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Afterthought.UnitTest.Target
{
Expand Down Expand Up @@ -174,5 +175,10 @@ public long Sum3(int[] values)
{
return 0;
}

public void LogMe()
{
Thread.Sleep(100);
}
}
}
21 changes: 21 additions & 0 deletions Afterthought.UnitTest.Target/Test.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

namespace Afterthought.UnitTest.Target
{
[Afterthought.Amendment(typeof(amend<>))]
public class junk
{
public static void Main()
{
Console.WriteLine("Test");
}
}

public class amend<T> : Amendment<T, T>
{
public override void Amend()
{
AddProperty(new Property<int>("Count"));
}
}
}
17 changes: 16 additions & 1 deletion Afterthought.UnitTest.Target/TestAmendment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,22 @@ public override void Amend(Method method)
((int[])parameters[0])[i] = ((int[])parameters[0])[i - 1] + ((int[])parameters[0])[i];
});
break;

case "LogMe":

method.Context<LogTracker>((m, t) =>
{
m.Before((instance, methodName) => t.Split = DateTime.Now);
m.After((instance, methodName) => Console.WriteLine(DateTime.Now.Subtract(t.Split)));
});

break;
}
}
}
}

public class LogTracker
{
public DateTime Split { get; set; }
}
}
1 change: 1 addition & 0 deletions Afterthought.UnitTest/Afterthought.UnitTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
</CodeAnalysisDependentAssemblyPaths>
</ItemGroup>
<ItemGroup>
<Compile Include="ConstructorTests.cs" />
<Compile Include="MethodTests.cs" />
<Compile Include="InterfaceTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand Down
103 changes: 103 additions & 0 deletions Afterthought.UnitTest/ConstructorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//-----------------------------------------------------------------------------
//
// Copyright (c) VC3, Inc. All rights reserved.
// This code is licensed under the Microsoft Public License.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//-----------------------------------------------------------------------------

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Afterthought.UnitTest.Target;

namespace Afterthought.UnitTest
{
[TestClass]
public class ConstructorTests
{
Calculator Calculator { get; set; }

[TestInitialize]
public void InitializeCalculator()
{
Calculator = new Calculator();
}

/// <summary>
/// Tests adding a new public constructor to a type.
/// </summary>
[TestMethod]
public void AddConstructor()
{

}

/// <summary>
/// Tests modifying an existing constructor to run code before the original method
/// implementation without affecting the values of the specified parameters.
/// </summary>
[TestMethod]
public void BeforeConstructorWithoutChangesGeneric()
{
}

/// <summary>
/// Tests modifying an existing constructor to run code before the original method
/// implementation without affecting the values of the specified parameters.
/// </summary>
[TestMethod]
public void BeforeConstructorWithoutChangesArray()
{
}

/// <summary>
/// Tests modifying an existing constructor to run code before the original method
/// implementation without affecting the values of the specified parameters.
/// </summary>
[TestMethod]
public void BeforeConstructorWithChangesGeneric()
{
}

/// <summary>
/// Tests modifying an existing constructor to run code before the original method
/// implementation without affecting the values of the specified parameters.
/// </summary>
[TestMethod]
public void BeforeConstructorWithChangesArray()
{
}

/// <summary>
/// Tests modifying an existing constructor to run code after the original method
/// implementation that does not return a value.
/// </summary>
[TestMethod]
public void AfterConstructorGeneric()
{
}

/// <summary>
/// Tests modifying an existing constructor to run code after the original method
/// implementation that does not return a value.
/// </summary>
[TestMethod]
public void AfterConstructorArray()
{
}

/// <summary>
/// Tests modifying an existing constructor to completely replace the implementation.
/// </summary>
[TestMethod]
public void ImplementConstructor()
{
}
}
}
Loading

0 comments on commit 95a0e74

Please sign in to comment.