diff options
author | Jackson Schuster <36744439+jtschuster@users.noreply.github.com> | 2022-08-29 23:13:24 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-29 23:13:24 +0300 |
commit | 45e2e5944abc04763263cc1c4b3f9ed86659ae43 (patch) | |
tree | 7b39256652ef565cd71f3704b40e8b831e18c0f8 | |
parent | d0462c464774db99f0fb5d8239c81dd39fb97d37 (diff) |
Use enum for method parameter indexing (#2993)
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.
-rw-r--r-- | src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs | 8 | ||||
-rw-r--r-- | src/ILLink.Shared/ILLink.Shared.projitems | 2 | ||||
-rw-r--r-- | src/ILLink.Shared/SourceParameterIndex.cs | 19 | ||||
-rw-r--r-- | src/ILLink.Shared/TrimAnalysis/FlowAnnotations.cs | 4 | ||||
-rw-r--r-- | src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs | 6 | ||||
-rw-r--r-- | src/linker/BannedSymbols.txt | 2 | ||||
-rw-r--r-- | src/linker/Linker.Dataflow/AttributeDataFlow.cs | 3 | ||||
-rw-r--r-- | src/linker/Linker.Dataflow/FlowAnnotations.cs | 26 | ||||
-rw-r--r-- | src/linker/Linker.Dataflow/MethodBodyScanner.cs | 103 | ||||
-rw-r--r-- | src/linker/Linker.Dataflow/ParameterReferenceValue.cs | 6 | ||||
-rw-r--r-- | src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs | 16 | ||||
-rw-r--r-- | src/linker/Linker.Dataflow/ThisParameterReferenceValue.cs | 16 | ||||
-rw-r--r-- | src/linker/Linker/ILParameterIndex.cs | 22 | ||||
-rw-r--r-- | src/linker/Linker/MethodReferenceExtensions.cs | 15 | ||||
-rw-r--r-- | src/linker/Linker/ParameterHelpers.cs | 88 | ||||
-rw-r--r-- | src/linker/Linker/TypeMapInfo.cs | 4 |
16 files changed, 247 insertions, 93 deletions
diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs index 533ef6a83..d3146c157 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs @@ -94,12 +94,12 @@ namespace ILLink.Shared.TrimAnalysis internal partial MethodThisParameterValue GetMethodThisParameterValue (MethodProxy method) => GetMethodThisParameterValue (method, method.Method.GetDynamicallyAccessedMemberTypes ()); - internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - => new MethodParameterValue (method.Method.Parameters[parameterIndex], dynamicallyAccessedMemberTypes); + internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, SourceParameterIndex parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => new MethodParameterValue (method.Method.Parameters[(int) parameterIndex], dynamicallyAccessedMemberTypes); - internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, int parameterIndex) + internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, SourceParameterIndex parameterIndex) { - var annotation = GetMethodParameterAnnotation (method.Method.Parameters[parameterIndex]); + var annotation = GetMethodParameterAnnotation (method.Method.Parameters[(int) parameterIndex]); return GetMethodParameterValue (method, parameterIndex, annotation); } #pragma warning restore CA1822 diff --git a/src/ILLink.Shared/ILLink.Shared.projitems b/src/ILLink.Shared/ILLink.Shared.projitems index 8b16e3778..66dd85465 100644 --- a/src/ILLink.Shared/ILLink.Shared.projitems +++ b/src/ILLink.Shared/ILLink.Shared.projitems @@ -23,4 +23,4 @@ <SubType>Designer</SubType> </None> </ItemGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/src/ILLink.Shared/SourceParameterIndex.cs b/src/ILLink.Shared/SourceParameterIndex.cs new file mode 100644 index 000000000..ecca20ac6 --- /dev/null +++ b/src/ILLink.Shared/SourceParameterIndex.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace ILLink.Shared +{ + /// <summary> + /// Used to indicate the index of the parameter in source code (i.e. the index is not offset by 1 if there is a `this` parameter) + /// 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. + /// There is no way to represent a `this` parameter with a SourceParameterIndex + /// </summary> + /// <example> + /// In a call to a non-static function Foo(int a, int b, int c) + /// 0 refers to a, + /// 1 refers to b, + /// 2 refers to c. + /// </example> + public enum SourceParameterIndex + { } +} diff --git a/src/ILLink.Shared/TrimAnalysis/FlowAnnotations.cs b/src/ILLink.Shared/TrimAnalysis/FlowAnnotations.cs index 7d3912e97..40688f448 100644 --- a/src/ILLink.Shared/TrimAnalysis/FlowAnnotations.cs +++ b/src/ILLink.Shared/TrimAnalysis/FlowAnnotations.cs @@ -24,8 +24,8 @@ namespace ILLink.Shared.TrimAnalysis internal partial MethodThisParameterValue GetMethodThisParameterValue (MethodProxy method); - internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes); + internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, SourceParameterIndex parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes); - internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, int parameterIndex); + internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, SourceParameterIndex parameterIndex); } }
\ No newline at end of file diff --git a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index dd4334808..934cbd002 100644 --- a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -631,7 +631,7 @@ namespace ILLink.Shared.TrimAnalysis // In all other cases we may not even know which type this is about, so there's nothing we can do // report it as a warning. _diagnosticContext.AddDiagnostic (DiagnosticId.PropertyAccessorParameterInLinqExpressionsCannotBeStaticallyDetermined, - _annotations.GetMethodParameterValue (calledMethod, 1, DynamicallyAccessedMemberTypes.None).GetDiagnosticArgumentsForAnnotationMismatch ().ToArray ()); + _annotations.GetMethodParameterValue (calledMethod, (SourceParameterIndex) 1, DynamicallyAccessedMemberTypes.None).GetDiagnosticArgumentsForAnnotationMismatch ().ToArray ()); } } break; @@ -652,7 +652,7 @@ namespace ILLink.Shared.TrimAnalysis break; } - var targetValue = _annotations.GetMethodParameterValue (calledMethod, 1, memberTypes); + var targetValue = _annotations.GetMethodParameterValue (calledMethod, (SourceParameterIndex) 1, memberTypes); foreach (var value in argumentValues[1]) { if (value is SystemTypeValue systemTypeValue) { foreach (var stringParam in argumentValues[2]) { @@ -1147,7 +1147,7 @@ namespace ILLink.Shared.TrimAnalysis for (int argumentIndex = 0; argumentIndex < argumentValues.Count; argumentIndex++) { if (argumentIndex >= calledMethod.GetParametersCount () || calledMethod.ParameterReferenceKind (argumentIndex) == ReferenceKind.Out) continue; - _requireDynamicallyAccessedMembersAction.Invoke (argumentValues[argumentIndex], _annotations.GetMethodParameterValue (calledMethod, argumentIndex)); + _requireDynamicallyAccessedMembersAction.Invoke (argumentValues[argumentIndex], _annotations.GetMethodParameterValue (calledMethod, (SourceParameterIndex) argumentIndex)); } } break; diff --git a/src/linker/BannedSymbols.txt b/src/linker/BannedSymbols.txt index c39f451ee..6ef58b3b0 100644 --- a/src/linker/BannedSymbols.txt +++ b/src/linker/BannedSymbols.txt @@ -1,2 +1,4 @@ T:Mono.Cecil.Cil.ILProcessor;Use LinkerILProcessor instead M:Mono.Cecil.TypeReference.Resolve();Use LinkContext.Resolve and LinkContext.TryResolve helpers instead +P:Mono.Collections.Generic.Collection`1{Mono.Cecil.ParameterDefinition}.Item(System.Int32); use x +P:Mono.Cecil.ParameterDefinitionCollection.Item(System.Int32); use x diff --git a/src/linker/Linker.Dataflow/AttributeDataFlow.cs b/src/linker/Linker.Dataflow/AttributeDataFlow.cs index 3ed11d392..5dccfe2b1 100644 --- a/src/linker/Linker.Dataflow/AttributeDataFlow.cs +++ b/src/linker/Linker.Dataflow/AttributeDataFlow.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using ILLink.Shared; using ILLink.Shared.TrimAnalysis; using Mono.Cecil; using Mono.Linker.Steps; @@ -27,7 +28,7 @@ namespace Mono.Linker.Dataflow public void ProcessAttributeDataflow (MethodDefinition method, IList<CustomAttributeArgument> arguments) { for (int i = 0; i < method.Parameters.Count; i++) { - var parameterValue = _context.Annotations.FlowAnnotations.GetMethodParameterValue (method, i); + var parameterValue = _context.Annotations.FlowAnnotations.GetMethodParameterValue (method, (SourceParameterIndex)i); if (parameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None) { MultiValue value = GetValueForCustomAttributeArgument (arguments[i]); var diagnosticContext = new DiagnosticContext (_origin, diagnosticsEnabled: true, _context); diff --git a/src/linker/Linker.Dataflow/FlowAnnotations.cs b/src/linker/Linker.Dataflow/FlowAnnotations.cs index 472f8567f..e7ed760df 100644 --- a/src/linker/Linker.Dataflow/FlowAnnotations.cs +++ b/src/linker/Linker.Dataflow/FlowAnnotations.cs @@ -45,11 +45,23 @@ namespace ILLink.Shared.TrimAnalysis /// </summary> /// <param name="parameterIndex">Parameter index in the IL sense. Parameter 0 on instance methods is `this`.</param> /// <returns></returns> - public DynamicallyAccessedMemberTypes GetParameterAnnotation (MethodDefinition method, int parameterIndex) + public DynamicallyAccessedMemberTypes GetParameterAnnotation (MethodDefinition method, SourceParameterIndex parameterIndex) { if (GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation) && annotation.ParameterAnnotations != null) - return annotation.ParameterAnnotations[parameterIndex]; + return annotation.ParameterAnnotations[(int) ParameterHelpers.GetILParameterIndex (method, parameterIndex)]; + + return DynamicallyAccessedMemberTypes.None; + } + + public DynamicallyAccessedMemberTypes GetThisParameterAnnotation (MethodDefinition method) + { + if (!method.HasThis) + throw new InvalidOperationException ($"Cannot get annotation of `this` of method {method.FullName} without `this`"); + + if (GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation) && + annotation.ParameterAnnotations != null) + return annotation.ParameterAnnotations[0]; return DynamicallyAccessedMemberTypes.None; } @@ -704,13 +716,13 @@ namespace ILLink.Shared.TrimAnalysis #pragma warning restore CA1822 internal partial MethodThisParameterValue GetMethodThisParameterValue (MethodProxy method) - => GetMethodThisParameterValue (method, GetParameterAnnotation (method.Method, 0)); + => GetMethodThisParameterValue (method, GetThisParameterAnnotation (method.Method)); - internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - => new (method.Method.Parameters[parameterIndex].ParameterType.ResolveToTypeDefinition (_context), method.Method, parameterIndex, dynamicallyAccessedMemberTypes); + internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, SourceParameterIndex parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => new (method.Method.Parameters[(int) parameterIndex].ParameterType.ResolveToTypeDefinition (_context), method.Method, (int) parameterIndex, dynamicallyAccessedMemberTypes); - internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, int parameterIndex) - => GetMethodParameterValue (method, parameterIndex, GetParameterAnnotation (method.Method, parameterIndex + (method.IsStatic () ? 0 : 1))); + internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, SourceParameterIndex parameterIndex) + => GetMethodParameterValue (method, parameterIndex, GetParameterAnnotation (method.Method, parameterIndex)); // Linker-specific dataflow value creation. Eventually more of these should be shared. internal SingleValue GetFieldValue (FieldDefinition field) diff --git a/src/linker/Linker.Dataflow/MethodBodyScanner.cs b/src/linker/Linker.Dataflow/MethodBodyScanner.cs index 2e3f09d6c..179ac10bb 100644 --- a/src/linker/Linker.Dataflow/MethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/MethodBodyScanner.cs @@ -16,6 +16,7 @@ using LocalVariableStore = System.Collections.Generic.Dictionary< Mono.Cecil.Cil.VariableDefinition, Mono.Linker.Dataflow.ValueBasicBlockPair>; using MultiValue = ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>; +using static Mono.Linker.ParameterHelpers; namespace Mono.Linker.Dataflow { @@ -402,7 +403,7 @@ namespace Mono.Linker.Dataflow case Code.Ldarg_S: case Code.Ldarga: case Code.Ldarga_S: - ScanLdarg (operation, currentStack, thisMethod, methodBody); + ScanLdarg (operation, currentStack, thisMethod); break; case Code.Ldloc: @@ -717,53 +718,35 @@ namespace Mono.Linker.Dataflow } } - protected abstract SingleValue GetMethodParameterValue (MethodDefinition method, int parameterIndex); + protected abstract SingleValue GetMethodParameterValue (MethodDefinition method, SourceParameterIndex parameterIndex); - private void ScanLdarg (Instruction operation, Stack<StackSlot> currentStack, MethodDefinition thisMethod, MethodBody methodBody) + protected abstract SingleValue GetMethodThisParameterValue (MethodDefinition method); + + private void ScanLdarg (Instruction operation, Stack<StackSlot> currentStack, MethodDefinition thisMethod) { Code code = operation.OpCode.Code; + bool isByRef = code == Code.Ldarga || code == Code.Ldarga_S; - bool isByRef; - - // Thank you Cecil, Operand being a ParameterDefinition instead of an integer, - // (except for Ldarg_0 - Ldarg_3, where it's null) makes all of this really convenient... - // NOT. - int paramNum; - if (code >= Code.Ldarg_0 && - code <= Code.Ldarg_3) { - paramNum = code - Code.Ldarg_0; - - if (thisMethod.HasImplicitThis ()) { - if (paramNum == 0) { - isByRef = thisMethod.DeclaringType.IsValueType; - } else { - isByRef = thisMethod.Parameters[paramNum - 1].ParameterType.IsByRefOrPointer (); - } - } else { - isByRef = thisMethod.Parameters[paramNum].ParameterType.IsByRefOrPointer (); - } - } else { - var paramDefinition = (ParameterDefinition) operation.Operand; - if (thisMethod.HasImplicitThis ()) { - if (paramDefinition == methodBody.ThisParameter) { - paramNum = 0; - } else { - paramNum = paramDefinition.Index + 1; - } - } else { - paramNum = paramDefinition.Index; - } - + SingleValue valueToPush; + switch (GetSourceParameterIndex (thisMethod, operation, out var sourceIndex)) { + case SourceParameterKind.Numbered: // 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 - isByRef = paramDefinition.ParameterType.IsByRefOrPointer (); + isByRef |= thisMethod.GetParameterType (sourceIndex).IsByRefOrPointer (); + valueToPush = isByRef + ? new ParameterReferenceValue (thisMethod, sourceIndex) + : GetMethodParameterValue (thisMethod, sourceIndex); + break; + case SourceParameterKind.This: + isByRef |= thisMethod.DeclaringType.IsValueType; + valueToPush = isByRef + ? new ThisParameterReferenceValue (thisMethod) + : GetMethodThisParameterValue (thisMethod); + break; + default: + throw new InvalidOperationException ("Unexpected IParameterIndex type"); } - isByRef |= code == Code.Ldarga || code == Code.Ldarga_S; - - StackSlot slot = new StackSlot ( - isByRef - ? new ParameterReferenceValue (thisMethod, paramNum) - : GetMethodParameterValue (thisMethod, paramNum)); + StackSlot slot = new StackSlot (valueToPush); currentStack.Push (slot); } @@ -773,13 +756,19 @@ namespace Mono.Linker.Dataflow MethodDefinition thisMethod, MethodBody methodBody) { - ParameterDefinition param = (ParameterDefinition) operation.Operand; var valueToStore = PopUnknown (currentStack, 1, methodBody, operation.Offset); - var targetValue = GetMethodParameterValue (thisMethod, param.Sequence); - if (targetValue is MethodParameterValue targetParameterValue) - HandleStoreParameter (thisMethod, targetParameterValue, operation, valueToStore.Value); - + switch (GetSourceParameterIndex (thisMethod, operation, out var sourceParameterIndex)) { + case SourceParameterKind.Numbered: + var targetValue = GetMethodParameterValue (thisMethod, sourceParameterIndex); + if (targetValue is MethodParameterValue targetParameterValue) + HandleStoreParameter (thisMethod, targetParameterValue, operation, valueToStore.Value); + break; // If the targetValue is MethodThisValue do nothing - it should never happen really, and if it does, there's nothing we can track there + case SourceParameterKind.This: + break; + default: + break; + } } private void ScanLdloc ( @@ -899,8 +888,8 @@ namespace Mono.Linker.Dataflow when GetMethodParameterValue (parameterReference.MethodDefinition, parameterReference.ParameterIndex) is MethodParameterValue parameterValue: HandleStoreParameter (method, parameterValue, operation, source); break; - case ParameterReferenceValue parameterReference - when GetMethodParameterValue (parameterReference.MethodDefinition, parameterReference.ParameterIndex) is MethodThisParameterValue thisParameterValue: + case ThisParameterReferenceValue parameterReference + when GetMethodThisParameterValue (parameterReference.MethodDefinition) is MethodThisParameterValue thisParameterValue: HandleStoreMethodThisParameter (method, thisParameterValue, operation, source); break; case MethodReturnValue methodReturnValue: @@ -1069,6 +1058,11 @@ namespace Mono.Linker.Dataflow else dereferencedValue = MultiValue.Meet (dereferencedValue, UnknownValue.Instance); break; + case ThisParameterReferenceValue thisParameterReferenceValue: + dereferencedValue = MultiValue.Meet ( + dereferencedValue, + GetMethodThisParameterValue (thisParameterReferenceValue.MethodDefinition)); + break; case ReferenceValue referenceValue: throw new NotImplementedException ($"Unhandled dereference of ReferenceValue of type {referenceValue.GetType ().FullName}"); // Incomplete handling for ref values @@ -1097,15 +1091,16 @@ namespace Mono.Linker.Dataflow { MethodDefinition? calledMethodDefinition = _context.Resolve (calledMethod); bool methodIsResolved = calledMethodDefinition is not null; - int offset = calledMethod.HasImplicitThis () ? 1 : 0; - int parameterIndex = 0; - for (int ilArgumentIndex = offset; ilArgumentIndex < methodArguments.Count; ilArgumentIndex++, parameterIndex++) { - if (calledMethod.ParameterReferenceKind (ilArgumentIndex) is not (ReferenceKind.Ref or ReferenceKind.Out)) + ILParameterIndex ilArgumentIndex; + for (SourceParameterIndex parameterIndex = 0; (int) parameterIndex < calledMethod.Parameters.Count; parameterIndex++) { + ilArgumentIndex = GetILParameterIndex (calledMethod, parameterIndex); + + if (calledMethod.ParameterReferenceKind ((int) ilArgumentIndex) is not (ReferenceKind.Ref or ReferenceKind.Out)) continue; - SingleValue newByRefValue = methodIsResolved && parameterIndex < calledMethodDefinition!.Parameters.Count + SingleValue newByRefValue = methodIsResolved && (int)parameterIndex < calledMethodDefinition!.Parameters.Count ? _context.Annotations.FlowAnnotations.GetMethodParameterValue (calledMethodDefinition!, parameterIndex) : UnknownValue.Instance; - StoreInReference (methodArguments[ilArgumentIndex], newByRefValue, callingMethodBody.Method, operation, locals, curBasicBlock, ref ipState); + StoreInReference (methodArguments[(int) ilArgumentIndex], newByRefValue, callingMethodBody.Method, operation, locals, curBasicBlock, ref ipState); } } diff --git a/src/linker/Linker.Dataflow/ParameterReferenceValue.cs b/src/linker/Linker.Dataflow/ParameterReferenceValue.cs index 79bdfdeab..853b31664 100644 --- a/src/linker/Linker.Dataflow/ParameterReferenceValue.cs +++ b/src/linker/Linker.Dataflow/ParameterReferenceValue.cs @@ -2,13 +2,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using ILLink.Shared.DataFlow; using Mono.Cecil; -using Mono.Linker; namespace ILLink.Shared.TrimAnalysis { - public partial record ParameterReferenceValue (MethodDefinition MethodDefinition, int ParameterIndex) - : ReferenceValue (MethodDefinition.HasImplicitThis () && ParameterIndex == 0 ? MethodDefinition.DeclaringType - : MethodDefinition.Parameters[MethodDefinition.HasImplicitThis () ? --ParameterIndex : ParameterIndex].ParameterType) + public partial record ParameterReferenceValue (MethodDefinition MethodDefinition, SourceParameterIndex ParameterIndex) + : ReferenceValue (MethodDefinition.Parameters[(int) ParameterIndex].ParameterType) { public override SingleValue DeepCopy () { diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index 56d554989..adac45faf 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -93,18 +93,14 @@ namespace Mono.Linker.Dataflow Debug.Fail ("Invalid IL or a bug in the scanner"); } - protected override ValueWithDynamicallyAccessedMembers GetMethodParameterValue (MethodDefinition method, int parameterIndex) - => GetMethodParameterValue (method, parameterIndex, _context.Annotations.FlowAnnotations.GetParameterAnnotation (method, parameterIndex)); + protected override ValueWithDynamicallyAccessedMembers GetMethodThisParameterValue (MethodDefinition method) + => _annotations.GetMethodThisParameterValue (method); - ValueWithDynamicallyAccessedMembers GetMethodParameterValue (MethodDefinition method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - { - if (method.HasImplicitThis ()) { - if (parameterIndex == 0) - return _annotations.GetMethodThisParameterValue (method, dynamicallyAccessedMemberTypes); - - parameterIndex--; - } + protected override ValueWithDynamicallyAccessedMembers GetMethodParameterValue (MethodDefinition method, SourceParameterIndex parameterIndex) + => GetMethodParameterValue (method, parameterIndex, _annotations.GetParameterAnnotation (method, parameterIndex)); + ValueWithDynamicallyAccessedMembers GetMethodParameterValue (MethodDefinition method, SourceParameterIndex parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { return _annotations.GetMethodParameterValue (method, parameterIndex, dynamicallyAccessedMemberTypes); } diff --git a/src/linker/Linker.Dataflow/ThisParameterReferenceValue.cs b/src/linker/Linker.Dataflow/ThisParameterReferenceValue.cs new file mode 100644 index 000000000..727181fbd --- /dev/null +++ b/src/linker/Linker.Dataflow/ThisParameterReferenceValue.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using ILLink.Shared.DataFlow; +using Mono.Cecil; + +namespace ILLink.Shared.TrimAnalysis +{ + public partial record ThisParameterReferenceValue (MethodDefinition MethodDefinition) + : ReferenceValue (MethodDefinition.DeclaringType) + { + public override SingleValue DeepCopy () + { + return this; + } + } +} diff --git a/src/linker/Linker/ILParameterIndex.cs b/src/linker/Linker/ILParameterIndex.cs new file mode 100644 index 000000000..3d9da48f4 --- /dev/null +++ b/src/linker/Linker/ILParameterIndex.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Mono.Linker +{ + /// <summary> + /// 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. + /// Generally prefer to use <see cref="ILLink.Shared.SourceParameterIndex"/> when possible. + /// See also <seealso cref="Mono.Linker.ParameterHelpers"/>. + /// </summary> + /// <example> + /// In a call to a non-static function Foo(int a, int b, int c) + /// 0 refers to `this`, + /// 1 refers to a, + /// 2 refers to b. + /// 3 referes to c. + /// </example> + public enum ILParameterIndex + { } +} diff --git a/src/linker/Linker/MethodReferenceExtensions.cs b/src/linker/Linker/MethodReferenceExtensions.cs index e38371399..3bbe6816a 100644 --- a/src/linker/Linker/MethodReferenceExtensions.cs +++ b/src/linker/Linker/MethodReferenceExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using ILLink.Shared; using ILLink.Shared.TypeSystemProxy; using Mono.Cecil; @@ -80,14 +81,18 @@ namespace Mono.Linker return method.ReturnType.WithoutModifiers ().MetadataType == MetadataType.Void; } - public static TypeReference? GetParameterType (this MethodReference method, int parameterIndex, LinkContext context) + public static TypeReference? GetInflatedParameterType (this MethodReference method, int index, LinkContext context) { - if (method.DeclaringType is GenericInstanceType genericInstance) - return TypeReferenceExtensions.InflateGenericType (genericInstance, method.Parameters[parameterIndex].ParameterType, context); - - return method.Parameters[parameterIndex].ParameterType; + var uninflatedParameterType = method.GetParameterType ((SourceParameterIndex) index); + if (method.DeclaringType is GenericInstanceType genericInstance) { + return TypeReferenceExtensions.InflateGenericType (genericInstance, uninflatedParameterType, context); + } + return uninflatedParameterType; } + public static TypeReference GetParameterType (this MethodReference method, SourceParameterIndex index) + => method.Parameters[(int) index].ParameterType; + public static bool IsDeclaredOnType (this MethodReference method, string fullTypeName) { return method.DeclaringType.IsTypeOf (fullTypeName); diff --git a/src/linker/Linker/ParameterHelpers.cs b/src/linker/Linker/ParameterHelpers.cs new file mode 100644 index 000000000..2ea4786b1 --- /dev/null +++ b/src/linker/Linker/ParameterHelpers.cs @@ -0,0 +1,88 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using Mono.Cecil; +using Mono.Cecil.Cil; +using ILLink.Shared; + +namespace Mono.Linker +{ + public static class ParameterHelpers + { + public static ILParameterIndex GetILParameterIndex (MethodDefinition thisMethod, Instruction operation) + { + // Thank you Cecil, Operand being a ParameterDefinition instead of an integer, + // (except for Ldarg_0 - Ldarg_3, where it's null) makes all of this really convenient... + // NOT. + Code code = operation.OpCode.Code; + return code switch { + Code.Ldarg_0 + or Code.Ldarg_1 + or Code.Ldarg_2 + or Code.Ldarg_3 + => GetLdargParamIndex (), + + Code.Starg + or Code.Ldarg + or Code.Starg_S + or Code.Ldarg_S + or Code.Ldarga + or Code.Ldarga_S + => GetParamSequence (), + + _ => throw new ArgumentException ($"{nameof (ILParameterIndex)} expected an ldarg or starg instruction, got {operation.OpCode.Name}") + }; + + ILParameterIndex GetLdargParamIndex () + { + return (ILParameterIndex) (code - Code.Ldarg_0); + } + ILParameterIndex GetParamSequence () + { + ParameterDefinition param = (ParameterDefinition) operation.Operand; + return (ILParameterIndex) param.Sequence; + } + } + + /// <Summary> + /// This enum is used when converting from an ILParameterIndex to a SourceParamterIndex to + /// differentiate `This` parameters from other parameters. + /// </Summary> + public enum SourceParameterKind + { + This, + Numbered + } + + /// <summary> + /// Used to get the SourceParameterIndex that an instruction refers to. + /// If the return value is <see cref="SourceParameterKind.Numbered" />, the instruction refers to a numbered non-this parameter and <paramref name="sourceParameterIndex"/> will have a valid value. + /// If the return value is <see cref="SourceParameterKind.This" />, the instruction refers to the `this` parameter, and <paramref name="sourceParameterIndex"/> will not have a valid value. + /// </summary> + public static SourceParameterKind GetSourceParameterIndex (MethodDefinition method, Instruction operation, out SourceParameterIndex sourceParameterIndex) + => GetSourceParameterIndex (method, GetILParameterIndex (method, operation), out sourceParameterIndex); + + /// <summary> + /// Used to get the SourceParameterIndex that an ILParameterIndex refers to. + /// If the return value is <see cref="SourceParameterKind.Numbered" />, the instruction refers to a numbered non-this parameter and <paramref name="sourceParameterIndex"/> will have a valid value. + /// If the return value is <see cref="SourceParameterKind.This" />, the instruction refers to the `this` parameter, and <paramref name="sourceParameterIndex"/> will not have a valid value. + /// </summary> + public static SourceParameterKind GetSourceParameterIndex (MethodReference method, ILParameterIndex ilIndex, out SourceParameterIndex sourceParameterIndex) + { + sourceParameterIndex = (SourceParameterIndex) (int) ilIndex; + if (method.HasImplicitThis ()) { + if (ilIndex == 0) { + return SourceParameterKind.This; + } + sourceParameterIndex--; + } + return SourceParameterKind.Numbered; + } + + public static ILParameterIndex GetILParameterIndex (MethodReference method, SourceParameterIndex sourceIndex) + => method.HasImplicitThis () + ? (ILParameterIndex) (sourceIndex + 1) + : (ILParameterIndex) sourceIndex; + } +} diff --git a/src/linker/Linker/TypeMapInfo.cs b/src/linker/Linker/TypeMapInfo.cs index 117df8fad..75613e2c0 100644 --- a/src/linker/Linker/TypeMapInfo.cs +++ b/src/linker/Linker/TypeMapInfo.cs @@ -347,8 +347,8 @@ namespace Mono.Linker return false; for (int i = 0; i < cp.Count; i++) { - if (candidate.GetParameterType (i, context) is not TypeReference candidateParameterType || - method.GetParameterType (i, context) is not TypeReference methodParameterType || + if (candidate.GetInflatedParameterType (i, context) is not TypeReference candidateParameterType || + method.GetInflatedParameterType (i, context) is not TypeReference methodParameterType || !TypeMatch (candidateParameterType, methodParameterType)) return false; } |