Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Throw on generic context GetTypeArgument + Add: GenericContext From methods #225

Merged
merged 11 commits into from
Nov 28, 2021
122 changes: 113 additions & 9 deletions src/AsmResolver.DotNet/Signatures/GenericContext.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
using System;
using System.ComponentModel;
using AsmResolver.DotNet.Signatures.Types;

namespace AsmResolver.DotNet.Signatures
{
/// <summary>
/// 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.
/// </summary>
public readonly struct GenericContext
{
Expand All @@ -18,39 +19,45 @@ public GenericContext(IGenericArgumentsProvider? type, IGenericArgumentsProvider
Type = type;
Method = method;
}

/// <summary>
/// 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.
/// </summary>
public IGenericArgumentsProvider? Type
{
get;
}

/// <summary>
/// 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.
/// </summary>
public IGenericArgumentsProvider? Method
{
get;
}

/// <summary>
/// Enters a new generic context with a new type providing type arguments.
/// Returns true if both Type and Method providers are null
/// </summary>
public bool IsEmpty => Type is null && Method is null;
JPaja marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Enters a new generic context with a new type providing type arguments.
/// </summary>
/// <param name="type">The new type providing the type arguments.</param>
/// <returns>The new generic context.</returns>
public GenericContext WithType(IGenericArgumentsProvider type) => new GenericContext(type, Method);

/// <summary>
/// Enters a new generic context with a new method providing type arguments.
/// Enters a new generic context with a new method providing type arguments.
/// </summary>
/// <param name="method">The new method providing the type arguments.</param>
/// <returns>The new generic context.</returns>
public GenericContext WithMethod(IGenericArgumentsProvider method) => new GenericContext(Type, method);

/// <summary>
/// Resolves a type parameter to a type argument, based on the current generic context.
/// If generic argument provider is not initialised for type parameter, than it will return itself.
JPaja marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <param name="parameter">The parameter to get the argument value for.</param>
/// <returns>The argument type.</returns>
Expand All @@ -62,14 +69,111 @@ 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();
}


/// <summary>
/// Gets type generic context from <see cref="TypeSpecification"/>.
JPaja marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <param name="type">Type specification to get generic context from.</param>
JPaja marked this conversation as resolved.
Show resolved Hide resolved
/// <returns>Generic context.</returns>
public static GenericContext FromType(TypeSpecification type) => type.Signature switch
{
GenericInstanceTypeSignature typeSig => new GenericContext(typeSig, null),
_ => default
};

/// <summary>
/// Gets type generic context from <see cref="GenericInstanceTypeSignature"/>.
/// </summary>
/// <param name="type">Generic type signature to get generic context from.</param>
/// <returns>Generic context.</returns>
public static GenericContext FromType(GenericInstanceTypeSignature type) => new(type, null);

/// <summary>
/// Gets type generic context from <see cref="ITypeDescriptor"/>.
/// </summary>
/// <param name="type">Type to get generic context from.</param>
/// <returns>Generic context.</returns>
public static GenericContext FromType(ITypeDescriptor type) => type switch
{
TypeSpecification typeSpecification => FromType(typeSpecification),
GenericInstanceTypeSignature typeSig => FromType(typeSig),
_ => default
};

/// <summary>
/// Gets method and/or type generic context from <see cref="MethodSpecification"/>.
/// </summary>
/// <param name="method">Method specification to get generic context from.</param>
/// <returns>Generic context.</returns>
public static GenericContext FromMethod(MethodSpecification method) =>
(method.DeclaringType, method.Signature) switch
JPaja marked this conversation as resolved.
Show resolved Hide resolved
{
(TypeSpecification {Signature: GenericInstanceTypeSignature typeSig}, { } methodSig) =>
new GenericContext(typeSig, methodSig),
(_, { } methodSig) => new GenericContext(null, methodSig),
(TypeSpecification typeSpec, _) => FromType(typeSpec),
_ => default
};

/// <summary>
/// Gets method and/or type generic context from <see cref="IMethodDescriptor"/>.
/// </summary>
/// <param name="method">Method to get generic context from.</param>
/// <returns>Generic context.</returns>
public static GenericContext FromMethod(IMethodDescriptor method) =>
method switch
{
MethodSpecification methodSpecification => FromMethod(methodSpecification),
MemberReference member => FromMember(member),
_ => default
};

/// <summary>
/// Gets type generic context from <see cref="IFieldDescriptor"/>.
/// </summary>
/// <param name="field">Field to get generic context from.</param>
/// <returns>Generic context.</returns>
public static GenericContext FromField(IFieldDescriptor field) =>
field switch
{
MemberReference member => FromMember(member),
_ => default
};

/// <summary>
/// Gets type generic context from <see cref="MemberReference"/>.
/// </summary>
/// <param name="member">Member reference to get generic context from.</param>
/// <returns>Generic context.</returns>
public static GenericContext FromMember(MemberReference member) =>
member.DeclaringType switch
{
TypeSpecification type => FromType(type),
_ => default
};

/// <summary>
/// Gets type generic context from <see cref="IMemberDescriptor"/>.
/// </summary>
/// <param name="member">Member to get generic context from.</param>
/// <returns>Generic context.</returns>
public static GenericContext FromMember(IMemberDescriptor member) =>
member switch
{
IMethodDescriptor method => FromMethod(method),
IFieldDescriptor field => FromField(field),
ITypeDescriptor type => FromType(type),
_ => default
};
}
}
Loading