Skip to content

Commit

Permalink
More efficient reflection (#4263)
Browse files Browse the repository at this point in the history
  • Loading branch information
Youssef1313 authored Dec 7, 2024
1 parent 8dc8b14 commit e101a9d
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ internal ICollection<UnitTestElement> EnumerateAssembly(string assemblyFileName,

Assembly assembly = PlatformServiceProvider.Instance.FileOperations.LoadAssembly(assemblyFileName, isReflectionOnly: false);

IReadOnlyList<Type> types = GetTypes(assembly, assemblyFileName, warningMessages);
Type[] types = GetTypes(assembly, assemblyFileName, warningMessages);
bool discoverInternals = ReflectHelper.GetDiscoverInternalsAttribute(assembly) != null;
TestIdGenerationStrategy testIdGenerationStrategy = ReflectHelper.GetTestIdGenerationStrategy(assembly);

Expand Down
2 changes: 1 addition & 1 deletion src/Adapter/MSTest.TestAdapter/Discovery/TypeValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ internal static bool HasCorrectTestContextSignature(Type type)
{
DebugEx.Assert(type != null, "HasCorrectTestContextSignature type is null");

IEnumerable<PropertyInfo> propertyInfoEnumerable = PlatformServiceProvider.Instance.ReflectionOperations.GetDeclaredProperties(type);
PropertyInfo[] propertyInfoEnumerable = PlatformServiceProvider.Instance.ReflectionOperations.GetDeclaredProperties(type);
var propertyInfo = new List<PropertyInfo>();

foreach (PropertyInfo pinfo in propertyInfoEnumerable)
Expand Down
4 changes: 2 additions & 2 deletions src/Adapter/MSTest.TestAdapter/Execution/TypeCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ private static bool TryGetUnescapedManagedTypeName(TestMethod testMethod, [NotNu
/// <returns> The <see cref="TestClassInfo"/>. </returns>
private TestClassInfo CreateClassInfo(Type classType, TestMethod testMethod)
{
IEnumerable<ConstructorInfo> constructors = PlatformServiceProvider.Instance.ReflectionOperations.GetDeclaredConstructors(classType);
ConstructorInfo[] constructors = PlatformServiceProvider.Instance.ReflectionOperations.GetDeclaredConstructors(classType);
(ConstructorInfo CtorInfo, bool IsParameterless)? selectedConstructor = null;

foreach (ConstructorInfo ctor in constructors)
Expand Down Expand Up @@ -399,7 +399,7 @@ private TestAssemblyInfo GetAssemblyInfo(Type type)

assemblyInfo = new TestAssemblyInfo(assembly);

IReadOnlyList<Type> types = AssemblyEnumerator.GetTypes(assembly, assembly.FullName!, null);
Type[] types = AssemblyEnumerator.GetTypes(assembly, assembly.FullName!, null);

foreach (Type t in types)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,16 @@ public object[] GetCustomAttributes(Assembly assembly, Type /* the attribute typ
return attributes.ToArray();
}

public IEnumerable<ConstructorInfo> GetDeclaredConstructors(Type classType)
public ConstructorInfo[] GetDeclaredConstructors(Type classType)
=> ReflectionDataProvider.TypeConstructors[classType];

public MethodInfo? GetDeclaredMethod(Type dynamicDataDeclaringType, string dynamicDataSourceName)
=> GetDeclaredMethods(dynamicDataDeclaringType).FirstOrDefault(m => m.Name == dynamicDataSourceName);

public IEnumerable<MethodInfo> GetDeclaredMethods(Type classType)
public MethodInfo[] GetDeclaredMethods(Type classType)
=> ReflectionDataProvider.TypeMethods[classType];

public IEnumerable<PropertyInfo> GetDeclaredProperties(Type type)
public PropertyInfo[] GetDeclaredProperties(Type type)
=> ReflectionDataProvider.TypeProperties[type];

public PropertyInfo? GetDeclaredProperty(Type type, string propertyName)
Expand All @@ -68,7 +68,7 @@ public IEnumerable<PropertyInfo> GetDeclaredProperties(Type type)
public Type[] GetDefinedTypes(Assembly assembly)
=> ReflectionDataProvider.Types;

public IEnumerable<MethodInfo> GetRuntimeMethods(Type type)
public MethodInfo[] GetRuntimeMethods(Type type)
=> ReflectionDataProvider.TypeMethods[type];

public MethodInfo? GetRuntimeMethod(Type declaringType, string methodName, Type[] parameters) => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Int

internal interface IReflectionOperations2 : IReflectionOperations
{
IEnumerable<ConstructorInfo> GetDeclaredConstructors(Type classType);
ConstructorInfo[] GetDeclaredConstructors(Type classType);

MethodInfo? GetDeclaredMethod(Type dynamicDataDeclaringType, string dynamicDataSourceName);

IEnumerable<MethodInfo> GetDeclaredMethods(Type classType);
MethodInfo[] GetDeclaredMethods(Type classType);

IEnumerable<PropertyInfo> GetDeclaredProperties(Type type);
PropertyInfo[] GetDeclaredProperties(Type type);

PropertyInfo? GetDeclaredProperty(Type type, string propertyName);

Type[] GetDefinedTypes(Assembly assembly);

IEnumerable<MethodInfo> GetRuntimeMethods(Type type);
MethodInfo[] GetRuntimeMethods(Type type);

MethodInfo? GetRuntimeMethod(Type declaringType, string methodName, Type[] parameters);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;

internal sealed class ReflectionOperations2 : ReflectionOperations, IReflectionOperations2
{
private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
private const BindingFlags Everything = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;

public ReflectionOperations2()
{
#if NET8_0_OR_GREATER
Expand All @@ -23,26 +26,26 @@ public ReflectionOperations2()
#pragma warning disable IL2026 // Members attributed with RequiresUnreferencedCode may break when trimming
#pragma warning disable IL2067 // 'target parameter' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to 'target method'.
#pragma warning disable IL2057 // Unrecognized value passed to the typeName parameter of 'System.Type.GetType(String)'
public IEnumerable<ConstructorInfo> GetDeclaredConstructors(Type classType)
=> classType.GetTypeInfo().DeclaredConstructors;
public ConstructorInfo[] GetDeclaredConstructors(Type classType)
=> classType.GetConstructors(DeclaredOnlyLookup);

public MethodInfo? GetDeclaredMethod(Type type, string methodName)
=> type.GetTypeInfo().GetDeclaredMethod(methodName);
=> type.GetMethod(methodName, DeclaredOnlyLookup);

public IEnumerable<MethodInfo> GetDeclaredMethods(Type classType)
=> classType.GetTypeInfo().DeclaredMethods;
public MethodInfo[] GetDeclaredMethods(Type classType)
=> classType.GetMethods(DeclaredOnlyLookup);

public IEnumerable<PropertyInfo> GetDeclaredProperties(Type type)
=> type.GetTypeInfo().DeclaredProperties;
public PropertyInfo[] GetDeclaredProperties(Type type)
=> type.GetProperties(DeclaredOnlyLookup);

public PropertyInfo? GetDeclaredProperty(Type type, string propertyName)
=> type.GetTypeInfo().GetDeclaredProperty(propertyName);
=> type.GetProperty(propertyName, DeclaredOnlyLookup);

public Type[] GetDefinedTypes(Assembly assembly)
=> assembly.DefinedTypes.ToArray();
=> assembly.GetTypes();

public IEnumerable<MethodInfo> GetRuntimeMethods(Type type)
=> type.GetRuntimeMethods();
public MethodInfo[] GetRuntimeMethods(Type type)
=> type.GetMethods(Everything);

public MethodInfo? GetRuntimeMethod(Type declaringType, string methodName, Type[] parameters)
=> declaringType.GetRuntimeMethod(methodName, parameters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public void GetTypesShouldReturnSetOfDefinedTypes()
TypeInfo[] expectedTypes = [typeof(DummyTestClass).GetTypeInfo(), typeof(DummyTestClass).GetTypeInfo()];

// Setup mocks
mockAssembly.Setup(a => a.DefinedTypes).Returns(expectedTypes);
mockAssembly.Setup(a => a.GetTypes()).Returns(expectedTypes);

IReadOnlyList<Type> types = AssemblyEnumerator.GetTypes(mockAssembly.Object, string.Empty, _warnings);
Verify(expectedTypes.SequenceEqual(types));
Expand All @@ -123,7 +123,7 @@ public void GetTypesShouldReturnReflectionTypeLoadExceptionTypesOnException()
var reflectedTypes = new Type[] { typeof(DummyTestClass) };

// Setup mocks
mockAssembly.Setup(a => a.DefinedTypes).Throws(new ReflectionTypeLoadException(reflectedTypes, null));
mockAssembly.Setup(a => a.GetTypes()).Throws(new ReflectionTypeLoadException(reflectedTypes, null));

IReadOnlyList<Type> types = AssemblyEnumerator.GetTypes(mockAssembly.Object, string.Empty, _warnings);

Expand All @@ -137,8 +137,8 @@ public void GetTypesShouldLogWarningsWhenReflectionFailsWithLoaderExceptions()
var exceptions = new Exception[] { new("DummyLoaderException") };

// Setup mocks
mockAssembly.Setup(a => a.DefinedTypes).Throws(new ReflectionTypeLoadException(null, exceptions));
mockAssembly.Setup(a => a.DefinedTypes).Throws(new ReflectionTypeLoadException(null, exceptions));
mockAssembly.Setup(a => a.GetTypes()).Throws(new ReflectionTypeLoadException(null, exceptions));
mockAssembly.Setup(a => a.GetTypes()).Throws(new ReflectionTypeLoadException(null, exceptions));

IReadOnlyList<Type> types = AssemblyEnumerator.GetTypes(mockAssembly.Object, "DummyAssembly", _warnings);

Expand Down Expand Up @@ -237,7 +237,7 @@ public void EnumerateAssemblyShouldReturnEmptyListWhenNoTestElementsInAType()
// Setup mocks
mockAssembly.Setup(a => a.GetTypes())
.Returns([typeof(DummyTestClass)]);
mockAssembly.Setup(a => a.DefinedTypes)
mockAssembly.Setup(a => a.GetTypes())
.Returns([typeof(DummyTestClass).GetTypeInfo()]);
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false))
.Returns(mockAssembly.Object);
Expand All @@ -256,7 +256,7 @@ public void EnumerateAssemblyShouldReturnTestElementsForAType()
// Setup mocks
mockAssembly.Setup(a => a.GetTypes())
.Returns([typeof(DummyTestClass)]);
mockAssembly.Setup(a => a.DefinedTypes)
mockAssembly.Setup(a => a.GetTypes())
.Returns([typeof(DummyTestClass).GetTypeInfo()]);
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false))
.Returns(mockAssembly.Object);
Expand All @@ -278,7 +278,7 @@ public void EnumerateAssemblyShouldReturnMoreThanOneTestElementForAType()
// Setup mocks
mockAssembly.Setup(a => a.GetTypes())
.Returns([typeof(DummyTestClass)]);
mockAssembly.Setup(a => a.DefinedTypes)
mockAssembly.Setup(a => a.GetTypes())
.Returns([typeof(DummyTestClass).GetTypeInfo()]);
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false))
.Returns(mockAssembly.Object);
Expand All @@ -300,7 +300,7 @@ public void EnumerateAssemblyShouldReturnMoreThanOneTestElementForMoreThanOneTyp
// Setup mocks
mockAssembly.Setup(a => a.GetTypes())
.Returns([typeof(DummyTestClass), typeof(DummyTestClass)]);
mockAssembly.Setup(a => a.DefinedTypes)
mockAssembly.Setup(a => a.GetTypes())
.Returns([typeof(DummyTestClass).GetTypeInfo(), typeof(DummyTestClass).GetTypeInfo()]);
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false))
.Returns(mockAssembly.Object);
Expand All @@ -323,7 +323,7 @@ public void EnumerateAssemblyShouldNotLogWarningsIfNonePresent()
// Setup mocks
mockAssembly.Setup(a => a.GetTypes())
.Returns([typeof(InternalTestClass)]);
mockAssembly.Setup(a => a.DefinedTypes)
mockAssembly.Setup(a => a.GetTypes())
.Returns([typeof(InternalTestClass).GetTypeInfo()]);
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false))
.Returns(mockAssembly.Object);
Expand All @@ -345,7 +345,7 @@ public void EnumerateAssemblyShouldLogWarningsIfPresent()
// Setup mocks
mockAssembly.Setup(a => a.GetTypes())
.Returns([typeof(InternalTestClass)]);
mockAssembly.Setup(a => a.DefinedTypes)
mockAssembly.Setup(a => a.GetTypes())
.Returns([typeof(InternalTestClass).GetTypeInfo()]);
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false))
.Returns(mockAssembly.Object);
Expand All @@ -365,7 +365,7 @@ public void EnumerateAssemblyShouldHandleExceptionsWhileEnumeratingAType()
// Setup mocks
mockAssembly.Setup(a => a.GetTypes())
.Returns([typeof(InternalTestClass)]);
mockAssembly.Setup(a => a.DefinedTypes)
mockAssembly.Setup(a => a.GetTypes())
.Returns([typeof(InternalTestClass).GetTypeInfo()]);
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false))
.Returns(mockAssembly.Object);
Expand Down

0 comments on commit e101a9d

Please sign in to comment.