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
124 changes: 115 additions & 9 deletions src/AsmResolver.DotNet/Signatures/GenericContext.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
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,32 +20,37 @@ 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>
Expand All @@ -52,6 +59,11 @@ public IGenericArgumentsProvider? Method
/// <summary>
/// Resolves a type parameter to a type argument, based on the current generic context.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="parameter">The parameter to get the argument value for.</param>
/// <returns>The argument type.</returns>
public TypeSignature GetTypeArgument(GenericParameterSignature parameter)
Expand All @@ -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();
}


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

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

/// <summary>
/// Gets a type generic context from <see cref="ITypeDescriptor"/>.
/// </summary>
/// <param name="type">Type to get the 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 a method and/or type generic context from <see cref="MethodSpecification"/>.
/// </summary>
/// <param name="method">Method specification to get the generic context from.</param>
/// <returns>Generic context.</returns>
public static GenericContext FromMethod(MethodSpecification method)
{
return method.DeclaringType is TypeSpecification {Signature: GenericInstanceTypeSignature typeSig}
? new GenericContext(typeSig, method.Signature)
: new GenericContext(null, method.Signature);
}

/// <summary>
/// Gets a method and/or type generic context from <see cref="IMethodDescriptor"/>.
/// </summary>
/// <param name="method">Method to get the 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 a type generic context from <see cref="IFieldDescriptor"/>.
/// </summary>
/// <param name="field">Field to get the generic context from.</param>
/// <returns>Generic context.</returns>
public static GenericContext FromField(IFieldDescriptor field) =>
field switch
{
MemberReference member => FromMember(member),
_ => default
};

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

/// <summary>
/// Gets a type generic context from <see cref="IMemberDescriptor"/>.
/// </summary>
/// <param name="member">Member to get the 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