Skip to content

Commit

Permalink
Centralize check for potential to do reflection activation.
Browse files Browse the repository at this point in the history
  • Loading branch information
tillig committed Aug 4, 2023
1 parent 80d1978 commit aab6734
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Autofac.Builder;
using Autofac.Core;
using Autofac.Core.Registration;
using Autofac.Util;

namespace Autofac.Features.ResolveAnything;

Expand Down Expand Up @@ -55,9 +56,7 @@ public IEnumerable<IComponentRegistration> RegistrationsFor(
}

var serviceType = ts.ServiceType;
if (!serviceType.IsClass ||
serviceType.IsSubclassOf(typeof(Delegate)) ||
serviceType.IsAbstract ||
if (!serviceType.MayAllowReflectionActivation(allowCompilerGenerated: true) ||
serviceType.IsGenericTypeDefinition ||
!_predicate(ts.ServiceType) ||
registrationAccessor(service).Any())
Expand Down
2 changes: 1 addition & 1 deletion src/Autofac/Features/Scanning/AssemblyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ internal static IEnumerable<Type> GetPermittedTypesForAssemblyScanning(this Asse
static IReadOnlyList<Type> Uncached(Assembly assembly)
{
return assembly.GetLoadableTypes()
.WhichCanBeRegistered()
.WhichAreAllowedThroughScanning()
.ToList();
}

Expand Down
26 changes: 5 additions & 21 deletions src/Autofac/Features/Scanning/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,13 @@ internal static IEnumerable<Type> AllowedByActivatorFilters<TActivatorData, TReg
/// <returns>
/// A filtered set of types that remove non-concrete types (interfaces, abstract classes, etc.).
/// </returns>
internal static IEnumerable<Type> WhichCanBeRegistered(this IEnumerable<Type> types) => types.Where(t => t.IsRegisterableType());

/// <summary>
/// Determines if a type is a concrete type that is allowed to be registered during scanning.
/// </summary>
/// <param name="type">
/// The type to check.
/// </param>
/// <returns>
/// <see langword="true"/> if the type is allowed to be registered during scanning based on its reflection attributes; otherwise <see langword="false"/>.
/// </returns>
// Issue #897: For back compat reasons we can't filter out
// non-public types here. Folks use assembly scanning on their
// own stuff, so encapsulation is a tricky thing to manage.
// If people want only public types, a LINQ Where clause can be used.
//
// Run IsCompilerGenerated check last due to perf. See AssemblyScanningPerformanceTests.MeasurePerformance.
internal static bool IsRegisterableType(this Type? type) => type != null && type.IsClass && !type.IsAbstract && !type.IsDelegate() && !type.IsCompilerGenerated();
internal static IEnumerable<Type> WhichAreAllowedThroughScanning(this IEnumerable<Type> types) => types.Where(t => t.MayAllowReflectionActivation());

/// <summary>
/// Filters a list of types down to only those that are concrete
/// (<see cref="IsRegisterableType"/>), not open generic, and allowed by the
/// provided activator data. Registers those types into the provided
/// (<see cref="InternalTypeExtensions.MayAllowReflectionActivation"/>), not open generic,
/// and allowed by the provided activator data. Registers those types into
/// the provided
/// registry.
/// </summary>
/// <param name="types">
Expand All @@ -75,7 +59,7 @@ internal static IEnumerable<Type> AllowedByActivatorFilters<TActivatorData, TReg
/// </param>
internal static void FilterAndRegisterConcreteTypes(this IEnumerable<Type> types, IComponentRegistryBuilder cr, IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle> rb)
{
var closedTypes = types.WhichCanBeRegistered()
var closedTypes = types.WhichAreAllowedThroughScanning()
.Where(t => !t.IsGenericTypeDefinition)
.AllowedByActivatorFilters(rb.ActivatorData);

Expand Down
34 changes: 31 additions & 3 deletions src/Autofac/Util/InternalTypeExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Autofac Project. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System.Collections.Concurrent;
using System.Reflection;
using System.Runtime.CompilerServices;
using Autofac.Core;
Expand Down Expand Up @@ -149,7 +148,7 @@ static bool Uncached(Type type)
}

/// <summary>
/// Checks whether a given type is a generic list of colleciton interface type, e.g. <see cref="IList{T}"/>, <see cref="ICollection{T}"/> and the read-only variants.
/// Checks whether a given type is a generic list of collection interface type, e.g. <see cref="IList{T}"/>, <see cref="ICollection{T}"/> and the read-only variants.
/// </summary>
/// <param name="type">The type to check.</param>
/// <returns>True if the type is one of the supported list/collection types.</returns>
Expand All @@ -168,7 +167,7 @@ static bool Uncached(Type type)
}

/// <summary>
/// Checks whether a given type is a closed generic defined by the specifed open generic.
/// Checks whether a given type is a closed generic defined by the specified open generic.
/// </summary>
/// <param name="this">The type to check.</param>
/// <param name="openGeneric">The open generic to check against.</param>
Expand Down Expand Up @@ -214,6 +213,33 @@ public static bool IsOpenGenericTypeOf(this Type @this, Type type)
|| @this.CheckInterfacesAreOpenGenericTypeOf(type);
}

/// <summary>
/// Filters off interfaces, abstract types, and generally non-registerable
/// types. Largely used during type/assembly scanning, but also can
/// determine if this is something we can activate by reflection.
/// Intentionally does NOT filter out open generic definitions because this
/// gets used in both concrete and open generic scanning.
/// </summary>
/// <param name="type">
/// The type to check.
/// </param>
/// <param name="allowCompilerGenerated">
/// <see langword="true"/> to allow compiler-generated types to be
/// considered registerable. Defaults to <see langword="false"/>.
/// </param>
/// <returns>
/// <see langword="true"/> if the type is allowed to be registered during
/// scanning based on its reflection attributes; otherwise <see
/// langword="false"/>.
/// </returns>
// Issue #897: For back compat reasons we can't filter out
// non-public types here. Folks use assembly scanning on their
// own stuff, so encapsulation is a tricky thing to manage.
// If people want only public types, a LINQ Where clause can be used.
//
// Run IsCompilerGenerated check last due to perf. See AssemblyScanningPerformanceTests.MeasurePerformance.
internal static bool MayAllowReflectionActivation(this Type? type, bool allowCompilerGenerated = false) => type != null && type.IsClass && !type.IsAbstract && !type.IsDelegate() && (allowCompilerGenerated || !type.IsCompilerGenerated());

private static bool CheckBaseTypeIsOpenGenericTypeOf(this Type @this, Type type)
{
if (@this.BaseType == null)
Expand Down Expand Up @@ -287,7 +313,9 @@ private static bool ParameterCompatibleWithTypeConstraint(Type parameter, Type c
.Any(p => ParameterEqualsConstraint(p, constraint));
}

#if NET6_0_OR_GREATER
[SuppressMessage("Microsoft.Design", "CA1031", Justification = "Implementing a real TryMakeGenericType is not worth the effort.")]
#endif
private static bool ParameterEqualsConstraint(Type parameter, Type constraint)
{
var genericArguments = parameter.GenericTypeArguments;
Expand Down

0 comments on commit aab6734

Please sign in to comment.