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

Add Parameter.CreateParameterDefinition helper method #418

11 changes: 11 additions & 0 deletions src/AsmResolver.DotNet/Collections/Parameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,17 @@ public TypeSignature ParameterType
/// <inheritdoc />
public string Name => Definition?.Name ?? GetDummyArgumentName(MethodSignatureIndex);

/// <summary>
/// Creates a or returns the existing <see cref="ParameterDefinition"/> corresponding to this parameter.
/// If a <see cref="ParameterDefinition"/> is created it is automatically added to the method definition.
/// </summary>
public ParameterDefinition GetOrCreateDefinition()
{
if (_parentCollection is null)
throw new InvalidOperationException("Cannot create a parameter definition for a parameter that has been removed from its parent collection.");
return _parentCollection.GetOrCreateParameterDefinition(this);
}

[SuppressMessage("ReSharper", "InconsistentlySynchronizedField")]
private static string GetDummyArgumentName(int index)
{
Expand Down
31 changes: 28 additions & 3 deletions src/AsmResolver.DotNet/Collections/ParameterCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,24 @@ private void UpdateParameterTypes()

private TypeSignature? GetThisParameterType()
{
if (_owner.DeclaringType is null)
var declaringType = _owner.DeclaringType;
if (declaringType is null)
return null;

var result = _owner.DeclaringType.ToTypeSignature();
if (_owner.DeclaringType.IsValueType)
TypeSignature result;
if (declaringType.GenericParameters.Count > 0)
{
var genArgs = new TypeSignature[declaringType.GenericParameters.Count];
for (int i = 0; i < genArgs.Length; i++)
genArgs[i] = new GenericParameterSignature(_owner.Module, GenericParameterType.Type, i);
result = declaringType.MakeGenericInstanceType(genArgs);
}
else
{
result = declaringType.ToTypeSignature();
}

if (declaringType.IsValueType)
result = result.MakeByReferenceType();

return result;
Expand All @@ -142,6 +155,18 @@ private void UpdateParameterTypes()
return _owner.ParameterDefinitions.FirstOrDefault(p => p.Sequence == sequence);
}

internal ParameterDefinition GetOrCreateParameterDefinition(Parameter parameter)
{
if (parameter == ThisParameter)
throw new InvalidOperationException("Cannot retrieve a parameter definition for the virtual this parameter.");
if (parameter.Definition is not null)
return parameter.Definition;

var parameterDefinition = new ParameterDefinition(parameter.Sequence, Utf8String.Empty, 0);
_owner.ParameterDefinitions.Add(parameterDefinition);
return parameterDefinition;
}

internal void PushParameterUpdateToSignature(Parameter parameter)
{
if (_owner.Signature is null)
Expand Down
2 changes: 1 addition & 1 deletion src/AsmResolver.DotNet/Signatures/MethodSignatureBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ protected void WriteParametersAndReturnType(BlobSerializationContext context)
public int GetTotalParameterCount()
{
int count = ParameterTypes.Count + SentinelParameterTypes.Count;
if (HasThis || ExplicitThis)
if (HasThis && !ExplicitThis)
count++;
return count;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public GenericParameterSignature(GenericParameterType parameterType, int index)
/// <param name="module">The module in which this generic parameter signature resides.</param>
/// <param name="parameterType">Indicates the parameter signature is declared by a type or a method.</param>
/// <param name="index">The index of the referenced parameter.</param>
public GenericParameterSignature(ModuleDefinition module, GenericParameterType parameterType, int index)
public GenericParameterSignature(ModuleDefinition? module, GenericParameterType parameterType, int index)
{
Scope = module;
ParameterType = parameterType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using AsmResolver.DotNet.Signatures.Types;
using AsmResolver.DotNet.TestCases.Methods;
using AsmResolver.PE.DotNet.Metadata.Strings;
using AsmResolver.PE.DotNet.Metadata.Tables.Rows;
using Xunit;

namespace AsmResolver.DotNet.Tests.Collections
Expand All @@ -24,6 +25,13 @@ private static MethodDefinition ObtainInstanceTestMethod(string name)
return type.Methods.First(m => m.Name == name);
}

private static MethodDefinition ObtainGenericInstanceTestMethod(string name)
{
var module = ModuleDefinition.FromFile(typeof(GenericInstanceMethods<,>).Assembly.Location);
var type = module.TopLevelTypes.First(t => t.Name == "GenericInstanceMethods`2");
return type.Methods.First(m => m.Name == name);
}

[Fact]
public void ReadEmptyParametersFromStaticMethod()
{
Expand Down Expand Up @@ -111,6 +119,23 @@ public void ReadMultipleParametersFromInstanceMethod()
Assert.Equal(nameof(InstanceMethods), method.Parameters.ThisParameter.ParameterType.Name);
}

[Fact]
public void ReadEmptyParametersFromGenericInstanceMethod()
{
var method = ObtainGenericInstanceTestMethod(nameof(GenericInstanceMethods<int, int>.InstanceParameterlessMethod));
Assert.Empty(method.Parameters);
Assert.NotNull(method.Parameters.ThisParameter);
var genericInstanceType = Assert.IsAssignableFrom<GenericInstanceTypeSignature>(method.Parameters.ThisParameter.ParameterType);
Assert.Equal("GenericInstanceMethods`2", genericInstanceType.GenericType.Name);
Assert.Equal(2, genericInstanceType.TypeArguments.Count);
Assert.All(genericInstanceType.TypeArguments, (typeArgument, i) =>
{
var genericParameterSignature = Assert.IsAssignableFrom<GenericParameterSignature>(typeArgument);
Assert.Equal(GenericParameterType.Type, genericParameterSignature.ParameterType);
Assert.Equal(i, genericParameterSignature.Index);
});
}

[Fact]
public void ReadReturnTypeFromStaticParameterlessMethod()
{
Expand Down Expand Up @@ -190,5 +215,48 @@ public void UnnamedParameterShouldResultInDummyName()
param.Name = null;
Assert.All(method.Parameters, p => Assert.Equal(p.Name, $"A_{p.MethodSignatureIndex}"));
}

[Fact]
public void GetOrCreateDefinitionShouldCreateNewDefinition()
{
var dummyModule = new ModuleDefinition("TestModule");
var corLibTypesFactory = dummyModule.CorLibTypeFactory;
var method = new MethodDefinition("TestMethodNoParameterDefinitions",
MethodAttributes.Public | MethodAttributes.Static,
MethodSignature.CreateStatic(corLibTypesFactory.Void, corLibTypesFactory.Int32));

var param = Assert.Single(method.Parameters);

Assert.Null(param.Definition);
var definition = param.GetOrCreateDefinition();

Assert.Equal(param.Sequence, definition.Sequence);
Assert.Equal(Utf8String.Empty, definition.Name);
Assert.Equal((ParameterAttributes)0, definition.Attributes);
Assert.Contains(definition, method.ParameterDefinitions);
Assert.Same(definition, param.Definition);
}

[Fact]
public void GetOrCreateDefinitionShouldReturnExistingDefinition()
{
var method = ObtainStaticTestMethod(nameof(MultipleMethods.SingleParameterMethod));

var param = Assert.Single(method.Parameters);

var existingDefinition = param.Definition;
Assert.NotNull(existingDefinition);
var definition = param.GetOrCreateDefinition();
Assert.Same(existingDefinition, definition);
}

[Fact]
public void GetOrCreateDefinitionThrowsOnVirtualThisParameter()
{
var method = ObtainInstanceTestMethod(nameof(InstanceMethods.InstanceParameterlessMethod));

Assert.NotNull(method.Parameters.ThisParameter);
Assert.Throws<InvalidOperationException>(() => method.Parameters.ThisParameter.GetOrCreateDefinition());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace AsmResolver.DotNet.TestCases.Methods
{
public class GenericInstanceMethods<T, U>
{
public void InstanceParameterlessMethod()
{
}
}
}