diff --git a/src/AsmResolver.DotNet/Signatures/GenericContext.cs b/src/AsmResolver.DotNet/Signatures/GenericContext.cs
index 79571b830..65b435eee 100644
--- a/src/AsmResolver.DotNet/Signatures/GenericContext.cs
+++ b/src/AsmResolver.DotNet/Signatures/GenericContext.cs
@@ -1,10 +1,12 @@
using System;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
using AsmResolver.DotNet.Signatures.Types;
namespace AsmResolver.DotNet.Signatures
{
///
- /// Provides a context within a generic instantiation, including the type arguments of the enclosing type and method.
+ /// Provides a context within a generic instantiation, including the type arguments of the enclosing type and method.
///
public readonly struct GenericContext
{
@@ -18,9 +20,9 @@ public GenericContext(IGenericArgumentsProvider? type, IGenericArgumentsProvider
Type = type;
Method = method;
}
-
+
///
- /// Gets the object responsible for providing type arguments defined by the current generic type instantiation.
+ /// Gets the object responsible for providing type arguments defined by the current generic type instantiation.
///
public IGenericArgumentsProvider? Type
{
@@ -28,7 +30,7 @@ public IGenericArgumentsProvider? Type
}
///
- /// Gets the object responsible for providing type arguments defined by the current generic method instantiation.
+ /// Gets the object responsible for providing type arguments defined by the current generic method instantiation.
///
public IGenericArgumentsProvider? Method
{
@@ -36,14 +38,19 @@ public IGenericArgumentsProvider? Method
}
///
- /// Enters a new generic context with a new type providing type arguments.
+ /// Returns true if both Type and Method providers are null
+ ///
+ public bool IsEmpty => Type is null && Method is null;
+
+ ///
+ /// Enters a new generic context with a new type providing type arguments.
///
/// The new type providing the type arguments.
/// The new generic context.
public GenericContext WithType(IGenericArgumentsProvider type) => new GenericContext(type, Method);
-
+
///
- /// Enters a new generic context with a new method providing type arguments.
+ /// Enters a new generic context with a new method providing type arguments.
///
/// The new method providing the type arguments.
/// The new generic context.
@@ -52,6 +59,11 @@ public IGenericArgumentsProvider? Method
///
/// Resolves a type parameter to a type argument, based on the current generic context.
///
+ ///
+ /// If a type parameter within the signature references a parameter that is not captured by the context
+ /// (i.e. the corresponding generic argument provider is set to null),
+ /// then this type parameter will not be substituted.
+ ///
/// The parameter to get the argument value for.
/// The argument type.
public TypeSignature GetTypeArgument(GenericParameterSignature parameter)
@@ -62,14 +74,108 @@ public TypeSignature GetTypeArgument(GenericParameterSignature parameter)
GenericParameterType.Method => Method,
_ => throw new ArgumentOutOfRangeException()
};
-
+
if (argumentProvider is null)
- throw new ArgumentOutOfRangeException();
+ return parameter;
if (parameter.Index >= 0 && parameter.Index < argumentProvider.TypeArguments.Count)
return argumentProvider.TypeArguments[parameter.Index];
throw new ArgumentOutOfRangeException();
}
+
+
+ ///
+ /// Gets a type generic context from .
+ ///
+ /// Type specification to get the generic context from.
+ /// Generic context.
+ public static GenericContext FromType(TypeSpecification type) => type.Signature switch
+ {
+ GenericInstanceTypeSignature typeSig => new GenericContext(typeSig, null),
+ _ => default
+ };
+
+ ///
+ /// Gets a type generic context from .
+ ///
+ /// Generic type signature to get the generic context from.
+ /// Generic context.
+ public static GenericContext FromType(GenericInstanceTypeSignature type) => new(type, null);
+
+ ///
+ /// Gets a type generic context from .
+ ///
+ /// Type to get the generic context from.
+ /// Generic context.
+ public static GenericContext FromType(ITypeDescriptor type) => type switch
+ {
+ TypeSpecification typeSpecification => FromType(typeSpecification),
+ GenericInstanceTypeSignature typeSig => FromType(typeSig),
+ _ => default
+ };
+
+ ///
+ /// Gets a method and/or type generic context from .
+ ///
+ /// Method specification to get the generic context from.
+ /// Generic context.
+ public static GenericContext FromMethod(MethodSpecification method)
+ {
+ return method.DeclaringType is TypeSpecification {Signature: GenericInstanceTypeSignature typeSig}
+ ? new GenericContext(typeSig, method.Signature)
+ : new GenericContext(null, method.Signature);
+ }
+
+ ///
+ /// Gets a method and/or type generic context from .
+ ///
+ /// Method to get the generic context from.
+ /// Generic context.
+ public static GenericContext FromMethod(IMethodDescriptor method) =>
+ method switch
+ {
+ MethodSpecification methodSpecification => FromMethod(methodSpecification),
+ MemberReference member => FromMember(member),
+ _ => default
+ };
+
+ ///
+ /// Gets a type generic context from .
+ ///
+ /// Field to get the generic context from.
+ /// Generic context.
+ public static GenericContext FromField(IFieldDescriptor field) =>
+ field switch
+ {
+ MemberReference member => FromMember(member),
+ _ => default
+ };
+
+ ///
+ /// Gets a type generic context from .
+ ///
+ /// Member reference to get the generic context from.
+ /// Generic context.
+ public static GenericContext FromMember(MemberReference member) =>
+ member.DeclaringType switch
+ {
+ TypeSpecification type => FromType(type),
+ _ => default
+ };
+
+ ///
+ /// Gets a type generic context from .
+ ///
+ /// Member to get the generic context from.
+ /// Generic context.
+ public static GenericContext FromMember(IMemberDescriptor member) =>
+ member switch
+ {
+ IMethodDescriptor method => FromMethod(method),
+ IFieldDescriptor field => FromField(field),
+ ITypeDescriptor type => FromType(type),
+ _ => default
+ };
}
}
diff --git a/test/AsmResolver.DotNet.Tests/Signatures/GenericContextTest.cs b/test/AsmResolver.DotNet.Tests/Signatures/GenericContextTest.cs
index 73c2de250..aa9a55beb 100644
--- a/test/AsmResolver.DotNet.Tests/Signatures/GenericContextTest.cs
+++ b/test/AsmResolver.DotNet.Tests/Signatures/GenericContextTest.cs
@@ -1,15 +1,14 @@
-using System;
using System.Collections.Generic;
using AsmResolver.DotNet.Signatures;
using AsmResolver.DotNet.Signatures.Types;
+using AsmResolver.PE.DotNet.Metadata.Tables.Rows;
using Xunit;
-using Xunit.Sdk;
namespace AsmResolver.DotNet.Tests.Signatures
{
public class GenericContextTest
{
- private ReferenceImporter _importer;
+ private readonly ReferenceImporter _importer;
public GenericContextTest()
{
@@ -20,22 +19,22 @@ public GenericContextTest()
[Theory]
[InlineData(GenericParameterType.Type)]
[InlineData(GenericParameterType.Method)]
- public void ResolveGenericParameterWithEmptyTypeShouldThrow(GenericParameterType parameterType)
+ public void ResolveGenericParameterWithEmptyType(GenericParameterType parameterType)
{
var context = new GenericContext();
var parameter = new GenericParameterSignature(parameterType, 0);
- Assert.Throws(() => context.GetTypeArgument(parameter));
+ Assert.Equal(parameter, context.GetTypeArgument(parameter));
}
-
+
[Fact]
public void ResolveMethodGenericParameterWithMethod()
{
var genericInstance = new GenericInstanceMethodSignature();
genericInstance.TypeArguments.Add(_importer.ImportTypeSignature(typeof(string)));
-
+
var context = new GenericContext(null, genericInstance);
-
+
var parameter = new GenericParameterSignature(GenericParameterType.Method, 0);
Assert.Equal("System.String", context.GetTypeArgument(parameter).FullName);
}
@@ -45,9 +44,9 @@ public void ResolveTypeGenericParameterWithType()
{
var genericInstance = new GenericInstanceTypeSignature(_importer.ImportType(typeof(List<>)), false);
genericInstance.TypeArguments.Add(_importer.ImportTypeSignature(typeof(string)));
-
+
var context = new GenericContext(genericInstance, null);
-
+
var parameter = new GenericParameterSignature(GenericParameterType.Type, 0);
Assert.Equal("System.String", context.GetTypeArgument(parameter).FullName);
}
@@ -57,12 +56,12 @@ public void ResolveTypeGenericParameterWithTypeAndMethod()
{
var genericType = new GenericInstanceTypeSignature(_importer.ImportType(typeof(List<>)), false);
genericType.TypeArguments.Add(_importer.ImportTypeSignature(typeof(string)));
-
+
var genericMethod = new GenericInstanceMethodSignature();
genericMethod.TypeArguments.Add(_importer.ImportTypeSignature(typeof(int)));
-
+
var context = new GenericContext(genericType, genericMethod);
-
+
var parameter = new GenericParameterSignature(GenericParameterType.Type, 0);
Assert.Equal("System.String", context.GetTypeArgument(parameter).FullName);
}
@@ -72,38 +71,319 @@ public void ResolveMethodGenericParameterWithTypeAndMethod()
{
var genericType = new GenericInstanceTypeSignature(_importer.ImportType(typeof(List<>)), false);
genericType.TypeArguments.Add(_importer.ImportTypeSignature(typeof(string)));
-
+
var genericMethod = new GenericInstanceMethodSignature();
genericMethod.TypeArguments.Add(_importer.ImportTypeSignature(typeof(int)));
-
+
var context = new GenericContext(genericType, genericMethod);
-
+
var parameter = new GenericParameterSignature(GenericParameterType.Method, 0);
Assert.Equal("System.Int32", context.GetTypeArgument(parameter).FullName);
}
[Fact]
- public void ResolveTypeGenericParameterWithOnlyMethodShouldThrow()
+ public void ResolveTypeGenericParameterWithOnlyMethod()
{
var genericInstance = new GenericInstanceMethodSignature();
genericInstance.TypeArguments.Add(_importer.ImportTypeSignature(typeof(string)));
-
+
var context = new GenericContext(null, genericInstance);
-
+
var parameter = new GenericParameterSignature(GenericParameterType.Type, 0);
- Assert.Throws(() => context.GetTypeArgument(parameter));
+ Assert.Equal(parameter, context.GetTypeArgument(parameter));
}
[Fact]
- public void ResolveMethodGenericParameterWithOnlyTypeShouldThrow()
+ public void ResolveMethodGenericParameterWithOnlyType()
{
var genericInstance = new GenericInstanceTypeSignature(_importer.ImportType(typeof(List<>)), false);
genericInstance.TypeArguments.Add(_importer.ImportTypeSignature(typeof(string)));
-
+
var context = new GenericContext(genericInstance, null);
-
+
var parameter = new GenericParameterSignature(GenericParameterType.Method, 0);
- Assert.Throws(() => context.GetTypeArgument(parameter));
+ Assert.Equal(parameter, context.GetTypeArgument(parameter));
+ }
+
+
+ [Fact]
+ public void ParseGenericFromTypeSignature()
+ {
+ var genericInstance = new GenericInstanceTypeSignature(_importer.ImportType(typeof(List<>)), false);
+ genericInstance.TypeArguments.Add(_importer.ImportTypeSignature(typeof(string)));
+
+ var context = GenericContext.FromType(genericInstance);
+ var context2 = GenericContext.FromMember(genericInstance);
+ var context3 = GenericContext.FromType((ITypeDescriptor) genericInstance);
+
+ Assert.Equal(context, context3);
+ Assert.Equal(context, context2);
+ Assert.False(context.IsEmpty);
+ Assert.Equal(genericInstance, context.Type);
+ Assert.Null(context.Method);
+ }
+
+ [Fact]
+ public void ParseGenericFromNonGenericTypeSignature()
+ {
+ var type = new TypeDefinition("", "Test type", TypeAttributes.Public);
+ var notGenericSignature = new TypeDefOrRefSignature(type);
+
+ var context = GenericContext.FromType(notGenericSignature);
+ var context2 = GenericContext.FromMember(notGenericSignature);
+
+ Assert.Equal(context, context2);
+ Assert.True(context.IsEmpty);
+ }
+
+ [Fact]
+ public void ParseGenericFromTypeSpecification()
+ {
+ var genericInstance = new GenericInstanceTypeSignature(_importer.ImportType(typeof(List<>)), false);
+ genericInstance.TypeArguments.Add(_importer.ImportTypeSignature(typeof(string)));
+ var typeSpecification = new TypeSpecification(genericInstance);
+
+ var context = GenericContext.FromType(typeSpecification);
+ var context2 = GenericContext.FromMember(typeSpecification);
+ var context3 = GenericContext.FromType((ITypeDescriptor) typeSpecification);
+
+ Assert.Equal(context, context3);
+ Assert.Equal(context, context2);
+ Assert.False(context.IsEmpty);
+ Assert.Equal(genericInstance, context.Type);
+ Assert.Null(context.Method);
+ }
+
+ [Fact]
+ public void ParseGenericFromMethodSpecification()
+ {
+ var genericParameter = new GenericParameterSignature(GenericParameterType.Method, 0);
+ var method = new MethodDefinition("TestMethod", MethodAttributes.Private,
+ MethodSignature.CreateStatic(genericParameter));
+ var genericInstance = new GenericInstanceMethodSignature();
+ genericInstance.TypeArguments.Add(_importer.ImportTypeSignature(typeof(int)));
+ var methodSpecification = new MethodSpecification(method, genericInstance);
+
+ var context = GenericContext.FromMethod(methodSpecification);
+ var context2 = GenericContext.FromMember(methodSpecification);
+ var context3 = GenericContext.FromMethod((IMethodDescriptor) methodSpecification);
+
+ Assert.Equal(context, context3);
+ Assert.Equal(context, context2);
+ Assert.False(context.IsEmpty);
+ Assert.Null(context.Type);
+ Assert.Equal(genericInstance, context.Method);
+ }
+
+ [Fact]
+ public void ParseGenericFromMethodSpecificationWithTypeSpecification()
+ {
+ var genericTypeInstance = new GenericInstanceTypeSignature(_importer.ImportType(typeof(List<>)), false);
+ genericTypeInstance.TypeArguments.Add(_importer.ImportTypeSignature(typeof(string)));
+ var typeSpecification = new TypeSpecification(genericTypeInstance);
+
+ var genericParameter = new GenericParameterSignature(GenericParameterType.Method, 0);
+ var method = new MemberReference(typeSpecification, "TestMethod",
+ MethodSignature.CreateStatic(genericParameter));
+
+ var genericMethodInstance = new GenericInstanceMethodSignature();
+ genericMethodInstance.TypeArguments.Add(_importer.ImportTypeSignature(typeof(int)));
+ var methodSpecification = new MethodSpecification(method, genericMethodInstance);
+
+
+ var context = GenericContext.FromMethod(methodSpecification);
+ var context2 = GenericContext.FromMember(methodSpecification);
+ var context3 = GenericContext.FromMethod((IMethodDescriptor) methodSpecification);
+
+ Assert.Equal(context, context3);
+ Assert.Equal(context, context2);
+ Assert.False(context.IsEmpty);
+ Assert.Equal(genericTypeInstance, context.Type);
+ Assert.Equal(genericMethodInstance, context.Method);
+ }
+
+ [Fact]
+ public void ParseGenericFromNotGenericTypeSpecification()
+ {
+ var type = new TypeDefinition("", "Test type", TypeAttributes.Public);
+ var notGenericSignature = new TypeDefOrRefSignature(type);
+
+ var typeSpecification = new TypeSpecification(notGenericSignature);
+
+ var context = GenericContext.FromType(typeSpecification);
+ var context2 = GenericContext.FromMember(typeSpecification);
+ var context3 = GenericContext.FromType((ITypeDescriptor) typeSpecification);
+
+ Assert.Equal(context, context3);
+ Assert.Equal(context, context2);
+ Assert.True(context.IsEmpty);
+ }
+
+ [Fact]
+ public void ParseGenericFromNotGenericMethodSpecification()
+ {
+ var type = new TypeDefinition("", "Test type", TypeAttributes.Public);
+ var notGenericSignature = new TypeDefOrRefSignature(type);
+
+ var method = new MethodDefinition("TestMethod", MethodAttributes.Private,
+ MethodSignature.CreateStatic(notGenericSignature));
+ var methodSpecification = new MethodSpecification(method, null);
+
+ var context = GenericContext.FromMethod(methodSpecification);
+ var context2 = GenericContext.FromMember(methodSpecification);
+ var context3 = GenericContext.FromMethod((IMethodDescriptor) methodSpecification);
+
+ Assert.Equal(context, context3);
+ Assert.Equal(context, context2);
+ Assert.True(context.IsEmpty);
+ }
+
+ [Fact]
+ public void ParseGenericFromNotGenericMethodSpecificationWithTypeSpecification()
+ {
+ var genericTypeInstance = new GenericInstanceTypeSignature(_importer.ImportType(typeof(List<>)), false);
+ genericTypeInstance.TypeArguments.Add(_importer.ImportTypeSignature(typeof(string)));
+ var typeSpecification = new TypeSpecification(genericTypeInstance);
+
+ var type = new TypeDefinition("", "Test type", TypeAttributes.Public);
+ var notGenericSignature = new TypeDefOrRefSignature(type);
+
+ var method = new MemberReference(typeSpecification, "TestMethod",
+ MethodSignature.CreateStatic(notGenericSignature));
+ var methodSpecification = new MethodSpecification(method, null);
+
+ var context = GenericContext.FromMethod(methodSpecification);
+ var context2 = GenericContext.FromMember(methodSpecification);
+ var context3 = GenericContext.FromMethod((IMethodDescriptor) methodSpecification);
+
+ Assert.Equal(context, context3);
+ Assert.Equal(context, context2);
+ Assert.False(context.IsEmpty);
+ Assert.Equal(genericTypeInstance, context.Type);
+ Assert.Null(context.Method);
+ }
+
+ [Fact]
+ public void ParseGenericFromField()
+ {
+ var genericTypeInstance = new GenericInstanceTypeSignature(_importer.ImportType(typeof(List<>)), false);
+ genericTypeInstance.TypeArguments.Add(_importer.ImportTypeSignature(typeof(string)));
+ var typeSpecification = new TypeSpecification(genericTypeInstance);
+
+ var genericParameter = new GenericParameterSignature(GenericParameterType.Type, 0);
+
+ var field = new FieldDefinition("Field", FieldAttributes.Private,
+ FieldSignature.CreateStatic(genericParameter));
+
+ var member = new MemberReference(typeSpecification, field.Name, field.Signature);
+
+ var context = GenericContext.FromField(member);
+ var context2 = GenericContext.FromMember(member);
+
+ Assert.Equal(context, context2);
+ Assert.False(context.IsEmpty);
+ Assert.Equal(genericTypeInstance, context.Type);
+ Assert.Null(context.Method);
+ }
+
+ [Fact]
+ public void ParseGenericFromNotGenericField()
+ {
+ var type = new TypeDefinition("", "Test type", TypeAttributes.Public);
+ var notGenericSignature = new TypeDefOrRefSignature(type);
+
+ var field = new FieldDefinition("Field", FieldAttributes.Private,
+ FieldSignature.CreateStatic(notGenericSignature));
+
+ var member = new MemberReference(type, field.Name, field.Signature);
+
+ var context = GenericContext.FromField(member);
+ var context2 = GenericContext.FromMember(member);
+
+ Assert.Equal(context, context2);
+ Assert.True(context.IsEmpty);
+ }
+
+ [Fact]
+ public void ParseGenericFromMethod()
+ {
+ var genericTypeInstance = new GenericInstanceTypeSignature(_importer.ImportType(typeof(List<>)), false);
+ genericTypeInstance.TypeArguments.Add(_importer.ImportTypeSignature(typeof(string)));
+ var typeSpecification = new TypeSpecification(genericTypeInstance);
+
+ var genericParameter = new GenericParameterSignature(GenericParameterType.Type, 0);
+
+ var method = new MethodDefinition("Method", MethodAttributes.Private,
+ MethodSignature.CreateStatic(genericParameter));
+
+ var member = new MemberReference(typeSpecification, method.Name, method.Signature);
+
+ var context = GenericContext.FromMethod(member);
+ var context2 = GenericContext.FromMember(member);
+
+ Assert.Equal(context, context2);
+ Assert.False(context.IsEmpty);
+ Assert.Equal(genericTypeInstance, context.Type);
+ Assert.Null(context.Method);
+ }
+
+ [Fact]
+ public void ParseGenericFromNotGenericMethod()
+ {
+ var type = new TypeDefinition("", "Test type", TypeAttributes.Public);
+ var notGenericSignature = new TypeDefOrRefSignature(type);
+
+ var method = new MethodDefinition("Method", MethodAttributes.Private,
+ MethodSignature.CreateStatic(notGenericSignature));
+
+ var member = new MemberReference(type, method.Name, method.Signature);
+
+ var context = GenericContext.FromMethod(member);
+ var context2 = GenericContext.FromMember(member);
+
+ Assert.Equal(context, context2);
+ Assert.True(context.IsEmpty);
+ }
+
+ [Fact]
+ public void ParseGenericFromProperty()
+ {
+ var genericTypeInstance = new GenericInstanceTypeSignature(_importer.ImportType(typeof(List<>)), false);
+ genericTypeInstance.TypeArguments.Add(_importer.ImportTypeSignature(typeof(string)));
+ var typeSpecification = new TypeSpecification(genericTypeInstance);
+
+ var genericParameter = new GenericParameterSignature(GenericParameterType.Type, 0);
+
+ var property = new PropertyDefinition("Property", PropertyAttributes.None,
+ PropertySignature.CreateStatic(genericParameter));
+
+ var member = new MemberReference(typeSpecification, property.Name, property.Signature);
+
+ var context = GenericContext.FromMethod(member);
+ var context2 = GenericContext.FromMember(member);
+
+ Assert.Equal(context, context2);
+ Assert.False(context.IsEmpty);
+ Assert.Equal(genericTypeInstance, context.Type);
+ Assert.Null(context.Method);
+ }
+
+ [Fact]
+ public void ParseGenericFromNotGenericProperty()
+ {
+ var type = new TypeDefinition("", "Test type", TypeAttributes.Public);
+ var notGenericSignature = new TypeDefOrRefSignature(type);
+
+ var property = new PropertyDefinition("Property", PropertyAttributes.None,
+ PropertySignature.CreateStatic(notGenericSignature));
+
+ var member = new MemberReference(type, property.Name, property.Signature);
+
+ var context = GenericContext.FromMethod(member);
+ var context2 = GenericContext.FromMember(member);
+
+ Assert.Equal(context, context2);
+ Assert.True(context.IsEmpty);
}
}
-}
\ No newline at end of file
+}