Skip to content

Commit

Permalink
Don't needlessly reinstantiate intercept strategies
Browse files Browse the repository at this point in the history
  • Loading branch information
stakx committed Sep 25, 2017
1 parent 9e46801 commit 99da297
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 83 deletions.
30 changes: 17 additions & 13 deletions Source/Interceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,24 +127,28 @@ internal void ClearCalls()
}
}

private IEnumerable<IInterceptStrategy> InterceptionStrategies()
{
yield return new HandleDestructor();
yield return new HandleTracking();
yield return new InterceptMockPropertyMixin();
yield return new InterceptObjectMethodsMixin();
yield return new AddActualInvocation();
yield return new ExtractProxyCall();
yield return new ExecuteCall();
yield return new InvokeBase();
yield return new HandleMockRecursion();
}
private static Lazy<IInterceptStrategy[]> interceptionStrategies =
new Lazy<IInterceptStrategy[]>(
() => new IInterceptStrategy[]
{
HandleDestructor.Instance,
HandleTracking.Instance,
InterceptMockPropertyMixin.Instance,
InterceptObjectMethodsMixin.Instance,
AddActualInvocation.Instance,
ExtractProxyCall.Instance,
ExecuteCall.Instance,
InvokeBase.Instance,
HandleMockRecursion.Instance,
});

private static IInterceptStrategy[] InterceptionStrategies => interceptionStrategies.Value;

[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
public void Intercept(ICallContext invocation)
{
CurrentInterceptContext localCtx = new CurrentInterceptContext();
foreach (var strategy in InterceptionStrategies())
foreach (var strategy in InterceptionStrategies)
{
if (InterceptionAction.Stop == strategy.HandleIntercept(invocation, InterceptionContext, localCtx))
{
Expand Down
148 changes: 78 additions & 70 deletions Source/InterceptorStrategies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ namespace Moq
{
internal class HandleMockRecursion : IInterceptStrategy
{
public static HandleMockRecursion Instance { get; } = new HandleMockRecursion();

public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx)
{
if (invocation.Method != null && invocation.Method.ReturnType != null &&
Expand All @@ -31,6 +33,8 @@ public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorCo

internal class InvokeBase : IInterceptStrategy
{
public static InvokeBase Instance { get; } = new InvokeBase();

public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx)
{
if (invocation.Method.DeclaringType == typeof(object) || // interface proxy
Expand All @@ -56,10 +60,10 @@ public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorCo

internal class ExecuteCall : IInterceptStrategy
{
InterceptorContext ctx;
public static ExecuteCall Instance { get; } = new ExecuteCall();

public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx)
{
this.ctx = ctx;
IProxyCall currentCall = localctx.Call;

if (currentCall != null)
Expand All @@ -70,15 +74,16 @@ public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorCo
// and therefore we might never get to the
// next line.
currentCall.Execute(invocation);
ThrowIfReturnValueRequired(currentCall, invocation);
ThrowIfReturnValueRequired(currentCall, invocation, ctx);
return InterceptionAction.Stop;
}
else
{
return InterceptionAction.Continue;
}
}
private void ThrowIfReturnValueRequired(IProxyCall call, ICallContext invocation)

private static void ThrowIfReturnValueRequired(IProxyCall call, ICallContext invocation, InterceptorContext ctx)
{
if (ctx.Behavior != MockBehavior.Loose &&
invocation.Method != null &&
Expand All @@ -98,6 +103,7 @@ private void ThrowIfReturnValueRequired(IProxyCall call, ICallContext invocation

internal class ExtractProxyCall : IInterceptStrategy
{
public static ExtractProxyCall Instance { get; } = new ExtractProxyCall();

public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx)
{
Expand Down Expand Up @@ -134,11 +140,12 @@ public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorCo

return InterceptionAction.Continue;
}

}

internal class InterceptMockPropertyMixin : IInterceptStrategy
{
public static InterceptMockPropertyMixin Instance { get; } = new InterceptMockPropertyMixin();

public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx)
{
var method = invocation.Method;
Expand All @@ -158,6 +165,8 @@ public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorCo
/// </summary>
internal class InterceptObjectMethodsMixin : IInterceptStrategy
{
public static InterceptObjectMethodsMixin Instance { get; } = new InterceptObjectMethodsMixin();

public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx)
{
var method = invocation.Method;
Expand Down Expand Up @@ -186,19 +195,15 @@ public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorCo
return InterceptionAction.Continue;
}

protected bool IsObjectMethod(MethodInfo method, string name)
private static bool IsObjectMethod(MethodInfo method, string name)
{
if (method.DeclaringType == typeof(object) && method.Name == name)
{
return true;
}

return false;
return method.DeclaringType == typeof(object) && method.Name == name;
}
}

internal class HandleTracking : IInterceptStrategy
{
public static HandleTracking Instance { get; } = new HandleTracking();

public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx)
{
Expand All @@ -213,6 +218,8 @@ public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorCo

internal class HandleDestructor : IInterceptStrategy
{
public static HandleDestructor Instance { get; } = new HandleDestructor();

public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx)
{
return invocation.Method.IsDestructor() ? InterceptionAction.Stop : InterceptionAction.Continue;
Expand All @@ -221,71 +228,16 @@ public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorCo

internal class AddActualInvocation : IInterceptStrategy
{
public static AddActualInvocation Instance { get; } = new AddActualInvocation();

/// <summary>
/// Get an eventInfo for a given event name. Search type ancestors depth first if necessary.
/// </summary>
/// <param name="eventName">Name of the event, with the set_ or get_ prefix already removed</param>
private EventInfo GetEventFromName(string eventName)
{
return GetEventFromName(eventName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public) ??
GetEventFromName(eventName, BindingFlags.Instance | BindingFlags.NonPublic);
}

/// <summary>
/// Get an eventInfo for a given event name. Search type ancestors depth first if necessary.
/// Searches events using the specified binding constraints.
/// </summary>
/// <param name="eventName">Name of the event, with the set_ or get_ prefix already removed</param>
/// <param name="bindingAttr">Specifies how the search for events is conducted</param>
private EventInfo GetEventFromName(string eventName, BindingFlags bindingAttr)
{
// Ignore internally implemented interfaces
var depthFirstProgress = new Queue<Type>(ctx.Mock.ImplementedInterfaces.Skip(ctx.Mock.InternallyImplementedInterfaceCount));
depthFirstProgress.Enqueue(ctx.TargetType);
while (depthFirstProgress.Count > 0)
{
var currentType = depthFirstProgress.Dequeue();
var eventInfo = currentType.GetEvent(eventName, bindingAttr);
if (eventInfo != null)
{
return eventInfo;
}

foreach (var implementedType in GetAncestorTypes(currentType))
{
depthFirstProgress.Enqueue(implementedType);
}
}

return null;
}


/// <summary>
/// Given a type return all of its ancestors, both types and interfaces.
/// </summary>
/// <param name="initialType">The type to find immediate ancestors of</param>
private static IEnumerable<Type> GetAncestorTypes(Type initialType)
{
var baseType = initialType.GetTypeInfo().BaseType;
if (baseType != null)
{
return new[] { baseType };
}

return initialType.GetInterfaces();
}
InterceptorContext ctx;
public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx)
{
this.ctx = ctx;
if (!FluentMockContext.IsActive)
{
//Special case for events
if (invocation.Method.LooksLikeEventAttach())
{
var eventInfo = this.GetEventFromName(invocation.Method.Name.Substring("add_".Length));
var eventInfo = GetEventFromName(invocation.Method.Name.Substring("add_".Length), ctx);
if (eventInfo != null)
{
// TODO: We could compare `invocation.Method` and `eventInfo.GetAddMethod()` here.
Expand All @@ -309,7 +261,7 @@ public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorCo
}
else if (invocation.Method.LooksLikeEventDetach())
{
var eventInfo = this.GetEventFromName(invocation.Method.Name.Substring("remove_".Length));
var eventInfo = GetEventFromName(invocation.Method.Name.Substring("remove_".Length), ctx);
if (eventInfo != null)
{
// TODO: We could compare `invocation.Method` and `eventInfo.GetRemoveMethod()` here.
Expand Down Expand Up @@ -341,5 +293,61 @@ public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorCo
}
return InterceptionAction.Continue;
}

/// <summary>
/// Get an eventInfo for a given event name. Search type ancestors depth first if necessary.
/// </summary>
/// <param name="eventName">Name of the event, with the set_ or get_ prefix already removed</param>
/// <param name="ctx"/>
private static EventInfo GetEventFromName(string eventName, InterceptorContext ctx)
{
return GetEventFromName(eventName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public, ctx)
?? GetEventFromName(eventName, BindingFlags.Instance | BindingFlags.NonPublic, ctx);
}

/// <summary>
/// Get an eventInfo for a given event name. Search type ancestors depth first if necessary.
/// Searches events using the specified binding constraints.
/// </summary>
/// <param name="eventName">Name of the event, with the set_ or get_ prefix already removed</param>
/// <param name="bindingAttr">Specifies how the search for events is conducted</param>
/// <param name="ctx"/>
private static EventInfo GetEventFromName(string eventName, BindingFlags bindingAttr, InterceptorContext ctx)
{
// Ignore internally implemented interfaces
var depthFirstProgress = new Queue<Type>(ctx.Mock.ImplementedInterfaces.Skip(ctx.Mock.InternallyImplementedInterfaceCount));
depthFirstProgress.Enqueue(ctx.TargetType);
while (depthFirstProgress.Count > 0)
{
var currentType = depthFirstProgress.Dequeue();
var eventInfo = currentType.GetEvent(eventName, bindingAttr);
if (eventInfo != null)
{
return eventInfo;
}

foreach (var implementedType in GetAncestorTypes(currentType))
{
depthFirstProgress.Enqueue(implementedType);
}
}

return null;
}

/// <summary>
/// Given a type return all of its ancestors, both types and interfaces.
/// </summary>
/// <param name="initialType">The type to find immediate ancestors of</param>
private static IEnumerable<Type> GetAncestorTypes(Type initialType)
{
var baseType = initialType.GetTypeInfo().BaseType;
if (baseType != null)
{
return new[] { baseType };
}

return initialType.GetInterfaces();
}
}
}

0 comments on commit 99da297

Please sign in to comment.