Skip to content

Instantly share code, notes, and snippets.

@thomaslevesque
Created September 29, 2019 09:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thomaslevesque/a4310070de3d44f81144aafd136bea7c to your computer and use it in GitHub Desktop.
Save thomaslevesque/a4310070de3d44f81144aafd136bea7c to your computer and use it in GitHub Desktop.
private static void ConfigureFakeToCallDefaultImplementations(object fake)
{
var fakedType = Fake.GetFakeManager(fake).FakeObjectType;
if (!fakedType.GetTypeInfo().IsInterface)
{
throw new FakeConfigurationException("The faked type is not an interface.");
}
var methodsWithDefaultImplementation = fakedType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(m => !m.IsPrivate && !m.IsAbstract)
.ToList();
var paramTypes = new[] { typeof(object), typeof(object[]) };
var nonVirtualImplementations = new Dictionary<MethodInfo, Func<object, object[], object>>();
foreach (var method in methodsWithDefaultImplementation)
{
var dm = new DynamicMethod(
method.Name + "_NonVirtualInvoker",
typeof(object),
paramTypes.ToArray(),
typeof(FakeOptionsExtensions));
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, fakedType);
var methodParameters = method.GetParameters();
for (int i = 0; i < methodParameters.Length; i++)
{
var paramType = methodParameters[i].ParameterType;
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldelem_Ref);
if (paramType.GetTypeInfo().IsValueType)
{
il.Emit(OpCodes.Unbox_Any, paramType);
}
else
{
il.Emit(OpCodes.Castclass, paramType);
}
}
il.EmitCall(OpCodes.Call, method, null);
if (method.ReturnType.GetTypeInfo().IsValueType)
{
il.Emit(OpCodes.Box, method.ReturnType);
}
il.Emit(OpCodes.Ret);
nonVirtualImplementations[method] = (Func<object, object[], object>)dm.CreateDelegate(typeof(Func<object, object[], object>));
}
A.CallTo(fake).Where(call => nonVirtualImplementations.ContainsKey(call.Method))
.WithVoidReturnType()
.Invokes(call => nonVirtualImplementations[call.Method].Invoke(fake, call.Arguments.GetUnderlyingArgumentsArray()));
A.CallTo(fake).Where(call => methodsWithDefaultImplementation.Contains(call.Method))
.WithNonVoidReturnType()
.ReturnsLazily(call => nonVirtualImplementations[call.Method].Invoke(fake, call.Arguments.GetUnderlyingArgumentsArray()));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment