Skip to content

Commit

Permalink
Merge pull request #418 from ElektroKill/feature/parameter-qol-improv…
Browse files Browse the repository at this point in the history
…ements

Add Parameter.CreateParameterDefinition helper method
  • Loading branch information
Washi1337 committed Mar 2, 2023
2 parents 0756858 + 9cbf3aa commit a105ac3
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 5 deletions.
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()
{
}
}
}

0 comments on commit a105ac3

Please sign in to comment.