Skip to content

Commit

Permalink
handle generic attributes (C#11) in the HaveCustomAttribute rule
Browse files Browse the repository at this point in the history
  • Loading branch information
NeVeSpl committed Nov 26, 2023
1 parent a6a49ec commit f49ed7a
Show file tree
Hide file tree
Showing 18 changed files with 347 additions and 104 deletions.
4 changes: 2 additions & 2 deletions sources/NetArchTest/Condition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ private void AddFunctionCall(Func<FunctionSequenceExecutionContext, IEnumerable<
/// <returns>An updated set of conditions that can be applied to a list of types.</returns>
public ConditionList HaveCustomAttribute(Type attribute)
{
AddFunctionCall(x => FunctionDelegates.HaveCustomAttribute(x, attribute, true));
AddFunctionCall((context, inputTypes) => FunctionDelegates.HaveCustomAttribute(context, inputTypes, attribute, true));
return CreateConditionList();
}
/// <summary>
Expand All @@ -68,7 +68,7 @@ public ConditionList HaveCustomAttribute<T>()
/// <returns>An updated set of conditions that can be applied to a list of types.</returns>
public ConditionList NotHaveCustomAttribute(Type attribute)
{
AddFunctionCall(x => FunctionDelegates.HaveCustomAttribute(x, attribute, false));
AddFunctionCall((context, inputTypes) => FunctionDelegates.HaveCustomAttribute(context, inputTypes, attribute, false));
return CreateConditionList();
}
/// <summary>
Expand Down
72 changes: 71 additions & 1 deletion sources/NetArchTest/Dependencies/TypeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static IEnumerable<string> Parse(string fullName, bool parseNames)

var monoTypeParser = Activator.CreateInstance(mono_TypeParserType, BindingFlags.Instance | BindingFlags.NonPublic, null, args: new object[] { fullName }, null);
var monoType = mono_ParseTypeMethod.Invoke(monoTypeParser, new object[] { false });
foreach(var token in WalkThroughMonoType(monoType))
foreach (var token in WalkThroughMonoType(monoType))
{
yield return token;
}
Expand Down Expand Up @@ -83,5 +83,75 @@ private static IEnumerable<string> WalkThroughMonoType(object monoType)
}
}
}




public static string ParseReflectionNameToRuntimeName(string fullName)
{
var monoTypeParser = Activator.CreateInstance(mono_TypeParserType, BindingFlags.Instance | BindingFlags.NonPublic, null, args: new object[] { fullName }, null);
var monoType = mono_ParseTypeMethod.Invoke(monoTypeParser, new object[] { false });
return string.Concat(WalkThroughMonoType2(monoType));
}

private static IEnumerable<string> WalkThroughMonoType2(object monoType)
{
yield return mono_type_fullnameField.GetValue(monoType) as string;

var nested = mono_nested_namesField.GetValue(monoType) as string[];
if (nested != null)
{
foreach (var nestedName in nested)
{
yield return "/";
yield return nestedName;
}
}

var generics = mono_generic_argumentsField.GetValue(monoType) as object[];
if (generics != null)
{
yield return "<";
for (int i = 0; i < generics.Length; i++)
{
object generic = generics[i];
foreach (var token in WalkThroughMonoType(generic))
{
yield return token;
}
if (i < generics.Length - 1)
{
yield return ",";
}
}
yield return ">";
}

var specs = mono_specsField.GetValue(monoType) as int[];
if (specs != null)
{
for (int i = 0; i < specs.Length; ++i)
{
if (specs[i] == -1)
{
yield return "*";
}
if (specs[i] == -2)
{
yield return "&";
}
if (specs[i] == -3)
{
yield return "[]";
}
if (specs[i] >= 2)
{
yield return "[,]";
}
}
}
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ namespace Mono.Cecil
{
static internal class TypeDefinitionExtensions
{
/// <summary>
/// Tests whether one class inherits from another.
/// </summary>
/// <param name="child">The class that is inheriting from the parent.</param>
/// <param name="parent">The parent that is inherited.</param>
/// <returns>An indication of whether the child inherits from the parent.</returns>
public static bool IsSubclassOf(this TypeDefinition child, TypeDefinition parent)
public static bool IsSubclassOf(this TypeReference child, TypeReference parent)
{
var typeDef = child.Resolve();
return typeDef.IsSubclassOf(parent);
}

public static bool IsSubclassOf(this TypeDefinition child, TypeReference parent)
{
if (parent != null)
{
Expand All @@ -22,36 +22,8 @@ public static bool IsSubclassOf(this TypeDefinition child, TypeDefinition parent
}

return false;
}

/// <summary>
/// Tests whether two type definitions are from the same assembly.
/// The comparison is based on the full assembly names.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns>An indication of whether the both types are from the same assembly.</returns>
public static bool IsFromSameAssemblyAs(this TypeDefinition a, TypeDefinition b)
{
return a.Module.Assembly.ToString() == b.Module.Assembly.ToString();
}
}

/// <summary>
/// Tests whether the provided types are the same type.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns>An indication of whether the types are the same.</returns>
public static bool IsSameTypeAs(this TypeDefinition a, TypeDefinition b)
{
return a.IsFromSameAssemblyAs(b) && a.MetadataToken == b.MetadataToken;
}

/// <summary>
/// Enumerate the base classes throughout the chain of inheritence.
/// </summary>
/// <param name="classType">The class to enumerate.</param>
/// <returns>The enumeration of base classes.</returns>
private static IEnumerable<TypeDefinition> EnumerateBaseClasses(this TypeDefinition classType)
{
for (var typeDefinition = classType; typeDefinition != null; typeDefinition = typeDefinition.BaseType?.Resolve())
Expand All @@ -60,27 +32,37 @@ private static IEnumerable<TypeDefinition> EnumerateBaseClasses(this TypeDefinit
}
}

public static bool IsAlmostEqualTo(this TypeReference child, TypeDefinition parent)
{
if (child is GenericInstanceType genericInstanceTypeB)
{
if (parent.IsSameTypeAs(genericInstanceTypeB.ElementType))
{
return true;
}
}

if (parent.IsSameTypeAs(child))
{
return true;
}

return false;
}



/// <summary>
/// Convert the definition to a <see cref="Type"/> object instance.
/// </summary>
/// <param name="typeDefinition">The type definition to convert.</param>
/// <returns>The equivalent <see cref="Type"/> object instance.</returns>
public static Type ToType(this TypeDefinition typeDefinition)
{
var fullName = RuntimeNameToReflectionName(typeDefinition.FullName);
var fullName = typeDefinition.FullName.RuntimeNameToReflectionName();
return Type.GetType(string.Concat(fullName, ", ", typeDefinition.Module.Assembly.FullName), true);
}
public static string RuntimeNameToReflectionName(this string cliName)
{
// Nested types have a forward slash that should be replaced with "+"
// C++ template instantiations contain comma separator for template arguments,
// getting address operators and pointer type designations which should be prefixed by backslash
var fullName = cliName.Replace("/", "+")
.Replace(",", "\\,")
.Replace("&", "\\&")
.Replace("*", "\\*");
return fullName;
}




Expand Down Expand Up @@ -150,9 +132,9 @@ public static string GetNameWithoutGenericPart(this TypeDefinition typeDefinitio
}
return typeDefinition.Name.RemoveGenericPart();
}






public static bool IsDelegate(this TypeDefinition typeDefinition)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,31 @@ public static string GetFullNameWithoutGenericParameters(this TypeReference type

return typeReference.FullName;
}

public static bool IsSameTypeAs(this TypeReference a, TypeReference b)
{
bool sameAssembly = a.IsFromSameAssemblyAs(b);
bool sameName = string.Equals(a.FullName, b.FullName, StringComparison.Ordinal);
return sameAssembly && sameName;
}
private static bool IsFromSameAssemblyAs(this TypeReference a, TypeReference b)
{
var aName = GetAssemblyName(a.Scope);
var bName = GetAssemblyName(b.Scope);

return aName == bName;
}
private static string GetAssemblyName(IMetadataScope scope)
{
if (scope is ModuleDefinition moduleDefinition)
{
return moduleDefinition.Assembly.FullName;
}
if (scope is AssemblyNameReference assemblyNameReference)
{
return assemblyNameReference.FullName;
}
return scope.Name;
}
}
}
19 changes: 19 additions & 0 deletions sources/NetArchTest/Extensions/System/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,24 @@ public static string RemoveGenericPart(this string name)
}
return name;
}


public static string RuntimeNameToReflectionName(this string cliName)
{
// Nested types have a forward slash that should be replaced with "+"
// C++ template instantiations contain comma separator for template arguments,
// getting address operators and pointer type designations which should be prefixed by backslash
var fullName = cliName.Replace("/", "+")
.Replace(",", "\\,")
.Replace("&", "\\&")
.Replace("*", "\\*");
return fullName;
}

public static string ReflectionNameToRuntimeName(this string typeName)
{
var fullName = typeName.Replace("+", "/");
return fullName;
}
}
}
47 changes: 29 additions & 18 deletions sources/NetArchTest/Extensions/System/TypeExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,41 @@
using System.Linq;
using System.Reflection;
using System.Reflection;
using Mono.Cecil;
using NetArchTest.Dependencies;

namespace System
{
{
static internal class TypeExtensions
{
/// <summary>
/// Converts the value to a <see cref="TypeDefinition"/> instance.
/// </summary>
/// <param name="type">The type to convert.</param>
/// <returns>The converted value.</returns>
public static TypeDefinition ToTypeDefinition(this Type type)
{
// Get the assembly using reflection
var assembly = Assembly.GetAssembly(type);
var reflectionAssembly = Assembly.GetAssembly(type);
var assemblyDef = AssemblyDefinition.ReadAssembly(reflectionAssembly.Location);

// Load the assembly into the Mono.Cecil library
var assemblyDef = AssemblyDefinition.ReadAssembly(assembly.Location);
foreach (var module in assemblyDef.Modules)
{
var typeRef = module.GetType(type.FullName, true);
var typeDef = typeRef?.Resolve();
if (typeDef is not null) return typeDef;
}

// Find the matching type
var dependencies = (assemblyDef.Modules
.SelectMany(t => t.Types)
.Where(t => t.IsClass && t.Namespace != null && t.FullName.Equals(type.FullName, StringComparison.InvariantCultureIgnoreCase)));
return null;
}

public static string GetNormalizedFullName(this Type type)
{
var name = type.Name;
var fullName = type.FullName;
var toString = type.ToString();
var assemblyQualifiedName = type.AssemblyQualifiedName;

var monoName = TypeParser.ParseReflectionNameToRuntimeName(type.FullName);

if (type.IsGenericType && type.ContainsGenericParameters == false)
{
//return toString.ReflectionNameToRuntimeName();
}

return dependencies.FirstOrDefault();
return monoName;
}
}
}
}
Loading

0 comments on commit f49ed7a

Please sign in to comment.