Skip to content

Conversation

@jtschuster
Copy link
Member

@jtschuster jtschuster commented Aug 22, 2022

I've run into multiple issues when I think an int parameterIndex is the source-based index when it is the IL-based index (offset by 1 for this) or vice versa.

In order to fix this, we can use different enums for each definition of "Parameter Index" to enforce the correct usage and avoid the off by one errors. The enums rely on the underlying int value to provide the index number.

This change adds ILParameterIndex to represent the IL-based indexing where 0 may refer to this, and SourceParameterIndex where 0 is the first "real" parameter in C# source code. Converting from ILParameterIndex to SourceParameterIndex returns a SourceParameterKind to differentiate between thisand other parameters and ensure that anySourceParameterIndexwill be able to indexMethodReference.Parameters`.

It uses these in ParameterReferenceValue as well as GetMethodParameterValue() and adds a GetMethodThisParameterValue(), ThisParameterReferenceValue, and bubbles the changes where necessary.

Most places should use SourceParameterIndex, and convert to ILParameterIndex when necessary or at the boundary of updated and old code.

I'll continue to update the code to use these enums in other PRs.

Copy link
Member

@vitek-karas vitek-karas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't look at the details, but I like the approach of using C# type system to differentiate the two cases - really nice idea!

}
}

public static SourceParameterIndex GetSourceParameter (MethodDefinition method, Instruction operation)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public static SourceParameterIndex GetSourceParameter (MethodDefinition method, Instruction operation)
public static SourceParameterIndex GetSourceParameterIndex (MethodDefinition method, Instruction operation)

/// <summary>
/// Represents the index of arguments passed to a function in IL. Generally prefer to use <see cref="ILLink.Shared.SourceParameterIndex"/> when possible. See also <seealso cref="Mono.Linker.ParameterHelpers"/>.
/// </summary>
///
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment describing that you're using enums as a way to separate source/IL indexes in the C# type system (took me a while to figure that out), and that they're actually used as numbers (relying on the fact that enum is really just an Int32).
Would also be nice to describe the numbering scheme here (IL parameter indexes are >=0 and for "instance" call the 0 is this and others are +1 .... and then something similar for source indexes).

{
/// <summary>
/// Used to indicate the index of the parameter in source code (i.e. the same regardless of if there is a `this` or not)
/// </summary>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as for ILParameterIndex

/// <summary>
/// Used to indicate the index of the parameter in source code (i.e. the same regardless of if there is a `this` or not)
/// </summary>
public enum SourceParameterIndex
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find the naming a bit weird - it's not exactly clear what "source" means here. That said - I didn't come up with a better name really. So I guess we should just add a good comment (maybe add couple of examples).

Copy link
Member

@sbomer sbomer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it, thank you!


bool isByRef;

// Thank you Cecil, Operand being a ParameterDefinition instead of an integer,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment can move to ILParameterIndexFromInstruction 🤣

paramNum = paramDefinition.Index;
}

// This is semantically wrong if it returns true - we would representing a reference parameter as a reference to a parameter - but it should be fine for now
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is still true right? Please keep it in the new code below.

@jtschuster jtschuster marked this pull request as draft August 24, 2022 17:19
@jtschuster
Copy link
Member Author

jtschuster commented Aug 24, 2022

Sorry for the force push, I ran into issues with rebasing and it seemed like the cleanest option. I've updated the description to describe the updated code. This version differentiates SourceParameterIndex and this parameters to ensure the SourceParameterIndex will always be able to index MethodReference.Parameters

@jtschuster jtschuster marked this pull request as ready for review August 24, 2022 17:51
Copy link
Contributor

@tlakollo tlakollo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only nit stuff from my review

Comment on lines 71 to 77
public static ILParameterIndex ILParameterIndexFromSourceParameterIndex (MethodReference method, SourceParameterIndex sourceIndex)
{
if (method.HasImplicitThis ())
return (ILParameterIndex) ((int)sourceIndex + 1);

return (ILParameterIndex) (int)sourceIndex;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: maybe we can simplify it to

Suggested change
public static ILParameterIndex ILParameterIndexFromSourceParameterIndex (MethodReference method, SourceParameterIndex sourceIndex)
{
if (method.HasImplicitThis ())
return (ILParameterIndex) ((int)sourceIndex + 1);
return (ILParameterIndex) (int)sourceIndex;
}
public static ILParameterIndex ILParameterIndexFromSourceParameterIndex (MethodReference method, SourceParameterIndex sourceIndex) =>
method.HasImplicitThis () ? (ILParameterIndex) ((int)sourceIndex + 1) : (ILParameterIndex) (int)sourceIndex;

Comment on lines 7 to 9
/// Represents the index of arguments passed to a function in IL (i.e. (ILParameterIndexs)0 represents `this` for non-static methods.
/// This is used to enforce a differentiatiation between scenarios where the 0 index should be `this` and when the 0 index should be the first non-this parameter in the type system.
/// There are no named enum values, the underlying integer value represents the index value.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Represents the index of arguments passed to a function in IL (i.e. (ILParameterIndexs)0 represents `this` for non-static methods.
/// This is used to enforce a differentiatiation between scenarios where the 0 index should be `this` and when the 0 index should be the first non-this parameter in the type system.
/// There are no named enum values, the underlying integer value represents the index value.
/// Represents the index of arguments passed to a function in IL (i.e. (ILParameterIndex)0 represents `this` for non-static methods.
/// This is used to enforce a differentiation between scenarios where the 0 index should be `this` and when the 0 index should be the first non-this parameter in the type system.
/// There are no named enum values, the underlying integer value represents the index value.

Comment on lines +3 to +4
P:Mono.Collections.Generic.Collection`1{Mono.Cecil.ParameterDefinition}.Item(System.Int32); use x
P:Mono.Cecil.ParameterDefinitionCollection.Item(System.Int32); use x
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this BannedSymbols file?

Copy link
Member Author

@jtschuster jtschuster Aug 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I was looking into using the BannedApiAnalyzer to prevent indexing MethodReference.Parameters directly with an int, but I couldn't get it to work. These changes shouldn't be there.

Comment on lines 8 to 9
/// This enum and <see cref="T:Mono.Linker.ILParameterIndex"/> is used to enforce a differentiatiation between scenarios where the 0 index should be `this` and when the 0 index should be the first non-this parameter in the type system.
/// `this` is the only named enum value. For all others, the underlying integer value represents the index value.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// This enum and <see cref="T:Mono.Linker.ILParameterIndex"/> is used to enforce a differentiatiation between scenarios where the 0 index should be `this` and when the 0 index should be the first non-this parameter in the type system.
/// `this` is the only named enum value. For all others, the underlying integer value represents the index value.
/// This enum and <see cref="T:Mono.Linker.ILParameterIndex"/> is used to enforce a differentiation between scenarios where the 0 index should be `this` and when the 0 index should be the first non-this parameter in the type system.
/// `this` is the only named enum value. For all others, the underlying integer value represents the index value.

/// <example>
/// 0 is the first argument passed to the function in C#,
/// 1 is the next, and so on. These can be used to index parameters.
/// -1 is a flag to indicate the `this` parameter, and when SourceParamater
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this comment ends abruptly in my view

Suggested change
/// -1 is a flag to indicate the `this` parameter, and when SourceParamater
/// -1 is a flag to indicate the `this` parameter, and when SourceParameter

@jtschuster jtschuster merged commit 45e2e59 into dotnet:main Aug 29, 2022
jtschuster added a commit to jtschuster/linker that referenced this pull request Sep 8, 2022
agocke pushed a commit to dotnet/runtime that referenced this pull request Nov 16, 2022
This change adds ILParameterIndex to represent the IL-based indexing where 0 may refer to this, and SourceParameterIndex where 0 is the first "real" parameter in C# source code. Converting from ILParameterIndex to SourceParameterIndex returns a SourceParameterKind to differentiate between thisand other parameters and ensure that anySourceParameterIndexwill be able to indexMethodReference.Parameters`.

It uses these in ParameterReferenceValue as well as GetMethodParameterValue() and adds a GetMethodThisParameterValue(), ThisParameterReferenceValue, and bubbles the changes where necessary.

Most places should use SourceParameterIndex, and convert to ILParameterIndex when necessary or at the boundary of updated and old code.

Commit migrated from dotnet/linker@45e2e59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants