From b20020df95989565c8dcc574d79914efaad189ef Mon Sep 17 00:00:00 2001 From: Alexander Linne Date: Fri, 25 Oct 2024 10:33:25 +0200 Subject: [PATCH] fix: deduplicate types by assembly qualified name Signed-off-by: Alexander Linne --- ArchUnitNET/Domain/GenericParameter.cs | 46 +- ArchUnitNET/Domain/MethodMember.cs | 4 +- ArchUnitNET/Loader/ArchBuilder.cs | 4 - .../AddGenericParameterDependencies.cs | 2 - ArchUnitNET/Loader/LoadTasks/AddMembers.cs | 15 +- ArchUnitNET/Loader/MethodMemberRegistry.cs | 39 -- .../Loader/MonoCecilAttributeExtensions.cs | 11 +- ArchUnitNET/Loader/Type.cs | 4 +- ArchUnitNET/Loader/TypeFactory.cs | 403 +++++++++++------- ArchUnitNET/Loader/TypeRegistry.cs | 42 -- ArchUnitNETTests/ArchUnitNETTests.csproj | 4 +- ArchUnitNETTests/Domain/GenericClassTests.cs | 5 +- .../Fluent/Extensions/BuildMocksExtensions.cs | 3 +- ArchUnitNETTests/Loader/ArchLoaderTests.cs | 38 +- .../DuplicateClassAcrossAssemblies.cs | 2 +- .../DuplicateClassAcrossAssemblies.cs | 2 +- 16 files changed, 329 insertions(+), 295 deletions(-) delete mode 100644 ArchUnitNET/Loader/MethodMemberRegistry.cs delete mode 100644 ArchUnitNET/Loader/TypeRegistry.cs diff --git a/ArchUnitNET/Domain/GenericParameter.cs b/ArchUnitNET/Domain/GenericParameter.cs index e4a1f39d..9815f799 100644 --- a/ArchUnitNET/Domain/GenericParameter.cs +++ b/ArchUnitNET/Domain/GenericParameter.cs @@ -14,22 +14,24 @@ namespace ArchUnitNET.Domain { public class GenericParameter : IType { - private readonly string _declarerFullName; internal readonly IEnumerable> TypeInstanceConstraints; public GenericParameter( - string declarerFullName, + ITypeInstance declaringTypeInstance, + [CanBeNull] MethodMemberInstance declaringMethodInstance, + string fullName, string name, GenericParameterVariance variance, IEnumerable> typeConstraints, bool hasReferenceTypeConstraint, bool hasNotNullableValueTypeConstraint, bool hasDefaultConstructorConstraint, - bool isCompilerGenerated, - bool declarerIsMethod + bool isCompilerGenerated ) { - _declarerFullName = declarerFullName; + DeclaringTypeInstance = declaringTypeInstance; + DeclaringMethodInstance = declaringMethodInstance; + FullName = fullName; Name = name; Variance = variance; TypeInstanceConstraints = typeConstraints; @@ -37,14 +39,17 @@ bool declarerIsMethod HasNotNullableValueTypeConstraint = hasNotNullableValueTypeConstraint; HasDefaultConstructorConstraint = hasDefaultConstructorConstraint; IsCompilerGenerated = isCompilerGenerated; - DeclarerIsMethod = declarerIsMethod; } - public IType DeclaringType { get; private set; } + public ITypeInstance DeclaringTypeInstance { get; } + public IType DeclaringType => DeclaringTypeInstance.Type; [CanBeNull] - public IMember DeclaringMethod { get; private set; } - public bool DeclarerIsMethod { get; } + public MethodMemberInstance DeclaringMethodInstance { get; } + + [CanBeNull] + public IMember DeclaringMethod => DeclaringMethodInstance?.Member; + public bool DeclarerIsMethod => DeclaringMethodInstance != null; public GenericParameterVariance Variance { get; } public IEnumerable TypeConstraints => TypeInstanceConstraints.Select(instance => instance.Type); @@ -59,7 +64,7 @@ bool declarerIsMethod || TypeConstraints.Any(); public string Name { get; } - public string FullName => _declarerFullName + "+<" + Name + ">"; + public string FullName { get; } public string AssemblyQualifiedName => System.Reflection.Assembly.CreateQualifiedName( DeclaringType.Assembly.FullName, @@ -84,27 +89,6 @@ bool declarerIsMethod public bool IsNested => true; public bool IsStub => true; - internal void AssignDeclarer(IMember declaringMethod) - { - if (!declaringMethod.FullName.Equals(_declarerFullName)) - { - throw new InvalidOperationException("Full name of declaring member doesn't match."); - } - - DeclaringType = declaringMethod.DeclaringType; - DeclaringMethod = declaringMethod; - } - - internal void AssignDeclarer(IType declaringType) - { - if (!declaringType.FullName.Equals(_declarerFullName)) - { - throw new InvalidOperationException("Full name of declaring type doesn't match."); - } - - DeclaringType = declaringType; - } - public bool Equals(GenericParameter other) { if (ReferenceEquals(null, other)) diff --git a/ArchUnitNET/Domain/MethodMember.cs b/ArchUnitNET/Domain/MethodMember.cs index 0dd7517c..0b4bc820 100644 --- a/ArchUnitNET/Domain/MethodMember.cs +++ b/ArchUnitNET/Domain/MethodMember.cs @@ -17,7 +17,6 @@ public MethodMember( string fullName, IType declaringType, Visibility visibility, - ITypeInstance returnTypeInstance, bool isVirtual, MethodForm methodForm, bool isGeneric, @@ -35,7 +34,6 @@ public MethodMember( ); DeclaringType = declaringType; Visibility = visibility; - ReturnTypeInstance = returnTypeInstance; IsVirtual = isVirtual; MethodForm = methodForm; IsGeneric = isGeneric; @@ -52,7 +50,7 @@ public MethodMember( new List>(); public IEnumerable Parameters => ParameterInstances.Select(instance => instance.Type); - public ITypeInstance ReturnTypeInstance { get; } + public ITypeInstance ReturnTypeInstance { get; internal set; } public IType ReturnType => ReturnTypeInstance.Type; public bool IsStub { get; } public bool IsCompilerGenerated { get; } diff --git a/ArchUnitNET/Loader/ArchBuilder.cs b/ArchUnitNET/Loader/ArchBuilder.cs index eecf9549..a8acbd17 100644 --- a/ArchUnitNET/Loader/ArchBuilder.cs +++ b/ArchUnitNET/Loader/ArchBuilder.cs @@ -31,11 +31,7 @@ public ArchBuilder() _assemblyRegistry = new AssemblyRegistry(); _namespaceRegistry = new NamespaceRegistry(); _loadTaskRegistry = new LoadTaskRegistry(); - var typeRegistry = new TypeRegistry(); - var methodMemberRegistry = new MethodMemberRegistry(); _typeFactory = new TypeFactory( - typeRegistry, - methodMemberRegistry, _loadTaskRegistry, _assemblyRegistry, _namespaceRegistry diff --git a/ArchUnitNET/Loader/LoadTasks/AddGenericParameterDependencies.cs b/ArchUnitNET/Loader/LoadTasks/AddGenericParameterDependencies.cs index 1b1eade5..18e00970 100644 --- a/ArchUnitNET/Loader/LoadTasks/AddGenericParameterDependencies.cs +++ b/ArchUnitNET/Loader/LoadTasks/AddGenericParameterDependencies.cs @@ -31,7 +31,6 @@ private void AddTypeGenericParameterDependencies() { foreach (var genericParameter in _type.GenericParameters) { - genericParameter.AssignDeclarer(_type); foreach (var typeInstanceConstraint in genericParameter.TypeInstanceConstraints) { var dependency = new TypeGenericParameterTypeConstraintDependency( @@ -49,7 +48,6 @@ private void AddMemberGenericParameterDependencies() { foreach (var genericParameter in member.GenericParameters) { - genericParameter.AssignDeclarer(member); foreach (var typeInstanceConstraint in genericParameter.TypeInstanceConstraints) { var dependency = new MemberGenericParameterTypeConstraintDependency( diff --git a/ArchUnitNET/Loader/LoadTasks/AddMembers.cs b/ArchUnitNET/Loader/LoadTasks/AddMembers.cs index 8c6183cb..193d059f 100644 --- a/ArchUnitNET/Loader/LoadTasks/AddMembers.cs +++ b/ArchUnitNET/Loader/LoadTasks/AddMembers.cs @@ -18,18 +18,18 @@ namespace ArchUnitNET.Loader.LoadTasks internal class AddMembers : ILoadTask { private readonly MemberList _memberList; - private readonly IType _type; + private readonly ITypeInstance _typeInstance; private readonly TypeDefinition _typeDefinition; private readonly TypeFactory _typeFactory; public AddMembers( - IType type, + ITypeInstance typeInstance, TypeDefinition typeDefinition, TypeFactory typeFactory, MemberList memberList ) { - _type = type; + _typeInstance = typeInstance; _typeDefinition = typeDefinition; _typeFactory = typeFactory; _memberList = memberList; @@ -53,7 +53,10 @@ private IEnumerable CreateMembers([NotNull] TypeDefinition typeDefiniti .Concat( typeDefinition.Methods.Select(method => _typeFactory - .GetOrCreateMethodMemberFromMethodReference(_type, method) + .GetOrCreateMethodMemberFromMethodReference( + _typeInstance, + method + ) .Member ) ) @@ -72,7 +75,7 @@ private IMember CreateFieldMember([NotNull] FieldDefinition fieldDefinition) var isCompilerGenerated = fieldDefinition.IsCompilerGenerated(); var writeAccessor = GetWriteAccessor(fieldDefinition); return new FieldMember( - _type, + _typeInstance.Type, fieldDefinition.Name, fieldDefinition.FullName, visibility, @@ -96,7 +99,7 @@ private IMember CreatePropertyMember(PropertyDefinition propertyDefinition) || (propertyDefinition.GetMethod != null && propertyDefinition.GetMethod.IsStatic); var writeAccessor = GetWriteAccessor(propertyDefinition); return new PropertyMember( - _type, + _typeInstance.Type, propertyDefinition.Name, propertyDefinition.FullName, propertyType, diff --git a/ArchUnitNET/Loader/MethodMemberRegistry.cs b/ArchUnitNET/Loader/MethodMemberRegistry.cs deleted file mode 100644 index 41870fe3..00000000 --- a/ArchUnitNET/Loader/MethodMemberRegistry.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2019 Florian Gather -// Copyright 2019 Fritz Brandhuber -// Copyright 2020 Pavel Fischer -// -// SPDX-License-Identifier: Apache-2.0 -// - -using System; -using System.Collections.Generic; -using System.Linq; -using ArchUnitNET.Domain; -using JetBrains.Annotations; -using Mono.Cecil; - -namespace ArchUnitNET.Loader -{ - internal class MethodMemberRegistry - { - private readonly Dictionary _allMethods = - new Dictionary(); - - public MethodMemberInstance GetOrCreateMethodFromMethodReference( - [NotNull] MethodReference methodReference, - [NotNull] Func createFunc - ) - { - return RegistryUtils.GetFromDictOrCreateAndAdd( - methodReference.BuildFullName(), - _allMethods, - createFunc - ); - } - - public IEnumerable GetAllMethodMembers() - { - return _allMethods.Values.Select(instance => instance.Member).Distinct(); - } - } -} diff --git a/ArchUnitNET/Loader/MonoCecilAttributeExtensions.cs b/ArchUnitNET/Loader/MonoCecilAttributeExtensions.cs index b408162f..6278ea52 100644 --- a/ArchUnitNET/Loader/MonoCecilAttributeExtensions.cs +++ b/ArchUnitNET/Loader/MonoCecilAttributeExtensions.cs @@ -5,11 +5,13 @@ // SPDX-License-Identifier: Apache-2.0 // +using System; using System.Collections.Generic; using System.Linq; using ArchUnitNET.Domain; using JetBrains.Annotations; using Mono.Cecil; +using Attribute = ArchUnitNET.Domain.Attribute; namespace ArchUnitNET.Loader { @@ -25,9 +27,12 @@ TypeFactory typeFactory var attributeType = typeFactory.GetOrCreateStubTypeInstanceFromTypeReference( attributeTypeReference ); - var attribute = attributeType.Type is Class cls - ? new Attribute(cls) - : new Attribute(attributeType.Type, null, null); + if (!(attributeType.Type is Attribute attribute)) + { + throw new ArgumentException( + $"Attribute type {attributeType.Type.FullName} is not an attribute." + ); + } var attributeArguments = new List(); diff --git a/ArchUnitNET/Loader/Type.cs b/ArchUnitNET/Loader/Type.cs index b06f3826..d14efca1 100644 --- a/ArchUnitNET/Loader/Type.cs +++ b/ArchUnitNET/Loader/Type.cs @@ -84,7 +84,7 @@ public override string ToString() private bool Equals(Type other) { - return string.Equals(FullName, other.FullName); + return string.Equals(AssemblyQualifiedName, other.AssemblyQualifiedName); } public override bool Equals(object obj) @@ -104,7 +104,7 @@ public override bool Equals(object obj) public override int GetHashCode() { - return FullName != null ? FullName.GetHashCode() : 0; + return AssemblyQualifiedName != null ? AssemblyQualifiedName.GetHashCode() : 0; } } } diff --git a/ArchUnitNET/Loader/TypeFactory.cs b/ArchUnitNET/Loader/TypeFactory.cs index 576cce61..8e3db7d3 100644 --- a/ArchUnitNET/Loader/TypeFactory.cs +++ b/ArchUnitNET/Loader/TypeFactory.cs @@ -23,13 +23,15 @@ internal class TypeFactory { private readonly AssemblyRegistry _assemblyRegistry; private readonly LoadTaskRegistry _loadTaskRegistry; - private readonly MethodMemberRegistry _methodMemberRegistry; private readonly NamespaceRegistry _namespaceRegistry; - private readonly TypeRegistry _typeRegistry; + + private readonly Dictionary> _allTypes = + new Dictionary>(); + + private readonly Dictionary _allMethods = + new Dictionary(); public TypeFactory( - TypeRegistry typeRegistry, - MethodMemberRegistry methodMemberRegistry, LoadTaskRegistry loadTaskRegistry, AssemblyRegistry assemblyRegistry, NamespaceRegistry namespaceRegistry @@ -38,24 +40,20 @@ NamespaceRegistry namespaceRegistry _loadTaskRegistry = loadTaskRegistry; _assemblyRegistry = assemblyRegistry; _namespaceRegistry = namespaceRegistry; - _typeRegistry = typeRegistry; - _methodMemberRegistry = methodMemberRegistry; } public IEnumerable GetAllNonCompilerGeneratedTypes() { - return _typeRegistry.GetAllTypes().Where(type => !type.IsCompilerGenerated); + return _allTypes + .Values.Select(instance => instance.Type) + .Distinct() + .Where(type => !type.IsCompilerGenerated); } [NotNull] internal IType GetOrCreateTypeFromTypeReference(TypeReference typeReference) { - return _typeRegistry - .GetOrCreateTypeFromTypeReference( - typeReference, - s => CreateTypeFromTypeReference(typeReference, false) - ) - .Type; + return GetOrCreateTypeInstanceFromTypeReference(typeReference, false).Type; } [NotNull] @@ -63,96 +61,26 @@ internal ITypeInstance GetOrCreateStubTypeInstanceFromTypeReference( TypeReference typeReference ) { - return _typeRegistry.GetOrCreateTypeFromTypeReference( - typeReference, - s => CreateTypeFromTypeReference(typeReference, true) - ); - } - - [NotNull] - internal MethodMemberInstance GetOrCreateMethodMemberFromMethodReference( - [NotNull] IType type, - [NotNull] MethodReference methodReference - ) - { - return _methodMemberRegistry.GetOrCreateMethodFromMethodReference( - methodReference, - s => - CreateMethodMemberFromMethodReference( - new TypeInstance(type), - methodReference - ) - ); - } - - [NotNull] - internal MethodMemberInstance GetOrCreateMethodMemberFromMethodReference( - [NotNull] ITypeInstance typeInstance, - [NotNull] MethodReference methodReference - ) - { - return _methodMemberRegistry.GetOrCreateMethodFromMethodReference( - methodReference, - s => CreateMethodMemberFromMethodReference(typeInstance, methodReference) - ); + return GetOrCreateTypeInstanceFromTypeReference(typeReference, true); } - [NotNull] - private ITypeInstance CreateTypeFromTypeReference( + private ITypeInstance GetOrCreateTypeInstanceFromTypeReference( TypeReference typeReference, bool isStub ) { if (typeReference.IsGenericParameter) { - var genericParameter = (Mono.Cecil.GenericParameter)typeReference; - var declarerIsMethod = genericParameter.Type == GenericParameterType.Method; - var declaringTypeFullName = declarerIsMethod - ? genericParameter.DeclaringMethod.BuildFullName() - : genericParameter.DeclaringType.BuildFullName(); - - return new TypeInstance( - CreateGenericParameter( - genericParameter, - declaringTypeFullName, - declarerIsMethod - ) - ); + return GetOrCreateGenericParameterTypeInstanceFromTypeReference(typeReference); } - if (typeReference.IsArray) { - var dimensions = new List(); - do - { - var arrayType = (ArrayType)typeReference; - dimensions.Add(arrayType.Rank); - typeReference = arrayType.ElementType; - } while (typeReference.IsArray); - - var elementTypeInstance = GetOrCreateStubTypeInstanceFromTypeReference( - typeReference - ); - return CreateTypeInstance( - elementTypeInstance.Type, - elementTypeInstance.GenericArguments, - dimensions - ); + return GetOrCreateArrayTypeInstanceFromTypeReference(typeReference); } - if (typeReference.IsGenericInstance) { - var elementType = GetOrCreateStubTypeInstanceFromTypeReference( - typeReference.GetElementType() - ).Type; - var genericInstance = (GenericInstanceType)typeReference; - var genericArguments = genericInstance - .GenericArguments.Select(CreateGenericArgumentFromTypeReference) - .Where(argument => !argument.Type.IsCompilerGenerated) - .ToList(); - return CreateTypeInstance(elementType, genericArguments, new List()); + return GetOrCreateGenericInstanceTypeInstanceFromTypeReference(typeReference); } - if ( typeReference.IsByReference || typeReference.IsPointer @@ -160,14 +88,30 @@ bool isStub || typeReference.IsRequiredModifier ) { - return CreateTypeFromTypeReference(typeReference.GetElementType(), isStub); + return GetOrCreateTypeInstanceFromTypeReference( + typeReference.GetElementType(), + isStub + ); } - if (typeReference is FunctionPointerType functionPointerType) { - return GetOrCreateTypeInstance(functionPointerType); + return GetOrCreateFunctionPointerTypeInstanceFromTypeReference(functionPointerType); } + if (typeReference is TypeDefinition typeDefinition) + { + return GetOrCreateTypeInstanceFromTypeDefinition(typeDefinition, isStub); + } + return GetOrCreateTypeInstanceFromTypeDefinition( + ResolveTypeReferenceToTypeDefinition(typeReference), + isStub + ); + } + [NotNull] + private static TypeDefinition ResolveTypeReferenceToTypeDefinition( + TypeReference typeReference + ) + { TypeDefinition typeDefinition; try { @@ -184,31 +128,119 @@ bool isStub { throw new ArchLoaderException($"Could not resolve type {typeReference.FullName}"); } + return typeDefinition; + } - var typeName = typeDefinition.BuildFullName(); - var declaringTypeReference = typeDefinition; - while (declaringTypeReference.IsNested) + private ITypeInstance GetOrCreateGenericParameterTypeInstanceFromTypeReference( + TypeReference typeReference + ) + { + var genericParameter = (Mono.Cecil.GenericParameter)typeReference; + var declarerIsMethod = genericParameter.Type == GenericParameterType.Method; + var declaringType = GetOrCreateStubTypeInstanceFromTypeReference( + declarerIsMethod + ? genericParameter.DeclaringMethod.DeclaringType + : genericParameter.DeclaringType + ); + var declaringMethod = declarerIsMethod + ? GetOrCreateMethodMemberFromMethodReference( + declaringType, + genericParameter.DeclaringMethod + ) + : null; + var declarerFullName = + declaringMethod != null + ? declaringMethod.Member.FullName + : declaringType.Type.FullName; + var assemblyQualifiedName = System.Reflection.Assembly.CreateQualifiedName( + declaringType.Type.Assembly.FullName, + $"{declarerFullName}+<{genericParameter.Name}>" + ); + if (_allTypes.TryGetValue(assemblyQualifiedName, out var existingTypeInstance)) { - declaringTypeReference = declaringTypeReference.DeclaringType; + return existingTypeInstance; } + var result = new TypeInstance( + CreateGenericParameter(genericParameter, declaringType, declaringMethod) + ); + _allTypes.Add(assemblyQualifiedName, result); + return result; + } - var currentNamespace = _namespaceRegistry.GetOrCreateNamespace( - declaringTypeReference.Namespace + private ITypeInstance GetOrCreateGenericInstanceTypeInstanceFromTypeReference( + TypeReference typeReference + ) + { + var elementType = GetOrCreateStubTypeInstanceFromTypeReference( + typeReference.GetElementType() + ).Type; + var assemblyQualifiedName = System.Reflection.Assembly.CreateQualifiedName( + elementType.Assembly.FullName, + typeReference.BuildFullName() ); - var currentAssembly = _assemblyRegistry.GetOrCreateAssembly( - typeDefinition.Module.Assembly.Name.FullName, - typeDefinition.Module.Assembly.FullName, - true, - null + if (_allTypes.TryGetValue(assemblyQualifiedName, out var existingTypeInstance)) + { + return existingTypeInstance; + } + var genericInstance = (GenericInstanceType)typeReference; + var genericArguments = genericInstance + .GenericArguments.Select(CreateGenericArgumentFromTypeReference) + .Where(argument => !argument.Type.IsCompilerGenerated) + .ToList(); + var result = CreateTypeInstance(elementType, genericArguments, new List()); + _allTypes.Add(assemblyQualifiedName, result); + return result; + } + + private ITypeInstance GetOrCreateArrayTypeInstanceFromTypeReference( + TypeReference typeReference + ) + { + var dimensions = new List(); + var elementType = typeReference; + do + { + var arrayType = (ArrayType)elementType; + dimensions.Add(arrayType.Rank); + elementType = arrayType.ElementType; + } while (elementType.IsArray); + var elementTypeInstance = GetOrCreateStubTypeInstanceFromTypeReference(elementType); + var assemblyQualifiedName = System.Reflection.Assembly.CreateQualifiedName( + elementTypeInstance.Type.Assembly.FullName, + typeReference.BuildFullName() + ); + if (_allTypes.TryGetValue(assemblyQualifiedName, out var existingTypeInstance)) + { + return existingTypeInstance; + } + var result = CreateTypeInstance( + elementTypeInstance.Type, + elementTypeInstance.GenericArguments, + dimensions ); + _allTypes.Add(assemblyQualifiedName, result); + return result; + } - Type type; - bool isCompilerGenerated, - isNested, - isGeneric; + [NotNull] + private ITypeInstance GetOrCreateTypeInstanceFromTypeDefinition( + TypeDefinition typeDefinition, + bool isStub + ) + { + var assemblyFullName = typeDefinition.Module.Assembly.FullName; + var fullName = typeDefinition.BuildFullName(); - const string fixedElementField = "FixedElementField"; + var assemblyQualifiedName = System.Reflection.Assembly.CreateQualifiedName( + assemblyFullName, + fullName + ); + if (_allTypes.TryGetValue(assemblyQualifiedName, out var existingTypeInstance)) + { + return existingTypeInstance; + } + const string fixedElementField = "FixedElementField"; if ( typeDefinition.CustomAttributes.Any(att => att.AttributeType.FullName == typeof(UnsafeValueTypeAttribute).FullName @@ -227,12 +259,26 @@ bool isStub ); } + var declaringTypeReference = typeDefinition; + while (declaringTypeReference.IsNested) + { + declaringTypeReference = declaringTypeReference.DeclaringType; + } + var currentNamespace = _namespaceRegistry.GetOrCreateNamespace( + declaringTypeReference.Namespace + ); + var currentAssembly = _assemblyRegistry.GetOrCreateAssembly( + assemblyFullName, + assemblyFullName, + true, + null + ); var visibility = typeDefinition.GetVisibility(); - isCompilerGenerated = typeDefinition.IsCompilerGenerated(); - isNested = typeDefinition.IsNested; - isGeneric = typeDefinition.HasGenericParameters; - type = new Type( - typeName, + var isCompilerGenerated = typeDefinition.IsCompilerGenerated(); + var isNested = typeDefinition.IsNested; + var isGeneric = typeDefinition.HasGenericParameters; + var type = new Type( + fullName, typeDefinition.Name, currentAssembly, currentNamespace, @@ -243,9 +289,6 @@ bool isStub isCompilerGenerated ); - var genericParameters = GetGenericParameters(typeDefinition); - type.GenericParameters.AddRange(genericParameters); - ITypeInstance createdTypeInstance; if (typeDefinition.IsInterface) @@ -288,9 +331,14 @@ bool isStub LoadBaseTask(createdTypeInstance.Type, type, typeDefinition); } - LoadNonBaseTasks(createdTypeInstance.Type, type, typeDefinition); + LoadNonBaseTasks(createdTypeInstance, type, typeDefinition); } + _allTypes.Add(assemblyQualifiedName, createdTypeInstance); + + var genericParameters = GetGenericParameters(typeDefinition); + type.GenericParameters.AddRange(genericParameters); + return createdTypeInstance; } @@ -325,10 +373,14 @@ IEnumerable arrayDimensions } [NotNull] - private ITypeInstance GetOrCreateTypeInstance( + private ITypeInstance GetOrCreateFunctionPointerTypeInstanceFromTypeReference( FunctionPointerType functionPointerType ) { + if (_allTypes.TryGetValue(functionPointerType.FullName, out var existingTypeInstance)) + { + return existingTypeInstance; + } var type = new Type( functionPointerType.FullName, functionPointerType.Name, @@ -348,38 +400,37 @@ FunctionPointerType functionPointerType GetOrCreateStubTypeInstanceFromTypeReference(parameter.ParameterType) ) .ToList(); - return new TypeInstance( + var result = new TypeInstance( new FunctionPointer(type, returnTypeInstance, parameterTypeInstances) ); + _allTypes.Add(functionPointerType.FullName, result); + return result; } [NotNull] - private MethodMemberInstance CreateMethodMemberFromMethodReference( + public MethodMemberInstance GetOrCreateMethodMemberFromMethodReference( [NotNull] ITypeInstance typeInstance, [NotNull] MethodReference methodReference ) { + var methodReferenceFullName = methodReference.BuildFullName(); if (methodReference.IsGenericInstance) { - var elementMethod = CreateMethodMemberFromMethodReference( - typeInstance, - methodReference.GetElementMethod() - ).Member; - - var genericInstanceMethod = (GenericInstanceMethod)methodReference; - var genericArguments = genericInstanceMethod - .GenericArguments.Select(CreateGenericArgumentFromTypeReference) - .Where(argument => !argument.Type.IsCompilerGenerated); - - return new MethodMemberInstance( - elementMethod, - typeInstance.GenericArguments, - genericArguments + return RegistryUtils.GetFromDictOrCreateAndAdd( + methodReferenceFullName, + _allMethods, + _ => + CreateGenericInstanceMethodMemberFromMethodReference( + typeInstance, + methodReference + ) ); } - var returnTypeReference = methodReference.ReturnType; - var returnType = GetOrCreateStubTypeInstanceFromTypeReference(returnTypeReference); + if (_allMethods.TryGetValue(methodReferenceFullName, out var existingMethodInstance)) + { + return existingMethodInstance; + } var name = methodReference.BuildMethodMemberName(); var fullName = methodReference.BuildFullName(); @@ -425,7 +476,6 @@ [NotNull] MethodReference methodReference fullName, typeInstance.Type, visibility, - returnType, false, methodForm, isGeneric, @@ -435,16 +485,47 @@ [NotNull] MethodReference methodReference isStatic ); - var parameters = methodReference.GetParameters(this).ToList(); - methodMember.ParameterInstances.AddRange(parameters); + var result = new MethodMemberInstance( + methodMember, + typeInstance.GenericArguments, + Enumerable.Empty() + ); + + _allMethods.Add(methodReferenceFullName, result); var genericParameters = GetGenericParameters(methodReference); methodMember.GenericParameters.AddRange(genericParameters); + var returnTypeReference = methodReference.ReturnType; + methodMember.ReturnTypeInstance = GetOrCreateStubTypeInstanceFromTypeReference( + returnTypeReference + ); + + var parameters = methodReference.GetParameters(this).ToList(); + methodMember.ParameterInstances.AddRange(parameters); + + return result; + } + + private MethodMemberInstance CreateGenericInstanceMethodMemberFromMethodReference( + ITypeInstance typeInstance, + MethodReference methodReference + ) + { + var elementMethod = GetOrCreateMethodMemberFromMethodReference( + typeInstance, + methodReference.GetElementMethod() + ).Member; + + var genericInstanceMethod = (GenericInstanceMethod)methodReference; + var genericArguments = genericInstanceMethod + .GenericArguments.Select(CreateGenericArgumentFromTypeReference) + .Where(argument => !argument.Type.IsCompilerGenerated); + return new MethodMemberInstance( - methodMember, + elementMethod, typeInstance.GenericArguments, - Enumerable.Empty() + genericArguments ); } @@ -493,8 +574,8 @@ IGenericParameterProvider genericParameterProvider private GenericParameter CreateGenericParameter( Mono.Cecil.GenericParameter genericParameter, - [NotNull] string declarerFullName, - bool declarerIsMethod + ITypeInstance declaringTypeInstance, + [CanBeNull] MethodMemberInstance declaringMethodInstance ) { var isCompilerGenerated = genericParameter.IsCompilerGenerated(); @@ -502,16 +583,21 @@ bool declarerIsMethod var typeConstraints = genericParameter.Constraints.Select(con => GetOrCreateStubTypeInstanceFromTypeReference(con.ConstraintType) ); + var declarerFullName = + declaringMethodInstance != null + ? declaringMethodInstance.Member.FullName + : declaringTypeInstance.Type.FullName; return new GenericParameter( - declarerFullName, + declaringTypeInstance, + declaringMethodInstance, + $"{declarerFullName}+<{genericParameter.Name}>", genericParameter.Name, variance, typeConstraints, genericParameter.HasReferenceTypeConstraint, genericParameter.HasNotNullableValueTypeConstraint, genericParameter.HasDefaultConstructorConstraint, - isCompilerGenerated, - declarerIsMethod + isCompilerGenerated ); } @@ -533,7 +619,11 @@ private void LoadBaseTask(IType cls, Type type, TypeDefinition typeDefinition) ); } - private void LoadNonBaseTasks(IType createdType, Type type, TypeDefinition typeDefinition) + private void LoadNonBaseTasks( + ITypeInstance createdTypeInstance, + Type type, + TypeDefinition typeDefinition + ) { if (typeDefinition == null) { @@ -542,7 +632,7 @@ private void LoadNonBaseTasks(IType createdType, Type type, TypeDefinition typeD _loadTaskRegistry.Add( typeof(AddMembers), - new AddMembers(createdType, typeDefinition, this, type.Members) + new AddMembers(createdTypeInstance, typeDefinition, this, type.Members) ); _loadTaskRegistry.Add( typeof(AddGenericParameterDependencies), @@ -550,15 +640,19 @@ private void LoadNonBaseTasks(IType createdType, Type type, TypeDefinition typeD ); _loadTaskRegistry.Add( typeof(AddAttributesAndAttributeDependencies), - new AddAttributesAndAttributeDependencies(createdType, typeDefinition, this) + new AddAttributesAndAttributeDependencies( + createdTypeInstance.Type, + typeDefinition, + this + ) ); _loadTaskRegistry.Add( typeof(AddFieldAndPropertyDependencies), - new AddFieldAndPropertyDependencies(createdType) + new AddFieldAndPropertyDependencies(createdTypeInstance.Type) ); _loadTaskRegistry.Add( typeof(AddMethodDependencies), - new AddMethodDependencies(createdType, typeDefinition, this) + new AddMethodDependencies(createdTypeInstance.Type, typeDefinition, this) ); _loadTaskRegistry.Add( typeof(AddGenericArgumentDependencies), @@ -566,11 +660,16 @@ private void LoadNonBaseTasks(IType createdType, Type type, TypeDefinition typeD ); _loadTaskRegistry.Add( typeof(AddClassDependencies), - new AddClassDependencies(createdType, typeDefinition, this, type.Dependencies) + new AddClassDependencies( + createdTypeInstance.Type, + typeDefinition, + this, + type.Dependencies + ) ); _loadTaskRegistry.Add( typeof(AddBackwardsDependencies), - new AddBackwardsDependencies(createdType) + new AddBackwardsDependencies(createdTypeInstance.Type) ); } } diff --git a/ArchUnitNET/Loader/TypeRegistry.cs b/ArchUnitNET/Loader/TypeRegistry.cs deleted file mode 100644 index c8de1e59..00000000 --- a/ArchUnitNET/Loader/TypeRegistry.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019 Florian Gather -// Copyright 2019 Paula Ruiz -// Copyright 2019 Fritz Brandhuber -// -// SPDX-License-Identifier: Apache-2.0 - -using System; -using System.Collections.Generic; -using System.Linq; -using ArchUnitNET.Domain; -using JetBrains.Annotations; -using Mono.Cecil; - -namespace ArchUnitNET.Loader -{ - internal class TypeRegistry - { - private readonly Dictionary> _allTypes = - new Dictionary>(); - - public ITypeInstance GetOrCreateTypeFromTypeReference( - [NotNull] TypeReference typeReference, - [NotNull] Func> createFunc - ) - { - var assemblyQualifiedName = System.Reflection.Assembly.CreateQualifiedName( - typeReference.Module.Assembly.FullName, - typeReference.BuildFullName() - ); - return RegistryUtils.GetFromDictOrCreateAndAdd( - assemblyQualifiedName, - _allTypes, - createFunc - ); - } - - public IEnumerable GetAllTypes() - { - return _allTypes.Values.Select(instance => instance.Type).Distinct(); - } - } -} diff --git a/ArchUnitNETTests/ArchUnitNETTests.csproj b/ArchUnitNETTests/ArchUnitNETTests.csproj index 4f4c6969..30d5ad81 100644 --- a/ArchUnitNETTests/ArchUnitNETTests.csproj +++ b/ArchUnitNETTests/ArchUnitNETTests.csproj @@ -13,9 +13,9 @@ - + + Include="..\TestAssemblies\OtherLoaderTestAssembly\OtherLoaderTestAssembly.csproj" Aliases="global,OtherLoaderTestAssemblyAlias" /> diff --git a/ArchUnitNETTests/Domain/GenericClassTests.cs b/ArchUnitNETTests/Domain/GenericClassTests.cs index d4837a66..c85b82c4 100644 --- a/ArchUnitNETTests/Domain/GenericClassTests.cs +++ b/ArchUnitNETTests/Domain/GenericClassTests.cs @@ -35,6 +35,9 @@ public GenericClassTests() _classWithGenericParameters = Architecture.GetClassOfType( typeof(ClassWithGenericParameters<>) ); + var systemType = Architecture.GetClassOfType( + typeof(string) + ); var invokesGenericClass = Architecture.GetClassOfType(typeof(InvokesGenericClass)); _genericallyTypedField = invokesGenericClass .GetFieldMembersWithName(nameof(InvokesGenericClass.GuidGenericArgument)) @@ -42,7 +45,7 @@ public GenericClassTests() var guidMock = new Type( SystemGuidFullName, GuidClassName, - _classWithGenericParameters.Assembly, + systemType.Assembly, new Namespace(StaticConstants.SystemNamespace, new List()), Public, false, diff --git a/ArchUnitNETTests/Fluent/Extensions/BuildMocksExtensions.cs b/ArchUnitNETTests/Fluent/Extensions/BuildMocksExtensions.cs index b7a7b3b1..52e82432 100644 --- a/ArchUnitNETTests/Fluent/Extensions/BuildMocksExtensions.cs +++ b/ArchUnitNETTests/Fluent/Extensions/BuildMocksExtensions.cs @@ -178,7 +178,6 @@ public static MethodMember CreateStubMethodMember(this MethodBase methodBase) fullName, declaringType, visibility, - returnTypeInstance, methodBase.IsVirtual, methodForm, isGeneric, @@ -187,7 +186,7 @@ public static MethodMember CreateStubMethodMember(this MethodBase methodBase) false, isStatic ); - + methodMember.ReturnTypeInstance = returnTypeInstance; methodMember.ParameterInstances.AddRange(parameters); return methodMember; } diff --git a/ArchUnitNETTests/Loader/ArchLoaderTests.cs b/ArchUnitNETTests/Loader/ArchLoaderTests.cs index a18d12be..a5dec9dd 100644 --- a/ArchUnitNETTests/Loader/ArchLoaderTests.cs +++ b/ArchUnitNETTests/Loader/ArchLoaderTests.cs @@ -4,6 +4,9 @@ // // SPDX-License-Identifier: Apache-2.0 +extern alias LoaderTestAssemblyAlias; +extern alias OtherLoaderTestAssemblyAlias; + using System.Linq; using ArchUnitNET.Loader; using ArchUnitNET.xUnit; @@ -12,6 +15,9 @@ using static ArchUnitNET.Fluent.ArchRuleDefinition; using static ArchUnitNETTests.StaticTestArchitectures; +using DuplicateClass = LoaderTestAssemblyAlias::DuplicateClassAcrossAssemblies.DuplicateClass; +using OtherDuplicateClass = OtherLoaderTestAssemblyAlias::DuplicateClassAcrossAssemblies.DuplicateClass; + namespace ArchUnitNETTests.Loader { public class ArchLoaderTests @@ -30,10 +36,34 @@ public void LoadAssemblies() [Fact] public void SameFullNameInMultipleAssemblies() { - var types = LoaderTestArchitecture.Types.Where(type => - type.Namespace.FullName == "DuplicateClassAcrossAssemblies" - ); - Assert.Equal(2, types.Count()); + // Loaded as non-stub types + var types = LoaderTestArchitecture + .Types.Where(type => type.Namespace.FullName == "DuplicateClassAcrossAssemblies") + .ToList(); + Assert.Equal(2, types.Count); + Assert.Single(types, type => type.Assembly.Name.StartsWith("LoaderTestAssembly")); + Assert.Single(types, type => type.Assembly.Name.StartsWith("OtherLoaderTestAssembly")); + + // Loaded as stub types + + // We create a direct dependency of ArchUnitNETTests on the duplicate type such that they are loaded as stub types + +#pragma warning disable CS0219 // Variable is assigned but its value is never used + DuplicateClass duplicateClass = null; + OtherDuplicateClass otherDuplicateClass = null; +#pragma warning restore CS0219 // Variable is assigned but its value is never used + + types = ArchUnitNETTestArchitecture + .ReferencedTypes.Where(type => + type.Namespace.FullName == "DuplicateClassAcrossAssemblies" + ) + .ToList(); + var types2 = ArchUnitNETTestArchitecture + .ReferencedTypes.Where(type => + type.FullName.Contains("Duplicate") + ) + .ToList(); + Assert.Equal(2, types.Count); Assert.Single(types, type => type.Assembly.Name.StartsWith("LoaderTestAssembly")); Assert.Single(types, type => type.Assembly.Name.StartsWith("OtherLoaderTestAssembly")); } diff --git a/TestAssemblies/LoaderTestAssembly/DuplicateClassAcrossAssemblies.cs b/TestAssemblies/LoaderTestAssembly/DuplicateClassAcrossAssemblies.cs index ab43e139..7234c0ff 100644 --- a/TestAssemblies/LoaderTestAssembly/DuplicateClassAcrossAssemblies.cs +++ b/TestAssemblies/LoaderTestAssembly/DuplicateClassAcrossAssemblies.cs @@ -1,3 +1,3 @@ namespace DuplicateClassAcrossAssemblies; -internal class DuplicateClass { } +public class DuplicateClass { } diff --git a/TestAssemblies/OtherLoaderTestAssembly/DuplicateClassAcrossAssemblies.cs b/TestAssemblies/OtherLoaderTestAssembly/DuplicateClassAcrossAssemblies.cs index ab43e139..7234c0ff 100644 --- a/TestAssemblies/OtherLoaderTestAssembly/DuplicateClassAcrossAssemblies.cs +++ b/TestAssemblies/OtherLoaderTestAssembly/DuplicateClassAcrossAssemblies.cs @@ -1,3 +1,3 @@ namespace DuplicateClassAcrossAssemblies; -internal class DuplicateClass { } +public class DuplicateClass { }