Skip to content

Commit

Permalink
Factored duplicated logic into a TypeAssemblyReferenceProvider method.
Browse files Browse the repository at this point in the history
  • Loading branch information
alistairjevans committed Feb 25, 2023
1 parent 6e00847 commit 7a3aaee
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 58 deletions.
34 changes: 8 additions & 26 deletions src/Autofac/Util/Cache/ReflectionCacheDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,36 +27,18 @@ public void Clear(ReflectionCacheClearPredicate predicate)
throw new ArgumentNullException(nameof(predicate));
}

HashSet<Assembly>? reusableAssemblySet = null;

foreach (var kvp in this)
if (Count == 0)
{
if (reusableAssemblySet is null)
{
reusableAssemblySet = new();
}
else
{
reusableAssemblySet.Clear();
}
return;
}

if (kvp.Key is Type keyType)
{
TypeAssemblyReferenceProvider.PopulateAllReferencedAssemblies(keyType, reusableAssemblySet);
var reusableAssemblySet = new HashSet<Assembly>();

if (predicate(kvp.Key, reusableAssemblySet))
{
TryRemove(kvp.Key, out _);
}
}
else if (kvp.Key.DeclaringType is Type declaredType)
foreach (var kvp in this)
{
if (predicate(kvp.Key, TypeAssemblyReferenceProvider.GetAllReferencedAssemblies(kvp.Key, reusableAssemblySet)))
{
TypeAssemblyReferenceProvider.PopulateAllReferencedAssemblies(declaredType, reusableAssemblySet);

if (predicate(kvp.Key, reusableAssemblySet))
{
TryRemove(kvp.Key, out _);
}
TryRemove(kvp.Key, out _);
}
}
}
Expand Down
33 changes: 8 additions & 25 deletions src/Autofac/Util/Cache/ReflectionCacheTupleDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,40 +23,23 @@ internal sealed class ReflectionCacheTupleDictionary<TKey, TValue>
/// <inheritdoc />
public void Clear(ReflectionCacheClearPredicate predicate)
{
HashSet<Assembly>? reusableAssemblySet = null;
if (Count == 0)
{
return;
}

var reusableAssemblySet = new HashSet<Assembly>();

foreach (var kvp in this)
{
reusableAssemblySet ??= new();

// Remove an item if *either* member of the tuple matches,
// for generic implementation type caches where the closed generic
// is in a different assembly to the open one.
if (predicate(kvp.Key.Item1, GetKeyAssemblies(kvp.Key.Item1, reusableAssemblySet)) ||
predicate(kvp.Key.Item2, GetKeyAssemblies(kvp.Key.Item2, reusableAssemblySet)))
if (predicate(kvp.Key.Item1, TypeAssemblyReferenceProvider.GetAllReferencedAssemblies(kvp.Key.Item1, reusableAssemblySet)) ||
predicate(kvp.Key.Item2, TypeAssemblyReferenceProvider.GetAllReferencedAssemblies(kvp.Key.Item2, reusableAssemblySet)))
{
TryRemove(kvp.Key, out _);
}
}
}

private static IEnumerable<Assembly> GetKeyAssemblies(TKey key, HashSet<Assembly> reusableSet)
{
reusableSet.Clear();

if (key is Type keyType)
{
TypeAssemblyReferenceProvider.PopulateAllReferencedAssemblies(keyType, reusableSet);

return reusableSet;
}
else if (key.DeclaringType is Type declaredType)
{
TypeAssemblyReferenceProvider.PopulateAllReferencedAssemblies(declaredType, reusableSet);

return reusableSet;
}

throw new InvalidOperationException("Impossible state");
}
}
38 changes: 32 additions & 6 deletions src/Autofac/Util/Cache/TypeAssemblyReferenceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,51 @@ namespace Autofac.Util.Cache;
internal static class TypeAssemblyReferenceProvider
{
/// <summary>
/// Get all distinct assemblies referenced by the given type.
/// Get all distinct assemblies referenced directly by <see cref="MemberInfo"/>, if that member is a type,
/// or the owning type of that member (if it's a field or property).
/// </summary>
/// <param name="inputType">The type to retrieve references for.</param>
/// <param name="memberInfo">The member to retrieve references for.</param>
/// <returns>The set of assemblies.</returns>
public static IEnumerable<Assembly> GetAllReferencedAssemblies(Type inputType)
public static IEnumerable<Assembly> GetAllReferencedAssemblies(MemberInfo memberInfo)
{
var set = new HashSet<Assembly>();

PopulateAllReferencedAssemblies(inputType, set);
GetAllReferencedAssemblies(memberInfo, set);

return set;
}

/// <summary>
/// Add to a provided <see cref="HashSet{T}"/> all the assemblies referenced directly by a <see cref="MemberInfo"/>, if that member is a type,
/// or the owning type of that member (if it's a field or property).
/// </summary>
/// <param name="memberInfo">The member to retrieve references for.</param>
/// <param name="holdingSet">A pre-allocated set to use for this purpose.</param>
/// <remarks>
/// The holding set is cleared each time this method is called.
/// </remarks>
public static IEnumerable<Assembly> GetAllReferencedAssemblies(MemberInfo memberInfo, HashSet<Assembly> holdingSet)
{
holdingSet.Clear();

if (memberInfo is Type keyType)
{
PopulateAllReferencedAssemblies(keyType, holdingSet);
}
else if (memberInfo.DeclaringType is Type declaredType)
{
PopulateAllReferencedAssemblies(declaredType, holdingSet);
}

return holdingSet;
}

/// <summary>
/// Add to a provided <see cref="HashSet{T}"/> all assemblies referenced by a given type.
/// </summary>
/// <param name="inputType">The type to reterieve references for.</param>
/// <param name="inputType">The type to retrieve references for.</param>
/// <param name="holdingSet">A set to add any assemblies to.</param>
public static void PopulateAllReferencedAssemblies(Type inputType, HashSet<Assembly> holdingSet)
private static void PopulateAllReferencedAssemblies(Type inputType, HashSet<Assembly> holdingSet)
{
if (inputType.IsArray && inputType.GetElementType() is Type elementType)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class TypeAssemblyReferenceProviderTests
[InlineData(typeof(IEnumerable<IIndex<int, Assert>>), new[] { typeof(IEnumerable<>), typeof(IIndex<,>), typeof(Assert) })]
[InlineData(typeof(DerivedClass), new[] { typeof(DerivedClass), typeof(RegistrationBuilder<,,>), typeof(Assert) })]
[InlineData(typeof(GenericDerivedClass<Assert>), new[] { typeof(DerivedClass), typeof(RegistrationBuilder<,,>), typeof(Assert), typeof(Mock) })]
public void SimpleType(Type inputType, Type[] expandedTypeAssemblies)
public void TypeReferencesCanBeDetermined(Type inputType, Type[] expandedTypeAssemblies)
{
var set = TypeAssemblyReferenceProvider.GetAllReferencedAssemblies(inputType);

Expand All @@ -31,6 +31,24 @@ public void SimpleType(Type inputType, Type[] expandedTypeAssemblies)
}
}

[Fact]
public void MemberInfoReferencesCanBeDetermined()
{
var memberInfo = typeof(PropertyOwner<ContainerBuilder>).GetProperty(nameof(PropertyOwner<ContainerBuilder>.Property));

var expectedResults = new[] { typeof(ContainerBuilder), typeof(PropertyOwner<>) };

var set = TypeAssemblyReferenceProvider.GetAllReferencedAssemblies(memberInfo);

Assert.Distinct(set);
Assert.Equal(expectedResults.Length, set.Count());

foreach (var item in expectedResults)
{
Assert.Contains(item, expectedResults);
}
}

private class DerivedClass
: RegistrationBuilder<Assert, SimpleActivatorData, SingleRegistrationStyle>
{
Expand All @@ -48,4 +66,9 @@ public GenericDerivedClass(Service defaultService, SimpleActivatorData activator
{
}
}

private class PropertyOwner<T>
{
public string Property { get; set; }
}
}

0 comments on commit 7a3aaee

Please sign in to comment.