diff options
author | Jackson Schuster <36744439+jtschuster@users.noreply.github.com> | 2022-10-27 20:50:07 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-27 20:50:07 +0300 |
commit | ca3999834a61bf71bb4b1c70875482492d14018e (patch) | |
tree | df471876947945de129e170e1102e81b28aae628 | |
parent | 4db6ac94ffa8bf6dae8ad9c5c68f94b58de917fd (diff) |
Create `ParameterProxy` to wrap logic surrounding parameters and use one `ParameterIndex` struct to index (#3059)
This reverts commit 45e2e5944abc04763263cc1c4b3f9ed86659ae43.
This commit creates a 'ParameterProxy' type and 'ParameterIndex' to represent parameters in a consistent way. This is motivated by using 'int' to represent different ways of counting parameters (sometimes offset by 1 for the 'this' parameter, sometimes not offset). ParameterIndex should now be the only type used to index into a method's parameters. ParameterIndex represents the index of the parameter as it is passed to the method in IL (i.e. 'this' is the 0 index in instance methods). ParameterProxy wraps the common convention of a MethodProxy and int to represent a parameter, and now holds a Method and a ParameterIndex to encapsulate a Parameter.
No behavioral changes are expected from this change.
Co-authored-by: Vitek Karas <10670590+vitek-karas@users.noreply.github.com>
Co-authored-by: dotnet-maestro[bot] <42748379+dotnet-maestro[bot]@users.noreply.github.com>
57 files changed, 986 insertions, 715 deletions
diff --git a/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs b/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs index 187ac40ea..d78468022 100644 --- a/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs @@ -204,79 +204,79 @@ namespace ILLink.RoslynAnalyzer } } - static void VerifyDamOnMethodsMatch (SymbolAnalysisContext context, IMethodSymbol method, IMethodSymbol overriddenMethod) + static void VerifyDamOnMethodsMatch (SymbolAnalysisContext context, IMethodSymbol overrideMethod, IMethodSymbol baseMethod) { - var methodReturnAnnotations = FlowAnnotations.GetMethodReturnValueAnnotation (method); - var overriddenMethodReturnAnnotations = FlowAnnotations.GetMethodReturnValueAnnotation (overriddenMethod); - if (methodReturnAnnotations != overriddenMethodReturnAnnotations) { + var overrideMethodReturnAnnotation = FlowAnnotations.GetMethodReturnValueAnnotation (overrideMethod); + var baseMethodReturnAnnotation = FlowAnnotations.GetMethodReturnValueAnnotation (baseMethod); + if (overrideMethodReturnAnnotation != baseMethodReturnAnnotation) { - (IMethodSymbol attributableMethod, DynamicallyAccessedMemberTypes missingAttribute) = GetTargetAndRequirements (method, - overriddenMethod, methodReturnAnnotations, overriddenMethodReturnAnnotations); + (IMethodSymbol attributableMethod, DynamicallyAccessedMemberTypes missingAttribute) = GetTargetAndRequirements (overrideMethod, + baseMethod, overrideMethodReturnAnnotation, baseMethodReturnAnnotation); Location attributableSymbolLocation = attributableMethod.Locations[0]; // code fix does not support merging multiple attributes. If an attribute is present or the method is not in source, do not provide args for code fix. (Location[]? sourceLocation, Dictionary<string, string?>? DAMArgs) = (!attributableSymbolLocation.IsInSource - || (method.TryGetReturnAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _) - && overriddenMethod.TryGetReturnAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)) + || (overrideMethod.TryGetReturnAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _) + && baseMethod.TryGetReturnAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)) ) ? (null, null) : CreateArguments (attributableSymbolLocation, missingAttribute); context.ReportDiagnostic (Diagnostic.Create ( DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersMismatchOnMethodReturnValueBetweenOverrides), - method.Locations[0], sourceLocation, DAMArgs?.ToImmutableDictionary (), method.GetDisplayName (), overriddenMethod.GetDisplayName ())); + overrideMethod.Locations[0], sourceLocation, DAMArgs?.ToImmutableDictionary (), overrideMethod.GetDisplayName (), baseMethod.GetDisplayName ())); } - for (int i = 0; i < method.Parameters.Length; i++) { - var methodParameterAnnotation = FlowAnnotations.GetMethodParameterAnnotation (method.Parameters[i]); - var overriddenParameterAnnotation = FlowAnnotations.GetMethodParameterAnnotation (overriddenMethod.Parameters[i]); - if (methodParameterAnnotation != overriddenParameterAnnotation) { + foreach (var overrideParam in overrideMethod.GetMetadataParameters ()) { + var baseParam = baseMethod.GetParameter (overrideParam.Index); + var baseParameterAnnotation = FlowAnnotations.GetMethodParameterAnnotation (baseParam); + var overrideParameterAnnotation = FlowAnnotations.GetMethodParameterAnnotation (overrideParam); + if (overrideParameterAnnotation != baseParameterAnnotation) { + (IMethodSymbol attributableMethod, DynamicallyAccessedMemberTypes missingAttribute) = GetTargetAndRequirements (overrideMethod, + baseMethod, overrideParameterAnnotation, baseParameterAnnotation); - (IMethodSymbol attributableMethod, DynamicallyAccessedMemberTypes missingAttribute) = GetTargetAndRequirements (method, - overriddenMethod, methodParameterAnnotation, overriddenParameterAnnotation); - - Location attributableSymbolLocation = attributableMethod.Parameters[i].Locations[0]; + Location attributableSymbolLocation = attributableMethod.GetParameter (overrideParam.Index).Location!; // code fix does not support merging multiple attributes. If an attribute is present or the method is not in source, do not provide args for code fix. (Location[]? sourceLocation, Dictionary<string, string?>? DAMArgs) = (!attributableSymbolLocation.IsInSource - || (method.Parameters[i].TryGetAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _) - && overriddenMethod.Parameters[i].TryGetAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)) + || (overrideParam.ParameterSymbol!.TryGetAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _) + && baseParam.ParameterSymbol!.TryGetAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)) ) ? (null, null) : CreateArguments (attributableSymbolLocation, missingAttribute); context.ReportDiagnostic (Diagnostic.Create ( DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersMismatchOnMethodParameterBetweenOverrides), - method.Parameters[i].Locations[0], sourceLocation, DAMArgs?.ToImmutableDictionary (), - method.Parameters[i].GetDisplayName (), method.GetDisplayName (), overriddenMethod.Parameters[i].GetDisplayName (), overriddenMethod.GetDisplayName ())); + overrideParam.Location, sourceLocation, DAMArgs?.ToImmutableDictionary (), + overrideParam.GetDisplayName (), overrideMethod.GetDisplayName (), baseParam.GetDisplayName (), baseMethod.GetDisplayName ())); } } - for (int i = 0; i < method.TypeParameters.Length; i++) { - var methodTypeParameterAnnotation = method.TypeParameters[i].GetDynamicallyAccessedMemberTypes (); - var overriddenMethodTypeParameterAnnotation = overriddenMethod.TypeParameters[i].GetDynamicallyAccessedMemberTypes (); + for (int i = 0; i < overrideMethod.TypeParameters.Length; i++) { + var methodTypeParameterAnnotation = overrideMethod.TypeParameters[i].GetDynamicallyAccessedMemberTypes (); + var overriddenMethodTypeParameterAnnotation = baseMethod.TypeParameters[i].GetDynamicallyAccessedMemberTypes (); if (methodTypeParameterAnnotation != overriddenMethodTypeParameterAnnotation) { - (IMethodSymbol attributableMethod, DynamicallyAccessedMemberTypes missingAttribute) = GetTargetAndRequirements (method, overriddenMethod, methodTypeParameterAnnotation, overriddenMethodTypeParameterAnnotation); + (IMethodSymbol attributableMethod, DynamicallyAccessedMemberTypes missingAttribute) = GetTargetAndRequirements (overrideMethod, baseMethod, methodTypeParameterAnnotation, overriddenMethodTypeParameterAnnotation); Location attributableSymbolLocation = attributableMethod.TypeParameters[i].Locations[0]; // code fix does not support merging multiple attributes. If an attribute is present or the method is not in source, do not provide args for code fix. (Location[]? sourceLocation, Dictionary<string, string?>? DAMArgs) = (!attributableSymbolLocation.IsInSource - || (method.TypeParameters[i].TryGetAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _) - && overriddenMethod.TypeParameters[i].TryGetAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)) + || (overrideMethod.TypeParameters[i].TryGetAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _) + && baseMethod.TypeParameters[i].TryGetAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)) ) ? (null, null) : CreateArguments (attributableSymbolLocation, missingAttribute); context.ReportDiagnostic (Diagnostic.Create ( DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersMismatchOnGenericParameterBetweenOverrides), - method.TypeParameters[i].Locations[0], sourceLocation, DAMArgs?.ToImmutableDictionary (), - method.TypeParameters[i].GetDisplayName (), method.GetDisplayName (), - overriddenMethod.TypeParameters[i].GetDisplayName (), overriddenMethod.GetDisplayName ())); + overrideMethod.TypeParameters[i].Locations[0], sourceLocation, DAMArgs?.ToImmutableDictionary (), + overrideMethod.TypeParameters[i].GetDisplayName (), overrideMethod.GetDisplayName (), + baseMethod.TypeParameters[i].GetDisplayName (), baseMethod.GetDisplayName ())); } } - if (!method.IsStatic && method.GetDynamicallyAccessedMemberTypes () != overriddenMethod.GetDynamicallyAccessedMemberTypes ()) + if (!overrideMethod.IsStatic && overrideMethod.GetDynamicallyAccessedMemberTypes () != baseMethod.GetDynamicallyAccessedMemberTypes ()) context.ReportDiagnostic (Diagnostic.Create ( DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersMismatchOnImplicitThisBetweenOverrides), - method.Locations[0], - method.GetDisplayName (), overriddenMethod.GetDisplayName ())); + overrideMethod.Locations[0], + overrideMethod.GetDisplayName (), baseMethod.GetDisplayName ())); } static void VerifyDamOnInterfaceAndImplementationMethodsMatch (SymbolAnalysisContext context, INamedTypeSymbol type) diff --git a/src/ILLink.RoslynAnalyzer/IMethodSymbolExtensions.cs b/src/ILLink.RoslynAnalyzer/IMethodSymbolExtensions.cs new file mode 100644 index 000000000..cc61d3cda --- /dev/null +++ b/src/ILLink.RoslynAnalyzer/IMethodSymbolExtensions.cs @@ -0,0 +1,72 @@ +// 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 ILLink.Shared.TypeSystemProxy; +using Microsoft.CodeAnalysis; + +namespace ILLink.RoslynAnalyzer +{ + internal static class IMethodSymbolExtensions + { + public static bool HasImplicitThis (this IMethodSymbol method) + { + return !method.IsStatic; + } + + /// <summary> + /// Returns a list of the parameters pushed onto the stack before the method call (including the implicit 'this' parameter) + /// </summary> + public static ParameterProxyEnumerable GetParameters (this IMethodSymbol method) + { + return new ParameterProxyEnumerable (0, method.GetParametersCount (), new (method)); + } + + /// <summary> + /// Returns a list of the parameters in the method's 'parameters' metadata section (i.e. excluding the implicit 'this' parameter) + /// </summary> + public static ParameterProxyEnumerable GetMetadataParameters (this IMethodSymbol method) + { + int implicitThisOffset = method.HasImplicitThis () ? 1 : 0; + return new ParameterProxyEnumerable (implicitThisOffset, method.GetParametersCount (), new (method)); + } + + /// <summary> + /// Gets the parameter at the <see cref="ParameterIndex"/> provided. Note ParameterIndex treat the implicit 'this' as index 0. + /// Throws if the index is out of bounds. + /// </summary> + public static ParameterProxy GetParameter (this IMethodSymbol method, ParameterIndex index) + { + if (method.TryGetParameter (index) is not ParameterProxy param) + throw new InvalidOperationException ($"Cannot get parameter at index {(int) index} of method {method.GetDisplayName ()} with {method.GetParametersCount ()} parameters."); + return param; + } + + /// <summary> + /// Gets the parameter at the <see cref="ParameterIndex"/> provided. Note ParameterIndex treat the implicit 'this' as index 0. + /// Returns null if the index is out of bounds. + /// </summary> + public static ParameterProxy? TryGetParameter (this IMethodSymbol method, ParameterIndex index) + { + if (method.GetParametersCount () <= (int) index || (int) index < 0) + return null; + return new ParameterProxy (new (method), index); + } + + /// <summary> + /// Gets the number of entries in the 'Parameters' section of a method's metadata (i.e. excludes the implicit 'this' from the count) + /// </summary> + public static int GetMetadataParametersCount (this IMethodSymbol method) + { + return method.Parameters.Length; + } + + /// <summary> + /// Gets the number of parameters pushed onto the stack when the method is called (including the implicit 'this' paramter) + /// </summary> + public static int GetParametersCount (this IMethodSymbol method) + { + return method.Parameters.Length + (method.HasImplicitThis () ? 1 : 0); + } + } +}
\ No newline at end of file diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/DiagnosticContext.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/DiagnosticContext.cs index a59bd1cc0..f920a2ed6 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/DiagnosticContext.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/DiagnosticContext.cs @@ -40,9 +40,9 @@ namespace ILLink.Shared.TrimAnalysis ISymbol symbol = actualValue switch { FieldValue field => field.FieldSymbol, - MethodParameterValue mpv => mpv.ParameterSymbol, + MethodParameterValue maybeThisParameter when maybeThisParameter.Parameter.IsImplicitThis => maybeThisParameter.MethodSymbol, + MethodParameterValue methodParameter => methodParameter.Parameter.ParameterSymbol!, MethodReturnValue mrv => mrv.MethodSymbol, - MethodThisParameterValue mtpv => mtpv.MethodSymbol, GenericParameterValue gpv => gpv.GenericParameter.TypeParameterSymbol, _ => throw new InvalidOperationException () }; diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs index 207d42447..09d64065d 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs @@ -8,6 +8,7 @@ using ILLink.RoslynAnalyzer; using ILLink.Shared.TypeSystemProxy; using Microsoft.CodeAnalysis; +#nullable enable namespace ILLink.Shared.TrimAnalysis { sealed partial class FlowAnnotations @@ -23,22 +24,24 @@ namespace ILLink.Shared.TrimAnalysis public static bool RequiresDataFlowAnalysis (IMethodSymbol method) { - if (method.GetDynamicallyAccessedMemberTypes () != DynamicallyAccessedMemberTypes.None) - return true; - if (GetMethodReturnValueAnnotation (method) != DynamicallyAccessedMemberTypes.None) return true; - foreach (var parameter in method.Parameters) { - if (GetMethodParameterAnnotation (parameter) != DynamicallyAccessedMemberTypes.None) + foreach (var param in method.GetParameters ()) { + if (GetMethodParameterAnnotation (param) != DynamicallyAccessedMemberTypes.None) return true; } return false; } - public static DynamicallyAccessedMemberTypes GetMethodParameterAnnotation (IParameterSymbol parameter) + public static DynamicallyAccessedMemberTypes GetMethodParameterAnnotation (ParameterProxy param) { + IMethodSymbol method = param.Method.Method; + if (param.IsImplicitThis) + return method.GetDynamicallyAccessedMemberTypes (); + + IParameterSymbol parameter = param.ParameterSymbol!; var damt = parameter.GetDynamicallyAccessedMemberTypes (); var parameterMethod = (IMethodSymbol) parameter.ContainingSymbol; @@ -92,20 +95,38 @@ namespace ILLink.Shared.TrimAnalysis internal partial GenericParameterValue GetGenericParameterValue (GenericParameterProxy genericParameter) => new GenericParameterValue (genericParameter.TypeParameterSymbol); - internal partial MethodThisParameterValue GetMethodThisParameterValue (MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - => new MethodThisParameterValue (method.Method, dynamicallyAccessedMemberTypes); - - internal partial MethodThisParameterValue GetMethodThisParameterValue (MethodProxy method) - => GetMethodThisParameterValue (method, method.Method.GetDynamicallyAccessedMemberTypes ()); + internal partial MethodParameterValue GetMethodThisParameterValue (MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + if (!method.HasImplicitThis ()) + throw new InvalidOperationException ($"Cannot get 'this' parameter of method {method.GetDisplayName ()} with no 'this' parameter."); + return GetMethodParameterValue (new ParameterProxy (method, (ParameterIndex) 0), dynamicallyAccessedMemberTypes); + } - internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, SourceParameterIndex parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - => new MethodParameterValue (method.Method.Parameters[(int) parameterIndex], dynamicallyAccessedMemberTypes); + // overrideIsThis is needed for backwards compatibility with MakeGenericType/Method https://github.com/dotnet/linker/issues/2428 + internal MethodParameterValue GetMethodThisParameterValue (MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, bool overrideIsThis = false) + { + if (!method.HasImplicitThis () && !overrideIsThis) + throw new InvalidOperationException ($"Cannot get 'this' parameter of method {method.GetDisplayName ()} with no 'this' parameter."); + return new MethodParameterValue (new ParameterProxy (method, (ParameterIndex) 0), dynamicallyAccessedMemberTypes, overrideIsThis); + } - internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, SourceParameterIndex parameterIndex) + internal partial MethodParameterValue GetMethodThisParameterValue (MethodProxy method) { - var annotation = GetMethodParameterAnnotation (method.Method.Parameters[(int) parameterIndex]); - return GetMethodParameterValue (method, parameterIndex, annotation); + if (!method.HasImplicitThis ()) + throw new InvalidOperationException ($"Cannot get 'this' parameter of method {method.GetDisplayName ()} with no 'this' parameter."); + ParameterProxy param = new (method, (ParameterIndex) 0); + var damt = GetMethodParameterAnnotation (param); + return GetMethodParameterValue (new ParameterProxy (method, (ParameterIndex) 0), damt); } + + internal MethodParameterValue GetMethodParameterValue (MethodProxy method, ParameterIndex parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => new MethodParameterValue (new (method, parameterIndex), dynamicallyAccessedMemberTypes); + + internal partial MethodParameterValue GetMethodParameterValue (ParameterProxy param) + => new MethodParameterValue (param, GetMethodParameterAnnotation (param)); + + internal partial MethodParameterValue GetMethodParameterValue (ParameterProxy param, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => new MethodParameterValue (param, dynamicallyAccessedMemberTypes); #pragma warning restore CA1822 } } diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodParameterValue.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodParameterValue.cs index 8a41cd171..36915b04b 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodParameterValue.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodParameterValue.cs @@ -1,10 +1,8 @@ // 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.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using ILLink.RoslynAnalyzer; -using ILLink.Shared.DataFlow; +using ILLink.Shared.TypeSystemProxy; using Microsoft.CodeAnalysis; namespace ILLink.Shared.TrimAnalysis @@ -12,21 +10,22 @@ namespace ILLink.Shared.TrimAnalysis partial record MethodParameterValue { public MethodParameterValue (IParameterSymbol parameterSymbol) - : this (parameterSymbol, FlowAnnotations.GetMethodParameterAnnotation (parameterSymbol)) { } + : this (new ParameterProxy (parameterSymbol)) { } + public MethodParameterValue (IMethodSymbol methodSymbol, ParameterIndex parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + : this (new (new (methodSymbol), parameterIndex), dynamicallyAccessedMemberTypes) { } - public MethodParameterValue (IParameterSymbol parameterSymbol, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - => (ParameterSymbol, DynamicallyAccessedMemberTypes) = (parameterSymbol, dynamicallyAccessedMemberTypes); + public MethodParameterValue (ParameterProxy parameter) + : this (parameter, FlowAnnotations.GetMethodParameterAnnotation (parameter)) { } - public readonly IParameterSymbol ParameterSymbol; + public MethodParameterValue (ParameterProxy parameter, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, bool overrideIsThis = false) + { + Parameter = parameter; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + _overrideIsThis = overrideIsThis; + } public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } - public override IEnumerable<string> GetDiagnosticArgumentsForAnnotationMismatch () - => new string[] { ParameterSymbol.GetDisplayName (), ParameterSymbol.ContainingSymbol.GetDisplayName () }; - - public override SingleValue DeepCopy () => this; // This value is immutable - - public override string ToString () - => this.ValueToString (ParameterSymbol, DynamicallyAccessedMemberTypes); + public IMethodSymbol MethodSymbol => Parameter.Method.Method; } } diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodProxy.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodProxy.cs index d42daee81..0c059e997 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodProxy.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodProxy.cs @@ -19,14 +19,15 @@ namespace ILLink.Shared.TypeSystemProxy internal partial bool IsDeclaredOnType (string fullTypeName) => IsTypeOf (Method.ContainingType, fullTypeName); - internal partial bool HasParameters () => Method.Parameters.Length > 0; + internal partial bool HasMetadataParameters () => Method.Parameters.Length > 0; - internal partial int GetParametersCount () => Method.Parameters.Length; + internal partial int GetMetadataParametersCount () => Method.GetMetadataParametersCount (); - internal partial bool HasParameterOfType (int parameterIndex, string fullTypeName) - => Method.Parameters.Length > parameterIndex && IsTypeOf (Method.Parameters[parameterIndex].Type, fullTypeName); + internal partial int GetParametersCount () => Method.GetParametersCount (); - internal partial string GetParameterDisplayName (int parameterIndex) => Method.Parameters[parameterIndex].GetDisplayName (); + internal partial ParameterProxyEnumerable GetParameters () => Method.GetParameters (); + + internal partial ParameterProxy GetParameter (ParameterIndex index) => Method.GetParameter (index); internal partial bool HasGenericParameters () => Method.IsGenericMethod; @@ -47,6 +48,8 @@ namespace ILLink.Shared.TypeSystemProxy internal partial bool IsStatic () => Method.IsStatic; + internal partial bool HasImplicitThis () => Method.HasImplicitThis (); + internal partial bool ReturnsVoid () => Method.ReturnType.SpecialType == SpecialType.System_Void; static bool IsTypeOf (ITypeSymbol type, string fullTypeName) @@ -57,14 +60,6 @@ namespace ILLink.Shared.TypeSystemProxy return namedType.HasName (fullTypeName); } - public ReferenceKind ParameterReferenceKind (int index) - => Method.Parameters[index].RefKind switch { - RefKind.In => ReferenceKind.In, - RefKind.Out => ReferenceKind.Out, - RefKind.Ref => ReferenceKind.Ref, - _ => ReferenceKind.None - }; - public override string ToString () => Method.ToString (); } } diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodThisParameterValue.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodThisParameterValue.cs deleted file mode 100644 index b97dfcc9b..000000000 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodThisParameterValue.cs +++ /dev/null @@ -1,31 +0,0 @@ -// 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.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using ILLink.RoslynAnalyzer; -using ILLink.Shared.DataFlow; -using Microsoft.CodeAnalysis; - -namespace ILLink.Shared.TrimAnalysis -{ - partial record MethodThisParameterValue - { - public MethodThisParameterValue (IMethodSymbol methodSymbol) - : this (methodSymbol, methodSymbol.GetDynamicallyAccessedMemberTypes ()) { } - - public MethodThisParameterValue (IMethodSymbol methodSymbol, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - => (MethodSymbol, DynamicallyAccessedMemberTypes) = (methodSymbol, dynamicallyAccessedMemberTypes); - - public readonly IMethodSymbol MethodSymbol; - - public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } - - public override IEnumerable<string> GetDiagnosticArgumentsForAnnotationMismatch () - => new string[] { MethodSymbol.GetDisplayName () }; - - public override SingleValue DeepCopy () => this; // This value is immutable - - public override string ToString () => this.ValueToString (MethodSymbol, DynamicallyAccessedMemberTypes); - } -} diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/ParameterProxy.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/ParameterProxy.cs new file mode 100644 index 000000000..6ee65cbb9 --- /dev/null +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/ParameterProxy.cs @@ -0,0 +1,57 @@ +// 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 ILLink.RoslynAnalyzer; +using Microsoft.CodeAnalysis; + +namespace ILLink.Shared.TypeSystemProxy +{ + partial struct ParameterProxy + { + public ParameterProxy (IParameterSymbol parameter) + { + Method = (new ((IMethodSymbol) parameter.ContainingSymbol)); + Index = (ParameterIndex) parameter.Ordinal + (Method.HasImplicitThis () ? 1 : 0); + } + + public partial ReferenceKind GetReferenceKind () => + IsImplicitThis + ? ((ITypeSymbol) Method.Method.ContainingSymbol).IsValueType + ? ReferenceKind.Ref + : ReferenceKind.None + : Method.Method.Parameters[MetadataIndex].RefKind switch { + RefKind.Ref => ReferenceKind.Ref, + RefKind.In => ReferenceKind.In, + RefKind.Out => ReferenceKind.Out, + RefKind.None => ReferenceKind.None, + _ => throw new NotImplementedException ($"Unexpected RefKind found on parameter {GetDisplayName ()}") + }; + + /// <summary> + /// Returns the IParameterSymbol representing the parameter. Returns null for the implicit this paramter. + /// </summary> + public IParameterSymbol? ParameterSymbol => IsImplicitThis ? null : Method.Method.Parameters[MetadataIndex]; + + /// <summary> + /// Returns the IParameterSymbol.Location[0] for the parameter. Returns null for the implicit this paramter. + /// </summary> + public Location? Location => ParameterSymbol?.Locations[0]; + + public TypeProxy ParameterType + => IsImplicitThis + ? new TypeProxy (Method.Method.ContainingType) + : new TypeProxy (Method.Method.Parameters[MetadataIndex].Type); + + public partial string GetDisplayName () + { + if (IsImplicitThis) + return "this"; + return ParameterSymbol!.GetDisplayName (); + } + + public partial bool IsTypeOf (string typeName) => ParameterType.IsTypeOf (typeName.Substring (0, typeName.LastIndexOf ('.')), typeName.Substring (1 + typeName.LastIndexOf ('.'))); + + public bool IsTypeOf (WellKnownType type) => ParameterType.IsTypeOf (type); + } +}
\ No newline at end of file diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs index 104a763fa..ec5f170c8 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs @@ -82,13 +82,10 @@ namespace ILLink.RoslynAnalyzer.TrimAnalysis { if (methodSymbol.TryGetRequiresUnreferencedCodeAttribute (out var requiresUnreferencedCodeAttributeData)) ReportRequiresUnreferencedCodeDiagnostic (diagnosticContext, requiresUnreferencedCodeAttributeData, methodSymbol); - - if (!methodSymbol.IsStatic && methodSymbol.GetDynamicallyAccessedMemberTypes () != DynamicallyAccessedMemberTypes.None) - diagnosticContext.AddDiagnostic (DiagnosticId.DynamicallyAccessedMembersMethodAccessedViaReflection, methodSymbol.GetDisplayName ()); else if (methodSymbol.IsVirtual && FlowAnnotations.GetMethodReturnValueAnnotation (methodSymbol) != DynamicallyAccessedMemberTypes.None) diagnosticContext.AddDiagnostic (DiagnosticId.DynamicallyAccessedMembersMethodAccessedViaReflection, methodSymbol.GetDisplayName ()); else { - foreach (var parameter in methodSymbol.Parameters) { + foreach (var parameter in methodSymbol.GetParameters ()) { if (FlowAnnotations.GetMethodParameterAnnotation (parameter) != DynamicallyAccessedMemberTypes.None) { diagnosticContext.AddDiagnostic (DiagnosticId.DynamicallyAccessedMembersMethodAccessedViaReflection, methodSymbol.GetDisplayName ()); break; diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs index f29eb96a8..1877947f4 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs @@ -113,7 +113,7 @@ namespace ILLink.RoslynAnalyzer.TrimAnalysis // The instance reference operation represents a 'this' or 'base' reference to the containing type, // so we get the annotation from the containing method. if (instanceRef.Type != null && instanceRef.Type.IsTypeInterestingForDataflow ()) - return new MethodThisParameterValue (Method); + return new MethodParameterValue (Method, (ParameterIndex) 0, Method.GetDynamicallyAccessedMemberTypes ()); return TopValue; } diff --git a/src/ILLink.Shared/Annotations.cs b/src/ILLink.Shared/Annotations.cs index ca09218c0..cd3098d1e 100644 --- a/src/ILLink.Shared/Annotations.cs +++ b/src/ILLink.Shared/Annotations.cs @@ -94,35 +94,35 @@ namespace ILLink.Shared public static (DiagnosticId Id, string[] Arguments) GetDiagnosticForAnnotationMismatch (ValueWithDynamicallyAccessedMembers source, ValueWithDynamicallyAccessedMembers target, string missingAnnotations) { DiagnosticId diagnosticId = (source, target) switch { + (MethodParameterValue maybeThisSource, MethodParameterValue maybeThisTarget) when maybeThisSource.IsThisParameter () && maybeThisTarget.IsThisParameter () => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsThisParameter, + (MethodParameterValue maybeThis, MethodParameterValue) when maybeThis.IsThisParameter () => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsParameter, + (MethodParameterValue maybeThis, MethodReturnValue) when maybeThis.IsThisParameter () => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsMethodReturnType, + (MethodParameterValue maybeThis, FieldValue) when maybeThis.IsThisParameter () => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsField, + (MethodParameterValue maybeThis, GenericParameterValue) when maybeThis.IsThisParameter () => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsGenericParameter, + (MethodParameterValue, MethodParameterValue maybeThis) when maybeThis.IsThisParameter () => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsThisParameter, (MethodParameterValue, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsParameter, (MethodParameterValue, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsMethodReturnType, (MethodParameterValue, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsField, - (MethodParameterValue, MethodThisParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsThisParameter, (MethodParameterValue, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsGenericParameter, + (MethodReturnValue, MethodParameterValue maybeThis) when maybeThis.IsThisParameter () => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsThisParameter, (MethodReturnValue, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsParameter, (MethodReturnValue, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsMethodReturnType, (MethodReturnValue, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsField, - (MethodReturnValue, MethodThisParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsThisParameter, (MethodReturnValue, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsGenericParameter, + (FieldValue, MethodParameterValue maybeThis) when maybeThis.IsThisParameter () => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsThisParameter, (FieldValue, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsParameter, (FieldValue, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsMethodReturnType, (FieldValue, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsField, - (FieldValue, MethodThisParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsThisParameter, (FieldValue, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsGenericParameter, - (MethodThisParameterValue, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsParameter, - (MethodThisParameterValue, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsMethodReturnType, - (MethodThisParameterValue, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsField, - (MethodThisParameterValue, MethodThisParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsThisParameter, - (MethodThisParameterValue, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsGenericParameter, + (GenericParameterValue, MethodParameterValue maybeThis) when maybeThis.IsThisParameter () => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsThisParameter, (GenericParameterValue, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsParameter, (GenericParameterValue, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsMethodReturnType, (GenericParameterValue, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsField, - (GenericParameterValue, MethodThisParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsThisParameter, (GenericParameterValue, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsGenericParameter, + (NullableValueWithDynamicallyAccessedMembers, MethodParameterValue maybeThis) when maybeThis.IsThisParameter () => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsThisParameter, (NullableValueWithDynamicallyAccessedMembers, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsParameter, (NullableValueWithDynamicallyAccessedMembers, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsMethodReturnType, (NullableValueWithDynamicallyAccessedMembers, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsField, - (NullableValueWithDynamicallyAccessedMembers, MethodThisParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsThisParameter, (NullableValueWithDynamicallyAccessedMembers, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsGenericParameter, _ => throw new NotImplementedException ($"Unsupported source context {source} or target context {target}.") diff --git a/src/ILLink.Shared/ParameterIndex.cs b/src/ILLink.Shared/ParameterIndex.cs new file mode 100644 index 000000000..84113a820 --- /dev/null +++ b/src/ILLink.Shared/ParameterIndex.cs @@ -0,0 +1,63 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace ILLink.Shared.TypeSystemProxy +{ + /// <summary> + /// Used to indicate the index of the parameter in the Parameters metadata section (i.e. the first parameter that is not the implicit 'this' is 0) + /// It is very error prone to use an int to represent the index in parameters metadata section / source code parameter index as well as for indexing into argument lists. + /// Instead, use this struct whenever representing an index of a parameter. + /// </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 refers to c. + /// In a call to a static extension function Foo(this Bar a, int b, int c) + /// 0 refers to "this Bar a" + /// 1 refers to b, + /// 2 refers to c. + /// In a call to a static function Foo(int a, int b, int c) + /// 0 refers to a, + /// 1 refers to b, + /// 2 refers to c. + /// </example> + public struct ParameterIndex + { + public readonly int Index; + + public ParameterIndex (int x) + => Index = x; + + public static bool operator == (ParameterIndex left, ParameterIndex right) => left.Index == right.Index; + + public static bool operator != (ParameterIndex left, ParameterIndex right) => left.Index != right.Index; + + public static ParameterIndex operator ++ (ParameterIndex val) => new ParameterIndex (val.Index + 1); + + public override bool Equals ([NotNullWhen (true)] object? obj) + => obj is ParameterIndex other && this == other; + + public override int GetHashCode () => Index.GetHashCode (); + + public static explicit operator ParameterIndex (int x) + => new ParameterIndex (x); + + public static explicit operator int (ParameterIndex x) + => x.Index; + public static ParameterIndex operator + (ParameterIndex left, ParameterIndex right) + => new ParameterIndex (left.Index + right.Index); + + public static ParameterIndex operator - (ParameterIndex left, ParameterIndex right) + => new ParameterIndex (left.Index - right.Index); + + public static ParameterIndex operator + (ParameterIndex left, int right) + => new ParameterIndex (left.Index + right); + + public static ParameterIndex operator - (ParameterIndex left, int right) + => new ParameterIndex (left.Index - right); + } +} diff --git a/src/ILLink.Shared/SourceParameterIndex.cs b/src/ILLink.Shared/SourceParameterIndex.cs deleted file mode 100644 index ecca20ac6..000000000 --- a/src/ILLink.Shared/SourceParameterIndex.cs +++ /dev/null @@ -1,19 +0,0 @@ -// 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 40688f448..2a9f4d008 100644 --- a/src/ILLink.Shared/TrimAnalysis/FlowAnnotations.cs +++ b/src/ILLink.Shared/TrimAnalysis/FlowAnnotations.cs @@ -20,12 +20,12 @@ namespace ILLink.Shared.TrimAnalysis internal partial GenericParameterValue GetGenericParameterValue (GenericParameterProxy genericParameter); - internal partial MethodThisParameterValue GetMethodThisParameterValue (MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes); + internal partial MethodParameterValue GetMethodThisParameterValue (MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes); - internal partial MethodThisParameterValue GetMethodThisParameterValue (MethodProxy method); + internal partial MethodParameterValue GetMethodThisParameterValue (MethodProxy method); - internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, SourceParameterIndex parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes); + internal partial MethodParameterValue GetMethodParameterValue (ParameterProxy param, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes); - internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, SourceParameterIndex parameterIndex); + internal partial MethodParameterValue GetMethodParameterValue (ParameterProxy param); } }
\ No newline at end of file diff --git a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index 0fcf700bd..9f0ddbf45 100644 --- a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -316,7 +316,7 @@ namespace ILLink.Shared.TrimAnalysis } BindingFlags? bindingFlags; - if (calledMethod.HasParameterOfType (1, "System.Reflection.BindingFlags")) + if (calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.Reflection.BindingFlags")) bindingFlags = GetBindingFlagsFromValue (argumentValues[1]); else // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter @@ -371,12 +371,12 @@ namespace ILLink.Shared.TrimAnalysis } BindingFlags? bindingFlags; - if (calledMethod.HasParametersCount (1)) { + if (calledMethod.HasMetadataParametersCount (1)) { // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter bindingFlags = BindingFlags.Public | BindingFlags.Instance; - } else if (calledMethod.HasParametersCount (2) && calledMethod.HasParameterOfType (1, "System.Reflection.BindingFlags")) + } else if (calledMethod.HasMetadataParametersCount (2) && calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.Reflection.BindingFlags")) bindingFlags = GetBindingFlagsFromValue (argumentValues[1]); - else if (calledMethod.HasParametersCount (3) && calledMethod.HasParameterOfType (2, "System.Reflection.BindingFlags")) { + else if (calledMethod.HasMetadataParametersCount (3) && calledMethod.HasParameterOfType ((ParameterIndex) 3, "System.Reflection.BindingFlags")) { bindingFlags = GetBindingFlagsFromValue (argumentValues[2]); } else // Non recognized intrinsic throw new ArgumentException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{GetContainingSymbolDisplayName ()}' is an unexpected intrinsic."); @@ -423,9 +423,9 @@ namespace ILLink.Shared.TrimAnalysis } BindingFlags? bindingFlags; - if (calledMethod.HasParameterOfType (1, "System.Reflection.BindingFlags")) + if (calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.Reflection.BindingFlags")) bindingFlags = GetBindingFlagsFromValue (argumentValues[1]); - else if (calledMethod.HasParameterOfType (2, "System.Reflection.BindingFlags")) + else if (calledMethod.HasParameterOfType ((ParameterIndex) 3, "System.Reflection.BindingFlags")) bindingFlags = GetBindingFlagsFromValue (argumentValues[2]); else // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter @@ -471,7 +471,7 @@ namespace ILLink.Shared.TrimAnalysis } BindingFlags? bindingFlags; - if (calledMethod.HasParameterOfType (1, "System.Reflection.BindingFlags")) + if (calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.Reflection.BindingFlags")) bindingFlags = GetBindingFlagsFromValue (argumentValues[1]); else // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter @@ -544,7 +544,7 @@ namespace ILLink.Shared.TrimAnalysis _ => throw new ArgumentException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{GetContainingSymbolDisplayName ()}' is of unexpected member type."), }; - var targetValue = _annotations.GetMethodParameterValue (calledMethod, 0, requiredMemberTypes); + var targetValue = _annotations.GetMethodParameterValue (new (calledMethod, (ParameterIndex) 1), requiredMemberTypes); foreach (var value in argumentValues[0]) { if (value is SystemTypeValue systemTypeValue) { @@ -593,7 +593,7 @@ namespace ILLink.Shared.TrimAnalysis // static New (Type) // case IntrinsicId.Expression_New: { - var targetValue = _annotations.GetMethodParameterValue (calledMethod, 0, DynamicallyAccessedMemberTypes.PublicParameterlessConstructor); + var targetValue = _annotations.GetMethodParameterValue (new (calledMethod, (ParameterIndex) 0), DynamicallyAccessedMemberTypes.PublicParameterlessConstructor); foreach (var value in argumentValues[0]) { if (value is SystemTypeValue systemTypeValue) { MarkConstructorsOnType (systemTypeValue.RepresentedType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, parameterCount: null); @@ -609,7 +609,7 @@ namespace ILLink.Shared.TrimAnalysis // // static Property (Expression, MethodInfo) // - case IntrinsicId.Expression_Property when calledMethod.HasParameterOfType (1, "System.Reflection.MethodInfo"): { + case IntrinsicId.Expression_Property when calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Reflection.MethodInfo"): { if (argumentValues[1].IsEmpty ()) { returnValue = MultiValueLattice.Top; break; @@ -629,7 +629,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, (SourceParameterIndex) 1, DynamicallyAccessedMemberTypes.None).GetDiagnosticArgumentsForAnnotationMismatch ().ToArray ()); + _annotations.GetMethodParameterValue (new (calledMethod, (ParameterIndex) 1), DynamicallyAccessedMemberTypes.None).GetDiagnosticArgumentsForAnnotationMismatch ().ToArray ()); } } break; @@ -651,7 +651,7 @@ namespace ILLink.Shared.TrimAnalysis break; } - var targetValue = _annotations.GetMethodParameterValue (calledMethod, (SourceParameterIndex) 1, memberTypes); + var targetValue = _annotations.GetMethodParameterValue (new (calledMethod, (ParameterIndex) 1), memberTypes); foreach (var value in argumentValues[1]) { if (value is SystemTypeValue systemTypeValue) { foreach (var stringParam in argumentValues[2]) { @@ -684,8 +684,7 @@ namespace ILLink.Shared.TrimAnalysis BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; var targetValue = _annotations.GetMethodParameterValue ( - calledMethod, - 0, + new ParameterProxy (calledMethod, (ParameterIndex) 0), GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags)); // This is true even if we "don't know" - so it's only false if we're sure that there are no type arguments @@ -763,8 +762,8 @@ namespace ILLink.Shared.TrimAnalysis break; } - if ((calledMethod.HasParametersCount (3) && calledMethod.HasParameterOfType (2, "System.Boolean") && argumentValues[2].AsConstInt () != 0) || - (calledMethod.HasParametersCount (5) && argumentValues[4].AsConstInt () != 0)) { + if ((calledMethod.HasMetadataParametersCount (3) && calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.Boolean") && argumentValues[2].AsConstInt () != 0) || + (calledMethod.HasMetadataParametersCount (5) && argumentValues[4].AsConstInt () != 0)) { _diagnosticContext.AddDiagnostic (DiagnosticId.CaseInsensitiveTypeGetTypeCallIsNotSupported, calledMethod.GetDisplayName ()); returnValue = MultiValueLattice.Top; // This effectively disables analysis of anything which uses the return value break; @@ -939,13 +938,13 @@ namespace ILLink.Shared.TrimAnalysis } BindingFlags? bindingFlags; - if (calledMethod.HasParameterOfType (0, "System.Reflection.BindingFlags")) + if (calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Reflection.BindingFlags")) bindingFlags = GetBindingFlagsFromValue (argumentValues[0]); else // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter bindingFlags = BindingFlags.Public | BindingFlags.Instance; - int? ctorParameterCount = calledMethod.GetParametersCount () switch { + int? ctorParameterCount = calledMethod.GetMetadataParametersCount () switch { 1 => (argumentValues[0].AsSingleValue () as ArrayValue)?.Size.AsConstInt (), 2 => (argumentValues[1].AsSingleValue () as ArrayValue)?.Size.AsConstInt (), 4 => (argumentValues[2].AsSingleValue () as ArrayValue)?.Size.AsConstInt (), @@ -1017,8 +1016,8 @@ namespace ILLink.Shared.TrimAnalysis case IntrinsicId.Activator_CreateInstance__Type: { int? ctorParameterCount = null; BindingFlags bindingFlags = BindingFlags.Instance; - if (calledMethod.GetParametersCount () > 1) { - if (calledMethod.HasParameterOfType (1, "System.Boolean")) { + if (calledMethod.GetMetadataParametersCount () > 1) { + if (calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Boolean")) { // The overload that takes a "nonPublic" bool bool nonPublic = argumentValues[1].AsConstInt () != 0; @@ -1029,7 +1028,7 @@ namespace ILLink.Shared.TrimAnalysis ctorParameterCount = 0; } else { // Overload that has the parameters as the second or fourth argument - int argsParam = calledMethod.HasParametersCount (2) || calledMethod.HasParametersCount (3) ? 1 : 3; + int argsParam = calledMethod.HasMetadataParametersCount (2) || calledMethod.HasMetadataParametersCount (3) ? 1 : 3; if (argumentValues.Count > argsParam) { if (argumentValues[argsParam].AsSingleValue () is ArrayValue arrayValue && @@ -1039,7 +1038,7 @@ namespace ILLink.Shared.TrimAnalysis ctorParameterCount = 0; } - if (calledMethod.GetParametersCount () > 3) { + if (calledMethod.GetMetadataParametersCount () > 3) { if (argumentValues[1].AsConstInt () is int constInt) bindingFlags |= (BindingFlags) constInt; else @@ -1069,7 +1068,7 @@ namespace ILLink.Shared.TrimAnalysis requiredMemberTypes |= DynamicallyAccessedMemberTypes.PublicParameterlessConstructor; } - var targetValue = _annotations.GetMethodParameterValue (calledMethod, 0, requiredMemberTypes); + var targetValue = _annotations.GetMethodParameterValue (new (calledMethod, (ParameterIndex) 0), requiredMemberTypes); _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); } @@ -1140,13 +1139,14 @@ namespace ILLink.Shared.TrimAnalysis case IntrinsicId.None: // Verify the argument values match the annotations on the parameter definition if (requiresDataFlowAnalysis) { - if (!calledMethod.IsStatic ()) { - _requireDynamicallyAccessedMembersAction.Invoke (instanceValue, _annotations.GetMethodThisParameterValue (calledMethod)); - } - for (int argumentIndex = 0; argumentIndex < argumentValues.Count; argumentIndex++) { - if (argumentIndex >= calledMethod.GetParametersCount () || calledMethod.ParameterReferenceKind (argumentIndex) == ReferenceKind.Out) + foreach (var parameter in calledMethod.GetParameters ()) { + if (parameter.GetReferenceKind () is ReferenceKind.Out) + continue; + if (parameter.IsImplicitThis) { + _requireDynamicallyAccessedMembersAction.Invoke (instanceValue, _annotations.GetMethodThisParameterValue (calledMethod)); continue; - _requireDynamicallyAccessedMembersAction.Invoke (argumentValues[argumentIndex], _annotations.GetMethodParameterValue (calledMethod, (SourceParameterIndex) argumentIndex)); + } + _requireDynamicallyAccessedMembersAction.Invoke (argumentValues[parameter.MetadataIndex], _annotations.GetMethodParameterValue (parameter)); } } break; @@ -1255,7 +1255,7 @@ namespace ILLink.Shared.TrimAnalysis // https://github.com/dotnet/linker/issues/2428 // We need to report the target as "this" - as that was the previous behavior // but with the annotation from the generic parameter. - var targetValue = _annotations.GetMethodThisParameterValue (calledMethod, GetGenericParameterEffectiveMemberTypes (genericParameters[i])); + var targetValue = _annotations.GetMethodThisParameterValue (calledMethod, GetGenericParameterEffectiveMemberTypes (genericParameters[i]), overrideIsThis: true); _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); } } @@ -1308,7 +1308,8 @@ namespace ILLink.Shared.TrimAnalysis { BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; bool parameterlessConstructor = true; - if (calledMethod.HasParametersCount (8) && calledMethod.HasParameterOfType (2, "System.Boolean")) { + int offset = calledMethod.HasImplicitThis () ? 1 : 0; + if (calledMethod.HasMetadataParametersCount (8) && calledMethod.HasParameterOfType ((ParameterIndex) 2 + offset, "System.Boolean")) { parameterlessConstructor = false; bindingFlags = BindingFlags.Instance; if (argumentValues[3].AsConstInt () is int bindingFlagsInt) @@ -1339,11 +1340,11 @@ namespace ILLink.Shared.TrimAnalysis MarkConstructorsOnType (resolvedType, bindingFlags, parameterlessConstructor ? 0 : null); } else { - _diagnosticContext.AddDiagnostic (DiagnosticId.UnrecognizedParameterInMethodCreateInstance, calledMethod.GetParameterDisplayName (1), calledMethod.GetDisplayName ()); + _diagnosticContext.AddDiagnostic (DiagnosticId.UnrecognizedParameterInMethodCreateInstance, new ParameterProxy (calledMethod, (ParameterIndex) 1 + offset).GetDisplayName (), calledMethod.GetDisplayName ()); } } } else { - _diagnosticContext.AddDiagnostic (DiagnosticId.UnrecognizedParameterInMethodCreateInstance, calledMethod.GetParameterDisplayName (0), calledMethod.GetDisplayName ()); + _diagnosticContext.AddDiagnostic (DiagnosticId.UnrecognizedParameterInMethodCreateInstance, new ParameterProxy (calledMethod, (ParameterIndex) 0 + offset).GetDisplayName (), calledMethod.GetDisplayName ()); } } } diff --git a/src/ILLink.Shared/TrimAnalysis/Intrinsics.cs b/src/ILLink.Shared/TrimAnalysis/Intrinsics.cs index 3b9ee24cd..031aa025e 100644 --- a/src/ILLink.Shared/TrimAnalysis/Intrinsics.cs +++ b/src/ILLink.Shared/TrimAnalysis/Intrinsics.cs @@ -25,11 +25,11 @@ namespace ILLink.Shared.TrimAnalysis // System.Type.TypeHandle getter "get_TypeHandle" when calledMethod.IsDeclaredOnType ("System.Type") => IntrinsicId.Type_get_TypeHandle, - // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle) - // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle, RuntimeTypeHandle declaringType) + // static System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle) + // static System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle, RuntimeTypeHandle declaringType) "GetMethodFromHandle" when calledMethod.IsDeclaredOnType ("System.Reflection.MethodBase") - && calledMethod.HasParameterOfType (0, "System.RuntimeMethodHandle") - && (calledMethod.HasParametersCount (1) || calledMethod.HasParametersCount (2)) + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.RuntimeMethodHandle") + && (calledMethod.HasMetadataParametersCount (1) || calledMethod.HasMetadataParametersCount (2)) => IntrinsicId.MethodBase_GetMethodFromHandle, // System.Reflection.MethodBase.MethodHandle getter @@ -40,87 +40,87 @@ namespace ILLink.Shared.TrimAnalysis // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeEvent (this Type type, string name) "GetRuntimeEvent" when calledMethod.IsDeclaredOnType ("System.Reflection.RuntimeReflectionExtensions") - && calledMethod.HasParameterOfType (0, "System.Type") - && calledMethod.HasParameterOfType (1, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.Type") + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent, // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeField (this Type type, string name) "GetRuntimeField" when calledMethod.IsDeclaredOnType ("System.Reflection.RuntimeReflectionExtensions") - && calledMethod.HasParameterOfType (0, "System.Type") - && calledMethod.HasParameterOfType (1, "System.,String") + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.Type") + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.,String") => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField, // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeMethod (this Type type, string name, Type[] parameters) "GetRuntimeMethod" when calledMethod.IsDeclaredOnType ("System.Reflection.RuntimeReflectionExtensions") - && calledMethod.HasParameterOfType (0, "System.Type") - && calledMethod.HasParameterOfType (1, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.Type") + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod, // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeProperty (this Type type, string name) "GetRuntimeProperty" when calledMethod.IsDeclaredOnType ("System.Reflection.RuntimeReflectionExtensions") - && calledMethod.HasParameterOfType (0, "System.Type") - && calledMethod.HasParameterOfType (1, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.Type") + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty, // static System.Linq.Expressions.Expression.Call (Type, String, Type[], Expression[]) "Call" when calledMethod.IsDeclaredOnType ("System.Linq.Expressions.Expression") - && calledMethod.HasParameterOfType (0, "System.Type") - && calledMethod.HasParametersCount (4) + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.Type") + && calledMethod.HasMetadataParametersCount (4) => IntrinsicId.Expression_Call, // static System.Linq.Expressions.Expression.Field (Expression, Type, String) "Field" when calledMethod.IsDeclaredOnType ("System.Linq.Expressions.Expression") - && calledMethod.HasParameterOfType (1, "System.Type") - && calledMethod.HasParametersCount (3) + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Type") + && calledMethod.HasMetadataParametersCount (3) => IntrinsicId.Expression_Field, // static System.Linq.Expressions.Expression.Property (Expression, Type, String) // static System.Linq.Expressions.Expression.Property (Expression, MethodInfo) "Property" when calledMethod.IsDeclaredOnType ("System.Linq.Expressions.Expression") - && ((calledMethod.HasParameterOfType (1, "System.Type") && calledMethod.HasParametersCount (3)) - || (calledMethod.HasParameterOfType (1, "System.Reflection.MethodInfo") && calledMethod.HasParametersCount (2))) + && ((calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Type") && calledMethod.HasMetadataParametersCount (3)) + || (calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Reflection.MethodInfo") && calledMethod.HasMetadataParametersCount (2))) => IntrinsicId.Expression_Property, // static System.Linq.Expressions.Expression.New (Type) "New" when calledMethod.IsDeclaredOnType ("System.Linq.Expressions.Expression") - && calledMethod.HasParameterOfType (0, "System.Type") - && calledMethod.HasParametersCount (1) + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.Type") + && calledMethod.HasMetadataParametersCount (1) => IntrinsicId.Expression_New, // static Array System.Enum.GetValues (Type) "GetValues" when calledMethod.IsDeclaredOnType ("System.Enum") - && calledMethod.HasParameterOfType (0, "System.Type") - && calledMethod.HasParametersCount (1) + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.Type") + && calledMethod.HasMetadataParametersCount (1) => IntrinsicId.Enum_GetValues, // static int System.Runtime.InteropServices.Marshal.SizeOf (Type) "SizeOf" when calledMethod.IsDeclaredOnType ("System.Runtime.InteropServices.Marshal") - && calledMethod.HasParameterOfType (0, "System.Type") - && calledMethod.HasParametersCount (1) + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.Type") + && calledMethod.HasMetadataParametersCount (1) => IntrinsicId.Marshal_SizeOf, // static int System.Runtime.InteropServices.Marshal.OffsetOf (Type, string) "OffsetOf" when calledMethod.IsDeclaredOnType ("System.Runtime.InteropServices.Marshal") - && calledMethod.HasParameterOfType (0, "System.Type") - && calledMethod.HasParametersCount (2) + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.Type") + && calledMethod.HasMetadataParametersCount (2) => IntrinsicId.Marshal_OffsetOf, // static object System.Runtime.InteropServices.Marshal.PtrToStructure (IntPtr, Type) "PtrToStructure" when calledMethod.IsDeclaredOnType ("System.Runtime.InteropServices.Marshal") - && calledMethod.HasParameterOfType (1, "System.Type") - && calledMethod.HasParametersCount (2) + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Type") + && calledMethod.HasMetadataParametersCount (2) => IntrinsicId.Marshal_PtrToStructure, // static void System.Runtime.InteropServices.Marshal.DestroyStructure (IntPtr, Type) "DestroyStructure" when calledMethod.IsDeclaredOnType ("System.Runtime.InteropServices.Marshal") - && calledMethod.HasParameterOfType (1, "System.Type") - && calledMethod.HasParametersCount (2) + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Type") + && calledMethod.HasMetadataParametersCount (2) => IntrinsicId.Marshal_DestroyStructure, // static Delegate System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer (IntPtr, Type) "GetDelegateForFunctionPointer" when calledMethod.IsDeclaredOnType ("System.Runtime.InteropServices.Marshal") - && calledMethod.HasParameterOfType (1, "System.Type") - && calledMethod.HasParametersCount (2) + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Type") + && calledMethod.HasMetadataParametersCount (2) => IntrinsicId.Marshal_GetDelegateForFunctionPointer, // static System.Type.GetType (string) @@ -130,7 +130,7 @@ namespace ILLink.Shared.TrimAnalysis // static System.Type.GetType (string, Func<AssemblyName, Assembly>, Func<Assembly, String, Boolean, Type>, Boolean) // static System.Type.GetType (string, Func<AssemblyName, Assembly>, Func<Assembly, String, Boolean, Type>, Boolean, Boolean) "GetType" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.String") => IntrinsicId.Type_GetType, // System.Type.GetConstructor (Type[]) @@ -143,8 +143,8 @@ namespace ILLink.Shared.TrimAnalysis // System.Type.GetConstructors (BindingFlags) "GetConstructors" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.Reflection.BindingFlags") - && calledMethod.HasParametersCount (1) + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Reflection.BindingFlags") + && calledMethod.HasMetadataParametersCount (1) && !calledMethod.IsStatic () => IntrinsicId.Type_GetConstructors__BindingFlags, @@ -160,102 +160,102 @@ namespace ILLink.Shared.TrimAnalysis // System.Type.GetMethod (string, int, BindingFlags, Binder?, Type[], ParameterModifier[]?) // System.Type.GetMethod (string, int, BindingFlags, Binder?, CallingConventions, Type[], ParameterModifier[]?) "GetMethod" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.String") - && !calledMethod.IsStatic () + && calledMethod.HasImplicitThis () + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") => IntrinsicId.Type_GetMethod, // System.Type.GetMethods (BindingFlags) "GetMethods" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.Reflection.BindingFlags") - && calledMethod.HasParametersCount (1) - && !calledMethod.IsStatic () + && calledMethod.HasImplicitThis () + && calledMethod.HasMetadataParametersCount (1) + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Reflection.BindingFlags") => IntrinsicId.Type_GetMethods__BindingFlags, // System.Type.GetField (string) // System.Type.GetField (string, BindingFlags) "GetField" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.String") - && !calledMethod.IsStatic () + && calledMethod.HasImplicitThis () + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") => IntrinsicId.Type_GetField, // System.Type.GetFields (BindingFlags) "GetFields" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.Reflection.BindingFlags") - && calledMethod.HasParametersCount (1) - && !calledMethod.IsStatic () + && calledMethod.HasImplicitThis () + && calledMethod.HasMetadataParametersCount (1) + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Reflection.BindingFlags") => IntrinsicId.Type_GetFields__BindingFlags, // System.Type.GetEvent (string) // System.Type.GetEvent (string, BindingFlags) "GetEvent" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.String") - && !calledMethod.IsStatic () + && calledMethod.HasImplicitThis () + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") => IntrinsicId.Type_GetEvent, // System.Type.GetEvents (BindingFlags) "GetEvents" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.Reflection.BindingFlags") - && calledMethod.HasParametersCount (1) - && !calledMethod.IsStatic () + && calledMethod.HasImplicitThis () + && calledMethod.HasMetadataParametersCount (1) + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Reflection.BindingFlags") => IntrinsicId.Type_GetEvents__BindingFlags, // System.Type.GetNestedType (string) // System.Type.GetNestedType (string, BindingFlags) "GetNestedType" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.String") - && !calledMethod.IsStatic () + && calledMethod.HasImplicitThis () + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") => IntrinsicId.Type_GetNestedType, // System.Type.GetNestedTypes (BindingFlags) "GetNestedTypes" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.Reflection.BindingFlags") - && calledMethod.HasParametersCount (1) - && !calledMethod.IsStatic () + && calledMethod.HasImplicitThis () + && calledMethod.HasMetadataParametersCount (1) + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Reflection.BindingFlags") => IntrinsicId.Type_GetNestedTypes__BindingFlags, // System.Type.GetMember (String) // System.Type.GetMember (String, BindingFlags) // System.Type.GetMember (String, MemberTypes, BindingFlags) "GetMember" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.String") - && !calledMethod.IsStatic () - && (calledMethod.HasParametersCount (1) || - (calledMethod.HasParametersCount (2) && calledMethod.HasParameterOfType (1, "System.Reflection.BindingFlags")) || - (calledMethod.HasParametersCount (3) && calledMethod.HasParameterOfType (2, "System.Reflection.BindingFlags"))) + && calledMethod.HasImplicitThis () + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") + && (calledMethod.HasMetadataParametersCount (1) || + (calledMethod.HasMetadataParametersCount (2) && calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.Reflection.BindingFlags")) || + (calledMethod.HasMetadataParametersCount (3) && calledMethod.HasParameterOfType ((ParameterIndex) 3, "System.Reflection.BindingFlags"))) => IntrinsicId.Type_GetMember, // System.Type.GetMembers (BindingFlags) "GetMembers" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.Reflection.BindingFlags") - && calledMethod.HasParametersCount (1) - && !calledMethod.IsStatic () + && calledMethod.HasImplicitThis () + && calledMethod.HasMetadataParametersCount (1) + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Reflection.BindingFlags") => IntrinsicId.Type_GetMembers__BindingFlags, // System.Type.GetInterface (string) // System.Type.GetInterface (string, bool) "GetInterface" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.String") - && !calledMethod.IsStatic () - && (calledMethod.HasParametersCount (1) || - (calledMethod.HasParametersCount (2) && calledMethod.HasParameterOfType (1, "System.Boolean"))) + && calledMethod.HasImplicitThis () + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") + && (calledMethod.HasMetadataParametersCount (1) || + (calledMethod.HasMetadataParametersCount (2) && calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.Boolean"))) => IntrinsicId.Type_GetInterface, // System.Type.AssemblyQualifiedName "get_AssemblyQualifiedName" when calledMethod.IsDeclaredOnType ("System.Type") - && !calledMethod.HasParameters () - && !calledMethod.IsStatic () + && calledMethod.HasImplicitThis () + && !calledMethod.HasMetadataParameters () => IntrinsicId.Type_get_AssemblyQualifiedName, // System.Type.UnderlyingSystemType "get_UnderlyingSystemType" when calledMethod.IsDeclaredOnType ("System.Type") - && !calledMethod.HasParameters () - && !calledMethod.IsStatic () + && calledMethod.HasImplicitThis () + && !calledMethod.HasMetadataParameters () => IntrinsicId.Type_get_UnderlyingSystemType, // System.Type.BaseType "get_BaseType" when calledMethod.IsDeclaredOnType ("System.Type") - && !calledMethod.HasParameters () - && !calledMethod.IsStatic () + && calledMethod.HasImplicitThis () + && !calledMethod.HasMetadataParameters () => IntrinsicId.Type_get_BaseType, // System.Type.GetProperty (string) @@ -266,15 +266,15 @@ namespace ILLink.Shared.TrimAnalysis // System.Type.GetProperty (string, Type, Type[], ParameterModifier[]) // System.Type.GetProperty (string, BindingFlags, Binder, Type, Type[], ParameterModifier[]) "GetProperty" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.String") - && !calledMethod.IsStatic () + && calledMethod.HasImplicitThis () + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") => IntrinsicId.Type_GetProperty, // System.Type.GetProperties (BindingFlags) "GetProperties" when calledMethod.IsDeclaredOnType ("System.Type") - && calledMethod.HasParameterOfType (0, "System.Reflection.BindingFlags") - && calledMethod.HasParametersCount (1) - && !calledMethod.IsStatic () + && calledMethod.HasImplicitThis () + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Reflection.BindingFlags") + && calledMethod.HasMetadataParametersCount (1) => IntrinsicId.Type_GetProperties__BindingFlags, // static System.Object.GetType () @@ -282,7 +282,7 @@ namespace ILLink.Shared.TrimAnalysis => IntrinsicId.Object_GetType, ".ctor" when calledMethod.IsDeclaredOnType ("System.Reflection.TypeDelegator") - && calledMethod.HasParameterOfType (0, "System.Type") + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Type") => IntrinsicId.TypeDelegator_Ctor, "Empty" when calledMethod.IsDeclaredOnType ("System.Array") @@ -296,7 +296,7 @@ namespace ILLink.Shared.TrimAnalysis // static System.Activator.CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; } "CreateInstance" when calledMethod.IsDeclaredOnType ("System.Activator") && !calledMethod.HasGenericParameters () - && calledMethod.HasParameterOfType (0, "System.Type") + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.Type") => IntrinsicId.Activator_CreateInstance__Type, // static System.Activator.CreateInstance (string assemblyName, string typeName) @@ -304,8 +304,8 @@ namespace ILLink.Shared.TrimAnalysis // static System.Activator.CreateInstance (string assemblyName, string typeName, object?[]? activationAttributes) "CreateInstance" when calledMethod.IsDeclaredOnType ("System.Activator") && !calledMethod.HasGenericParameters () - && calledMethod.HasParameterOfType (0, "System.String") - && calledMethod.HasParameterOfType (1, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") => IntrinsicId.Activator_CreateInstance__AssemblyName_TypeName, // static System.Activator.CreateInstanceFrom (string assemblyFile, string typeName) @@ -313,63 +313,64 @@ namespace ILLink.Shared.TrimAnalysis // static System.Activator.CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) "CreateInstanceFrom" when calledMethod.IsDeclaredOnType ("System.Activator") && !calledMethod.HasGenericParameters () - && calledMethod.HasParameterOfType (0, "System.String") - && calledMethod.HasParameterOfType (1, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") => IntrinsicId.Activator_CreateInstanceFrom, // System.AppDomain.CreateInstance (string assemblyName, string typeName) // System.AppDomain.CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) // System.AppDomain.CreateInstance (string assemblyName, string typeName, object? []? activationAttributes) "CreateInstance" when calledMethod.IsDeclaredOnType ("System.AppDomain") - && calledMethod.HasParameterOfType (0, "System.String") - && calledMethod.HasParameterOfType (1, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.String") => IntrinsicId.AppDomain_CreateInstance, // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName) // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName, object? []? activationAttributes) "CreateInstanceAndUnwrap" when calledMethod.IsDeclaredOnType ("System.AppDomain") - && calledMethod.HasParameterOfType (0, "System.String") - && calledMethod.HasParameterOfType (1, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.String") => IntrinsicId.AppDomain_CreateInstanceAndUnwrap, // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName) // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) "CreateInstanceFrom" when calledMethod.IsDeclaredOnType ("System.AppDomain") - && calledMethod.HasParameterOfType (0, "System.String") - && calledMethod.HasParameterOfType (1, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.String") => IntrinsicId.AppDomain_CreateInstanceFrom, // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName) // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, object? []? activationAttributes) "CreateInstanceFromAndUnwrap" when calledMethod.IsDeclaredOnType ("System.AppDomain") - && calledMethod.HasParameterOfType (0, "System.String") - && calledMethod.HasParameterOfType (1, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.String") => IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap, // System.Reflection.Assembly.CreateInstance (string typeName) // System.Reflection.Assembly.CreateInstance (string typeName, bool ignoreCase) // System.Reflection.Assembly.CreateInstance (string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder? binder, object []? args, CultureInfo? culture, object []? activationAttributes) "CreateInstance" when calledMethod.IsDeclaredOnType ("System.Reflection.Assembly") - && calledMethod.HasParameterOfType (0, "System.String") + && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String") => IntrinsicId.Assembly_CreateInstance, // System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) "RunClassConstructor" when calledMethod.IsDeclaredOnType ("System.Runtime.CompilerServices.RuntimeHelpers") - && calledMethod.HasParameterOfType (0, "System.RuntimeTypeHandle") + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.RuntimeTypeHandle") => IntrinsicId.RuntimeHelpers_RunClassConstructor, // System.Reflection.MethodInfo.MakeGenericMethod (Type[] typeArguments) "MakeGenericMethod" when calledMethod.IsDeclaredOnType ("System.Reflection.MethodInfo") - && !calledMethod.IsStatic () - && calledMethod.HasParametersCount (1) + && calledMethod.HasImplicitThis () + && calledMethod.HasMetadataParametersCount (1) => IntrinsicId.MethodInfo_MakeGenericMethod, + // static System.Nullable.GetUnderlyingType "GetUnderlyingType" when calledMethod.IsDeclaredOnType ("System.Nullable") - && calledMethod.HasParameterOfType (0, "System.Type") && calledMethod.IsStatic () + && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.Type") => IntrinsicId.Nullable_GetUnderlyingType, _ => IntrinsicId.None, diff --git a/src/ILLink.Shared/TrimAnalysis/MethodParameterValue.cs b/src/ILLink.Shared/TrimAnalysis/MethodParameterValue.cs index 8d2b4a6a6..108420326 100644 --- a/src/ILLink.Shared/TrimAnalysis/MethodParameterValue.cs +++ b/src/ILLink.Shared/TrimAnalysis/MethodParameterValue.cs @@ -4,7 +4,30 @@ // This is needed due to NativeAOT which doesn't enable nullable globally yet #nullable enable + +using System.Collections.Generic; +using ILLink.Shared.DataFlow; +using ILLink.Shared.TypeSystemProxy; + namespace ILLink.Shared.TrimAnalysis { - sealed partial record MethodParameterValue : ValueWithDynamicallyAccessedMembers; + sealed partial record MethodParameterValue : ValueWithDynamicallyAccessedMembers + { + // _overrideIsThis is needed for backwards compatibility with MakeGenericType/Method https://github.com/dotnet/linker/issues/2428 + private readonly bool _overrideIsThis; + + public ParameterProxy Parameter { get; } + + public override IEnumerable<string> GetDiagnosticArgumentsForAnnotationMismatch () + => Parameter.GetDiagnosticArgumentsForAnnotationMismatch (); + + public override string ToString () + => this.ValueToString (Parameter.Method.Method, DynamicallyAccessedMemberTypes); + + public bool IsThisParameter () => _overrideIsThis || Parameter.IsImplicitThis; + + public override SingleValue DeepCopy () => this; // This value is immutable + + public ParameterIndex Index => Parameter.Index; + } } diff --git a/src/ILLink.Shared/TrimAnalysis/MethodThisParameterValue.cs b/src/ILLink.Shared/TrimAnalysis/MethodThisParameterValue.cs deleted file mode 100644 index baf12d256..000000000 --- a/src/ILLink.Shared/TrimAnalysis/MethodThisParameterValue.cs +++ /dev/null @@ -1,10 +0,0 @@ -// 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. - -// This is needed due to NativeAOT which doesn't enable nullable globally yet -#nullable enable - -namespace ILLink.Shared.TrimAnalysis -{ - sealed partial record MethodThisParameterValue : ValueWithDynamicallyAccessedMembers; -} diff --git a/src/ILLink.Shared/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs b/src/ILLink.Shared/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs index 389116f97..54a1c7ca1 100644 --- a/src/ILLink.Shared/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs +++ b/src/ILLink.Shared/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs @@ -52,10 +52,10 @@ namespace ILLink.Shared.TrimAnalysis // Ignore - probably unreachable path as it would fail at runtime anyway. } else { DiagnosticId diagnosticId = targetValue switch { + MethodParameterValue maybeThis when maybeThis.IsThisParameter () => DiagnosticId.ImplicitThisCannotBeStaticallyDetermined, MethodParameterValue => DiagnosticId.MethodParameterCannotBeStaticallyDetermined, MethodReturnValue => DiagnosticId.MethodReturnValueCannotBeStaticallyDetermined, FieldValue => DiagnosticId.FieldValueCannotBeStaticallyDetermined, - MethodThisParameterValue => DiagnosticId.ImplicitThisCannotBeStaticallyDetermined, GenericParameterValue => DiagnosticId.TypePassedToGenericParameterCannotBeStaticallyDetermined, _ => throw new NotImplementedException ($"unsupported target value {targetValue}") }; diff --git a/src/ILLink.Shared/TypeSystemProxy/MethodProxy.cs b/src/ILLink.Shared/TypeSystemProxy/MethodProxy.cs index a30ce2200..92ba44e63 100644 --- a/src/ILLink.Shared/TypeSystemProxy/MethodProxy.cs +++ b/src/ILLink.Shared/TypeSystemProxy/MethodProxy.cs @@ -13,17 +13,51 @@ namespace ILLink.Shared.TypeSystemProxy // Currently this only needs to work on non-nested, non-generic types. // The format of the fullTypeName parameter is 'namespace.typename', so for example 'System.Reflection.Assembly' internal partial bool IsDeclaredOnType (string fullTypeName); - internal partial bool HasParameters (); + + /// <summary> + /// Returns the number of the parameters in the 'parameters' metadata section. This should map directly to the number of parameters in the C# source declaration as well. + /// </summary> + internal partial int GetMetadataParametersCount (); + + /// <summary> + /// Returns true if the method has parameters in the 'parameters' metadata section (i.e. has parameters besides the implicit 'this' parameter) + /// </summary> + internal partial bool HasMetadataParameters (); + + /// <summary> + /// Returns the number of parameters that are passed to the method in IL (including the implicit 'this' parameter). + /// In pseudocode: <code>method.HasImplicitThis() ? 1 + MetadataParametersCount : MetadataParametersCount;</code> + /// </summary> internal partial int GetParametersCount (); - internal bool HasParametersCount (int parameterCount) => GetParametersCount () == parameterCount; + + /// <summary> + /// Returns a List of <see cref="ParameterProxy"/> representing the parameters the method takes, including the implicit 'this' parameters. + /// </summary> + internal partial ParameterProxyEnumerable GetParameters (); + + /// <summary> + /// Returns the ParameterProxy corresponding to the parameter at <paramref name="index"/>, and throws if the index is out of bounds for the method. + /// <paramref name="index"/> is the index of the parameters as they are passed to the method, with 0 being the implicit this parameter if it exists. + /// See <see cref="ParameterIndex"/> for more info. + /// </summary> + internal partial ParameterProxy GetParameter (ParameterIndex index); + + /// <summary> + /// Returns true if the 'parameters' metadata section has <paramref name="parameterCount"/> number of parameters. + /// Metadata parameters count maps directly to the number of parameters in C# source code. + /// Metadata parameters count excludes the implicit 'this' parameter. + /// </summary> + internal bool HasMetadataParametersCount (int parameterCount) => GetMetadataParametersCount () == parameterCount; + // Currently this only needs to work on non-nested, non-generic types. // The format of the fullTypeName parameter is 'namespace.typename', so for example 'System.Reflection.Assembly' - internal partial bool HasParameterOfType (int parameterIndex, string fullTypeName); - internal partial string GetParameterDisplayName (int parameterIndex); + internal bool HasParameterOfType (ParameterIndex parameterIndex, string fullTypeName) + => (int) parameterIndex < GetParametersCount () && GetParameter (parameterIndex).IsTypeOf (fullTypeName); internal partial bool HasGenericParameters (); internal partial bool HasGenericParametersCount (int genericParameterCount); internal partial ImmutableArray<GenericParameterProxy> GetGenericParameters (); internal partial bool IsStatic (); + internal partial bool HasImplicitThis (); internal partial bool ReturnsVoid (); } } diff --git a/src/ILLink.Shared/TypeSystemProxy/ParameterCollection.cs b/src/ILLink.Shared/TypeSystemProxy/ParameterCollection.cs new file mode 100644 index 000000000..eaba6c5fd --- /dev/null +++ b/src/ILLink.Shared/TypeSystemProxy/ParameterCollection.cs @@ -0,0 +1,59 @@ +// 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 System.Collections; +using System.Collections.Generic; + +namespace ILLink.Shared.TypeSystemProxy +{ + /// <summary> + /// Enumerable struct used to enumerator over a method's parameters without allocating or going through IEnumerable + /// </summary> + internal struct ParameterProxyEnumerable : IEnumerable<ParameterProxy> + { + readonly int _start; + + readonly int _end; + + readonly MethodProxy _method; + + public ParameterProxyEnumerable (int start, int end, MethodProxy method) + { + _start = start; + _end = end; + _method = method; + } + + public ParameterEnumerator GetEnumerator () => new ParameterEnumerator (_start, _end, _method); + + IEnumerator<ParameterProxy> IEnumerable<ParameterProxy>.GetEnumerator () => new ParameterEnumerator (_start, _end, _method); + + IEnumerator IEnumerable.GetEnumerator () => new ParameterEnumerator (_start, _end, _method); + + public struct ParameterEnumerator : IEnumerator<ParameterProxy> + { + readonly int _start; + int _current; + readonly int _end; + readonly MethodProxy _method; + public ParameterEnumerator (int start, int end, MethodProxy method) + { + _start = start; + _current = start - 1; + _end = end; + _method = method; + } + + public ParameterProxy Current => new ParameterProxy (_method, (ParameterIndex) _current); + + object IEnumerator.Current => new ParameterProxy (_method, (ParameterIndex) _current); + + public bool MoveNext () => ++_current < _end; + + public void Reset () => _current = _start; + + void IDisposable.Dispose () { } + } + } +} diff --git a/src/ILLink.Shared/TypeSystemProxy/ParameterProxy.cs b/src/ILLink.Shared/TypeSystemProxy/ParameterProxy.cs new file mode 100644 index 000000000..c31c5f383 --- /dev/null +++ b/src/ILLink.Shared/TypeSystemProxy/ParameterProxy.cs @@ -0,0 +1,53 @@ +// 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 System.Collections.Generic; + +namespace ILLink.Shared.TypeSystemProxy +{ + internal partial struct ParameterProxy + { + public ParameterProxy (MethodProxy method, ParameterIndex index) + { + if ((int) index < 0 || (int) index >= method.GetParametersCount ()) + throw new InvalidOperationException ($"Parameter of index {(int) index} does not exist on method {method.GetDisplayName ()} with {method.GetParametersCount ()}"); + Method = method; + Index = index; + } + + public MethodProxy Method { get; } + + public ParameterIndex Index { get; } + + /// <summary> + /// The index of the entry in the '.parameters' metadata section corresponding to this parameter. + /// Maps to the index of the parameter in Cecil's MethodReference.Parameters or Roslyn's IMethodSymbol.Parameters + /// Throws if the parameter is the implicit 'this' parameter. + /// </summary> + public int MetadataIndex { + get { + if (Method.HasImplicitThis ()) { + if (IsImplicitThis) + throw new InvalidOperationException ("Cannot get metadata index of the implicit 'this' parameter"); + return (int) Index - 1; + } + return (int) Index; + } + } + + public partial ReferenceKind GetReferenceKind (); + + public partial string GetDisplayName (); + + public bool IsImplicitThis => Method.HasImplicitThis () && Index == (ParameterIndex) 0; + + public partial bool IsTypeOf (string typeName); + + public IEnumerable<string> GetDiagnosticArgumentsForAnnotationMismatch () + => IsImplicitThis ? + new string[] { Method.GetDisplayName () } + + : new string[] { GetDisplayName (), Method.GetDisplayName () }; + } +} diff --git a/src/linker/BannedSymbols.txt b/src/linker/BannedSymbols.txt index d7c70af18..de67d0d93 100644 --- a/src/linker/BannedSymbols.txt +++ b/src/linker/BannedSymbols.txt @@ -1,5 +1,7 @@ T:Mono.Cecil.Cil.ILProcessor;Use LinkerILProcessor instead M:Mono.Cecil.TypeReference.Resolve();Use LinkContext.Resolve and LinkContext.TryResolve helpers instead +P:Mono.Cecil.MethodReference.Parameters;Use an extension method from MethodReferenceExtensions instead +P:Mono.Cecil.MethodReference.HasParameters;Use an extension method from MethodReference.HasMetadataParameters() instead M:Mono.Cecil.MethodReference.Resolve();Use LinkContext.Resolve and LinkContext.TryResolve helpers instead M:Mono.Cecil.ExportedType.Resolve();Use LinkContext.Resolve and LinkContext.TryResolve helpers instead P:Mono.Collections.Generic.Collection`1{Mono.Cecil.ParameterDefinition}.Item(System.Int32); use x diff --git a/src/linker/Linker.Dataflow/AttributeDataFlow.cs b/src/linker/Linker.Dataflow/AttributeDataFlow.cs index 03a32483c..fa2aa697d 100644 --- a/src/linker/Linker.Dataflow/AttributeDataFlow.cs +++ b/src/linker/Linker.Dataflow/AttributeDataFlow.cs @@ -4,7 +4,6 @@ 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,10 +26,10 @@ 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, (SourceParameterIndex) i); + foreach (var parameter in method.GetMetadataParameters ()) { + var parameterValue = _context.Annotations.FlowAnnotations.GetMethodParameterValue (parameter); if (parameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None) { - MultiValue value = GetValueForCustomAttributeArgument (arguments[i]); + MultiValue value = GetValueForCustomAttributeArgument (arguments[parameter.MetadataIndex]); var diagnosticContext = new DiagnosticContext (_origin, diagnosticsEnabled: true, _context); RequireDynamicallyAccessedMembers (diagnosticContext, value, parameterValue); } @@ -74,4 +73,4 @@ namespace Mono.Linker.Dataflow requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); } } -}
\ No newline at end of file +} diff --git a/src/linker/Linker.Dataflow/DiagnosticUtilities.cs b/src/linker/Linker.Dataflow/DiagnosticUtilities.cs index e354b683e..390081bd7 100644 --- a/src/linker/Linker.Dataflow/DiagnosticUtilities.cs +++ b/src/linker/Linker.Dataflow/DiagnosticUtilities.cs @@ -1,30 +1,12 @@ // 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; namespace Mono.Linker.Dataflow { static class DiagnosticUtilities { - internal static IMetadataTokenProvider GetMethodParameterFromIndex (MethodDefinition method, int parameterIndex) - { - int declaredParameterIndex; - if (method.HasImplicitThis ()) { - if (parameterIndex == 0) - return method; - - declaredParameterIndex = parameterIndex - 1; - } else - declaredParameterIndex = parameterIndex; - - if (declaredParameterIndex >= 0 && declaredParameterIndex < method.Parameters.Count) - return method.Parameters[declaredParameterIndex]; - - throw new InvalidOperationException (); - } - internal static string GetParameterNameForErrorMessage (ParameterDefinition parameterDefinition) => string.IsNullOrEmpty (parameterDefinition.Name) ? $"#{parameterDefinition.Index}" : parameterDefinition.Name; diff --git a/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs b/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs index 26b67b553..dd6971040 100644 --- a/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs +++ b/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs @@ -41,7 +41,7 @@ namespace Mono.Linker } if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)) { - foreach (var c in typeDefinition.GetConstructorsOnType (filter: m => m.IsPublic && m.Parameters.Count == 0)) + foreach (var c in typeDefinition.GetConstructorsOnType (filter: m => m.IsPublic && !m.HasMetadataParameters ())) yield return c; } diff --git a/src/linker/Linker.Dataflow/FlowAnnotations.cs b/src/linker/Linker.Dataflow/FlowAnnotations.cs index e14b1928c..183e55ce1 100644 --- a/src/linker/Linker.Dataflow/FlowAnnotations.cs +++ b/src/linker/Linker.Dataflow/FlowAnnotations.cs @@ -40,28 +40,11 @@ namespace ILLink.Shared.TrimAnalysis public bool RequiresGenericArgumentDataFlowAnalysis (GenericParameter genericParameter) => GetGenericParameterAnnotation (genericParameter) != DynamicallyAccessedMemberTypes.None; - /// <summary> - /// Retrieves the annotations for the given parameter. - /// </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, SourceParameterIndex parameterIndex) + public DynamicallyAccessedMemberTypes GetParameterAnnotation (ParameterProxy param) { - if (GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation) && + if (GetAnnotations (param.Method.Method.DeclaringType).TryGetAnnotation (param.Method.Method, out var annotation) && annotation.ParameterAnnotations != null) - 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 annotation.ParameterAnnotations[(int) param.Index]; return DynamicallyAccessedMemberTypes.None; } @@ -238,46 +221,29 @@ namespace ILLink.Shared.TrimAnalysis foreach (MethodDefinition method in type.Methods) { DynamicallyAccessedMemberTypes[]? paramAnnotations = null; - // We convert indices from metadata space to IL space here. - // IL space assigns index 0 to the `this` parameter on instance methods. - - - DynamicallyAccessedMemberTypes methodMemberTypes = GetMemberTypesForDynamicallyAccessedMembersAttribute (method); - - int offset; - if (method.HasImplicitThis ()) { - offset = 1; - if (IsTypeInterestingForDataflow (method.DeclaringType)) { - // If there's an annotation on the method itself and it's one of the special types (System.Type for example) - // treat that annotation as annotating the "this" parameter. - if (methodMemberTypes != DynamicallyAccessedMemberTypes.None) { - paramAnnotations = new DynamicallyAccessedMemberTypes[method.Parameters.Count + offset]; - paramAnnotations[0] = methodMemberTypes; - } - } else if (methodMemberTypes != DynamicallyAccessedMemberTypes.None) { - _context.LogWarning (method, DiagnosticId.DynamicallyAccessedMembersIsNotAllowedOnMethods); - } - } else { - offset = 0; - if (methodMemberTypes != DynamicallyAccessedMemberTypes.None) { - _context.LogWarning (method, DiagnosticId.DynamicallyAccessedMembersIsNotAllowedOnMethods); - } + // Warn if there is an annotation on a method without a `this` parameter -- we won't catch it in the for loop if there's no parameters + if (GetMemberTypesForDynamicallyAccessedMembersAttribute (method) != DynamicallyAccessedMemberTypes.None + && !method.HasImplicitThis ()) { + _context.LogWarning (method, DiagnosticId.DynamicallyAccessedMembersIsNotAllowedOnMethods); } - for (int i = 0; i < method.Parameters.Count; i++) { - var methodParameter = method.Parameters[i]; - DynamicallyAccessedMemberTypes pa = GetMemberTypesForDynamicallyAccessedMembersAttribute (method, providerIfNotMember: methodParameter); + // We convert indices from metadata space to IL space here. + // IL space assigns index 0 to the `this` parameter on instance methods. + foreach (var param in method.GetParameters ()) { + DynamicallyAccessedMemberTypes pa = GetMemberTypesForDynamicallyAccessedMembersAttribute (method, param.GetCustomAttributeProvider ()); if (pa == DynamicallyAccessedMemberTypes.None) continue; - if (!IsTypeInterestingForDataflow (methodParameter.ParameterType)) { - _context.LogWarning (method, DiagnosticId.DynamicallyAccessedMembersOnMethodParameterCanOnlyApplyToTypesOrStrings, - DiagnosticUtilities.GetParameterNameForErrorMessage (methodParameter), DiagnosticUtilities.GetMethodSignatureDisplayName (methodParameter.Method)); + if (!IsTypeInterestingForDataflow (param.ParameterType)) { + if (param.IsImplicitThis) + _context.LogWarning (method, DiagnosticId.DynamicallyAccessedMembersIsNotAllowedOnMethods); + else + _context.LogWarning (method, DiagnosticId.DynamicallyAccessedMembersOnMethodParameterCanOnlyApplyToTypesOrStrings, + param.GetDisplayName (), DiagnosticUtilities.GetMethodSignatureDisplayName (method)); continue; } - - paramAnnotations ??= new DynamicallyAccessedMemberTypes[method.Parameters.Count + offset]; - paramAnnotations[i + offset] = pa; + paramAnnotations ??= new DynamicallyAccessedMemberTypes[method.GetParametersCount ()]; + paramAnnotations[(int) param.Index] = pa; } DynamicallyAccessedMemberTypes returnAnnotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (method, providerIfNotMember: method.MethodReturnType); @@ -353,13 +319,12 @@ namespace ILLink.Shared.TrimAnalysis if (setterAnnotation?.ParameterAnnotations?[^1] is not (null or DynamicallyAccessedMemberTypes.None)) { _context.LogWarning (setMethod, DiagnosticId.DynamicallyAccessedMembersConflictsBetweenPropertyAndAccessor, property.GetDisplayName (), setMethod.GetDisplayName ()); } else { - int offset = setMethod.HasImplicitThis () ? 1 : 0; if (setterAnnotation is not null) annotatedMethods.Remove (setterAnnotation.Value); DynamicallyAccessedMemberTypes[] paramAnnotations; if (setterAnnotation?.ParameterAnnotations is null) - paramAnnotations = new DynamicallyAccessedMemberTypes[setMethod.Parameters.Count + offset]; + paramAnnotations = new DynamicallyAccessedMemberTypes[setMethod.GetParametersCount ()]; else paramAnnotations = setterAnnotation.Value.ParameterAnnotations; @@ -519,8 +484,8 @@ namespace ILLink.Shared.TrimAnalysis for (int parameterIndex = 0; parameterIndex < methodAnnotations.ParameterAnnotations.Length; parameterIndex++) { if (methodAnnotations.ParameterAnnotations[parameterIndex] != baseMethodAnnotations.ParameterAnnotations[parameterIndex]) LogValidationWarning ( - DiagnosticUtilities.GetMethodParameterFromIndex (method, parameterIndex), - DiagnosticUtilities.GetMethodParameterFromIndex (baseMethod, parameterIndex), + method.TryGetParameter ((ParameterIndex) parameterIndex)?.GetCustomAttributeProvider ()!, + baseMethod.TryGetParameter ((ParameterIndex) parameterIndex)?.GetCustomAttributeProvider ()!, method); } } @@ -553,8 +518,8 @@ namespace ILLink.Shared.TrimAnalysis var annotation = parameterAnnotations[parameterIndex]; if (annotation != DynamicallyAccessedMemberTypes.None) LogValidationWarning ( - DiagnosticUtilities.GetMethodParameterFromIndex (method, parameterIndex), - DiagnosticUtilities.GetMethodParameterFromIndex (baseMethod, parameterIndex), + method.GetParameter ((ParameterIndex) parameterIndex).GetCustomAttributeProvider ()!, + baseMethod.GetParameter ((ParameterIndex) parameterIndex).GetCustomAttributeProvider ()!, origin); } } @@ -729,19 +694,33 @@ namespace ILLink.Shared.TrimAnalysis internal partial GenericParameterValue GetGenericParameterValue (GenericParameterProxy genericParameter) => new GenericParameterValue (genericParameter.GenericParameter, GetGenericParameterAnnotation (genericParameter.GenericParameter)); -#pragma warning disable CA1822 // Mark members as static - keep this an instance method for consistency with the others - internal partial MethodThisParameterValue GetMethodThisParameterValue (MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - => new MethodThisParameterValue (method.Method, dynamicallyAccessedMemberTypes); -#pragma warning restore CA1822 + internal partial MethodParameterValue GetMethodParameterValue (ParameterProxy param, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => new (param.ParameterType.ResolveToTypeDefinition (_context), param, dynamicallyAccessedMemberTypes); + + internal partial MethodParameterValue GetMethodParameterValue (ParameterProxy param) + => GetMethodParameterValue (param, GetParameterAnnotation (param)); - internal partial MethodThisParameterValue GetMethodThisParameterValue (MethodProxy method) - => GetMethodThisParameterValue (method, GetThisParameterAnnotation (method.Method)); +#pragma warning disable CA1822 // Mark members as static - Should be an instance method for consistency + // overrideIsThis is needed for backwards compatibility with MakeGenericType/Method https://github.com/dotnet/linker/issues/2428 + internal MethodParameterValue GetMethodThisParameterValue (MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, bool overrideIsThis = false) + { + if (!method.HasImplicitThis () && !overrideIsThis) + throw new InvalidOperationException ($"Cannot get 'this' parameter of method {method.GetDisplayName ()} with no 'this' parameter."); + return new MethodParameterValue (method.Method.DeclaringType, new ParameterProxy (method, (ParameterIndex) 0), dynamicallyAccessedMemberTypes, overrideIsThis); + } +#pragma warning restore CA1822 // Mark members as static - 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 GetMethodThisParameterValue (MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => GetMethodThisParameterValue (method, dynamicallyAccessedMemberTypes, false); - internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, SourceParameterIndex parameterIndex) - => GetMethodParameterValue (method, parameterIndex, GetParameterAnnotation (method.Method, parameterIndex)); + internal partial MethodParameterValue GetMethodThisParameterValue (MethodProxy method) + { + if (!method.HasImplicitThis ()) + throw new InvalidOperationException ($"Cannot get 'this' parameter of method {method.GetDisplayName ()} with no 'this' parameter."); + ParameterProxy param = new (method, (ParameterIndex) 0); + var damt = GetParameterAnnotation (param); + return GetMethodParameterValue (new ParameterProxy (method, (ParameterIndex) 0), damt); + } // Linker-specific dataflow value creation. Eventually more of these should be shared. internal SingleValue GetFieldValue (FieldDefinition field) diff --git a/src/linker/Linker.Dataflow/HandleCallAction.cs b/src/linker/Linker.Dataflow/HandleCallAction.cs index 69641ecc6..4b045a705 100644 --- a/src/linker/Linker.Dataflow/HandleCallAction.cs +++ b/src/linker/Linker.Dataflow/HandleCallAction.cs @@ -106,10 +106,10 @@ namespace ILLink.Shared.TrimAnalysis => _reflectionMarker.MarkPropertiesOnTypeHierarchy (_diagnosticContext.Origin, type.Type, p => p.Name == name, bindingFlags); private partial void MarkPublicParameterlessConstructorOnType (TypeProxy type) - => _reflectionMarker.MarkConstructorsOnType (_diagnosticContext.Origin, type.Type, m => m.IsPublic && m.Parameters.Count == 0); + => _reflectionMarker.MarkConstructorsOnType (_diagnosticContext.Origin, type.Type, m => m.IsPublic && m.GetMetadataParametersCount () == 0); private partial void MarkConstructorsOnType (TypeProxy type, BindingFlags? bindingFlags, int? parameterCount) - => _reflectionMarker.MarkConstructorsOnType (_diagnosticContext.Origin, type.Type, parameterCount == null ? null : m => m.Parameters.Count == parameterCount, bindingFlags); + => _reflectionMarker.MarkConstructorsOnType (_diagnosticContext.Origin, type.Type, (parameterCount == null) ? null : m => m.GetMetadataParametersCount () == parameterCount, bindingFlags); private partial void MarkMethod (MethodProxy method) => _reflectionMarker.MarkMethod (_diagnosticContext.Origin, method.Method); diff --git a/src/linker/Linker.Dataflow/MethodBodyScanner.cs b/src/linker/Linker.Dataflow/MethodBodyScanner.cs index 0216252cc..bc729794e 100644 --- a/src/linker/Linker.Dataflow/MethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/MethodBodyScanner.cs @@ -12,7 +12,6 @@ using ILLink.Shared.TypeSystemProxy; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; -using static Mono.Linker.ParameterHelpers; using LocalVariableStore = System.Collections.Generic.Dictionary< Mono.Cecil.Cil.VariableDefinition, Mono.Linker.Dataflow.ValueBasicBlockPair>; @@ -716,35 +715,24 @@ namespace Mono.Linker.Dataflow } } - protected abstract SingleValue GetMethodParameterValue (MethodDefinition method, SourceParameterIndex parameterIndex); - - protected abstract SingleValue GetMethodThisParameterValue (MethodDefinition method); + protected abstract SingleValue GetMethodParameterValue (ParameterProxy parameter); private void ScanLdarg (Instruction operation, Stack<StackSlot> currentStack, MethodDefinition thisMethod) { Code code = operation.OpCode.Code; - bool isByRef = code == Code.Ldarga || code == Code.Ldarga_S; - 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 |= 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"); - } + ParameterIndex paramNum = ParameterHelpers.GetParameterIndex (thisMethod, operation); + ParameterProxy param = thisMethod.GetParameter (paramNum); + TypeReference paramType = param.ParameterType; + + bool isByRef = code == Code.Ldarga || code == Code.Ldarga_S; + isByRef |= paramType.IsByRefOrPointer (); + isByRef |= param.IsImplicitThis == true && paramType.IsValueType; - StackSlot slot = new StackSlot (valueToPush); + StackSlot slot = new StackSlot ( + isByRef + ? new ParameterReferenceValue (param) + : GetMethodParameterValue (param)); currentStack.Push (slot); } @@ -755,18 +743,13 @@ namespace Mono.Linker.Dataflow MethodBody methodBody) { var valueToStore = PopUnknown (currentStack, 1, methodBody, operation.Offset); - 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; + ParameterIndex paramNum = ParameterHelpers.GetParameterIndex (thisMethod, operation); + ParameterProxy param = new (thisMethod, paramNum); + var targetValue = GetMethodParameterValue (param); + if (targetValue is MethodParameterValue targetParameterValue) + HandleStoreParameter (thisMethod, targetParameterValue, operation, valueToStore.Value); + // 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 ( @@ -883,13 +866,9 @@ namespace Mono.Linker.Dataflow HandleStoreField (method, fieldValue, operation, source); break; case ParameterReferenceValue parameterReference - when GetMethodParameterValue (parameterReference.MethodDefinition, parameterReference.ParameterIndex) is MethodParameterValue parameterValue: + when GetMethodParameterValue (parameterReference.Parameter) is MethodParameterValue parameterValue: HandleStoreParameter (method, parameterValue, operation, source); break; - case ThisParameterReferenceValue parameterReference - when GetMethodThisParameterValue (parameterReference.MethodDefinition) is MethodThisParameterValue thisParameterValue: - HandleStoreMethodThisParameter (method, thisParameterValue, operation, source); - break; case MethodReturnValue methodReturnValue: // Ref returns don't have special ReferenceValue values, so assume if the target here is a MethodReturnValue then it must be a ref return value HandleStoreMethodReturnValue (method, methodReturnValue, operation, source); @@ -953,10 +932,6 @@ namespace Mono.Linker.Dataflow { } - protected virtual void HandleStoreMethodThisParameter (MethodDefinition method, MethodThisParameterValue thisParameter, Instruction operation, MultiValue sourceValue) - { - } - protected virtual void HandleStoreMethodReturnValue (MethodDefinition method, MethodReturnValue thisParameter, Instruction operation, MultiValue sourceValue) { } @@ -1017,7 +992,7 @@ namespace Mono.Linker.Dataflow int countToPop = 0; if (!isNewObj && methodCalled.HasThis && !methodCalled.ExplicitThis) countToPop++; - countToPop += methodCalled.Parameters.Count; + countToPop += methodCalled.GetMetadataParametersCount (); ValueNodeList methodParams = new ValueNodeList (countToPop); for (int iParam = 0; iParam < countToPop; ++iParam) { @@ -1048,7 +1023,7 @@ namespace Mono.Linker.Dataflow case ParameterReferenceValue parameterReferenceValue: dereferencedValue = MultiValue.Meet ( dereferencedValue, - GetMethodParameterValue (parameterReferenceValue.MethodDefinition, parameterReferenceValue.ParameterIndex)); + GetMethodParameterValue (parameterReferenceValue.Parameter)); break; case LocalVariableReferenceValue localVariableReferenceValue: if (locals.TryGetValue (localVariableReferenceValue.LocalDefinition, out var valueBasicBlockPair)) @@ -1056,11 +1031,6 @@ 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 @@ -1087,18 +1057,22 @@ namespace Mono.Linker.Dataflow int curBasicBlock, ref InterproceduralState ipState) { - MethodDefinition? calledMethodDefinition = _context.Resolve (calledMethod); - bool methodIsResolved = calledMethodDefinition is not null; - 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 && (int) parameterIndex < calledMethodDefinition!.Parameters.Count - ? _context.Annotations.FlowAnnotations.GetMethodParameterValue (calledMethodDefinition!, parameterIndex) - : UnknownValue.Instance; - StoreInReference (methodArguments[(int) ilArgumentIndex], newByRefValue, callingMethodBody.Method, operation, locals, curBasicBlock, ref ipState); + if (_context.TryResolve (calledMethod) is MethodDefinition calledMethodDefinition) { + // We resolved the method and can put the ref/out values into the arguments + foreach (var parameter in calledMethodDefinition.GetParameters ()) { + if (parameter.GetReferenceKind () is not (ReferenceKind.Ref or ReferenceKind.Out)) + continue; + var newByRefValue = _context.Annotations.FlowAnnotations.GetMethodParameterValue (parameter); + StoreInReference (methodArguments[(int) parameter.Index], newByRefValue, callingMethodBody.Method, operation, locals, curBasicBlock, ref ipState); + } + } else { + // We couldn't resolve the method, so we put unknown values into the ref and out arguments + // Should be a very cold path, so using Linq.Zip should be okay + foreach (var (argument, refKind) in methodArguments.Zip (calledMethod.GetParameterReferenceKinds ())) { + if (refKind is not (ReferenceKind.Ref or ReferenceKind.Out)) + continue; + StoreInReference (argument, UnknownValue.Instance, callingMethodBody.Method, operation, locals, curBasicBlock, ref ipState); + } } } diff --git a/src/linker/Linker.Dataflow/MethodParameterValue.cs b/src/linker/Linker.Dataflow/MethodParameterValue.cs index 325b02852..8fac8fe64 100644 --- a/src/linker/Linker.Dataflow/MethodParameterValue.cs +++ b/src/linker/Linker.Dataflow/MethodParameterValue.cs @@ -1,10 +1,8 @@ // 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.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using ILLink.Shared.DataFlow; -using Mono.Cecil; +using ILLink.Shared.TypeSystemProxy; using Mono.Linker.Dataflow; using TypeDefinition = Mono.Cecil.TypeDefinition; @@ -17,33 +15,16 @@ namespace ILLink.Shared.TrimAnalysis /// </summary> partial record MethodParameterValue : IValueWithStaticType { - public MethodParameterValue (TypeDefinition? staticType, MethodDefinition method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + public MethodParameterValue (TypeDefinition? staticType, ParameterProxy param, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, bool overrideIsThis = false) { StaticType = staticType; - Method = method; - ParameterIndex = parameterIndex; DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + Parameter = param; + _overrideIsThis = overrideIsThis; } - public readonly MethodDefinition Method; - - /// <summary> - /// This is the index of non-implicit parameter - so the index into MethodDefinition.Parameters array. - /// It's NOT the IL parameter index which could be offset by 1 if the method has an implicit this. - /// </summary> - public readonly int ParameterIndex; - - public ParameterDefinition ParameterDefinition => Method.Parameters[ParameterIndex]; - public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } - public override IEnumerable<string> GetDiagnosticArgumentsForAnnotationMismatch () - => new string[] { DiagnosticUtilities.GetParameterNameForErrorMessage (ParameterDefinition), DiagnosticUtilities.GetMethodSignatureDisplayName (Method) }; - public TypeDefinition? StaticType { get; } - - public override SingleValue DeepCopy () => this; // This value is immutable - - public override string ToString () => this.ValueToString (Method, ParameterIndex, DynamicallyAccessedMemberTypes); } }
\ No newline at end of file diff --git a/src/linker/Linker.Dataflow/MethodProxy.cs b/src/linker/Linker.Dataflow/MethodProxy.cs index 6821613c4..421db0a80 100644 --- a/src/linker/Linker.Dataflow/MethodProxy.cs +++ b/src/linker/Linker.Dataflow/MethodProxy.cs @@ -22,13 +22,25 @@ namespace ILLink.Shared.TypeSystemProxy internal partial bool IsDeclaredOnType (string fullTypeName) => Method.IsDeclaredOnType (fullTypeName); - internal partial bool HasParameters () => Method.HasParameters; + internal partial bool HasMetadataParameters () => Method.HasMetadataParameters (); - internal partial int GetParametersCount () => Method.Parameters.Count; + /// <summary> + /// Gets the number of entries in the 'Parameters' section of a method's metadata (i.e. excludes the implicit 'this' from the count) + /// </summary> + internal partial int GetMetadataParametersCount () => Method.GetMetadataParametersCount (); - internal partial bool HasParameterOfType (int parameterIndex, string fullTypeName) => Method.HasParameterOfType (parameterIndex, fullTypeName); + /// <summary> + /// Returns the number of parameters that are passed to the method in IL (including the implicit 'this' parameter). + /// In pseudocode: <code>method.HasImplicitThis() ? 1 + MetadataParametersCount : MetadataParametersCount;</code> + /// </summary> + internal partial int GetParametersCount () => Method.GetParametersCount (); - internal partial string GetParameterDisplayName (int parameterIndex) => Method.Parameters[parameterIndex].Name; + /// <summary> + /// Use only when iterating over all parameters. When wanting to index, use GetParameters(ParameterIndex) + /// </summary> + internal partial ParameterProxyEnumerable GetParameters () => Method.GetParameters (); + + internal partial ParameterProxy GetParameter (ParameterIndex index) => Method.GetParameter (index); internal partial bool HasGenericParameters () => Method.HasGenericParameters; @@ -49,12 +61,12 @@ namespace ILLink.Shared.TypeSystemProxy internal partial bool IsStatic () => Method.IsStatic; + internal partial bool HasImplicitThis () => Method.HasImplicitThis (); + internal partial bool ReturnsVoid () => Method.ReturnsVoid (); public override string ToString () => Method.ToString (); - public ReferenceKind ParameterReferenceKind (int index) => Method.HasImplicitThis () ? Method.ParameterReferenceKind (index + 1) : Method.ParameterReferenceKind (index); - public bool Equals (MethodProxy other) => Method.Equals (other.Method); public override bool Equals (object? obj) => obj is MethodProxy other && Equals (other); diff --git a/src/linker/Linker.Dataflow/MethodThisParameterValue.cs b/src/linker/Linker.Dataflow/MethodThisParameterValue.cs deleted file mode 100644 index c2f46d599..000000000 --- a/src/linker/Linker.Dataflow/MethodThisParameterValue.cs +++ /dev/null @@ -1,40 +0,0 @@ -// 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.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using ILLink.Shared.DataFlow; -using Mono.Cecil; -using Mono.Linker; -using Mono.Linker.Dataflow; -using TypeDefinition = Mono.Cecil.TypeDefinition; - - -namespace ILLink.Shared.TrimAnalysis -{ - - /// <summary> - /// A value that came from the implicit this parameter of a method - /// </summary> - partial record MethodThisParameterValue : IValueWithStaticType - { - public MethodThisParameterValue (MethodDefinition method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - { - Method = method; - DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; - } - - public readonly MethodDefinition Method; - - public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } - - public override IEnumerable<string> GetDiagnosticArgumentsForAnnotationMismatch () - => new string[] { Method.GetDisplayName () }; - - public TypeDefinition? StaticType => Method.DeclaringType; - - public override SingleValue DeepCopy () => this; // This value is immutable - - public override string ToString () => this.ValueToString (Method, DynamicallyAccessedMemberTypes); - } -}
\ No newline at end of file diff --git a/src/linker/Linker.Dataflow/ParameterProxy.cs b/src/linker/Linker.Dataflow/ParameterProxy.cs new file mode 100644 index 000000000..bbcb230a4 --- /dev/null +++ b/src/linker/Linker.Dataflow/ParameterProxy.cs @@ -0,0 +1,56 @@ +// 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 Mono.Cecil; +using Mono.Linker; + +namespace ILLink.Shared.TypeSystemProxy +{ + internal partial struct ParameterProxy + { + public partial ReferenceKind GetReferenceKind () + { + if (IsImplicitThis) + return Method.Method.DeclaringType.IsValueType ? ReferenceKind.Ref : ReferenceKind.None; +#pragma warning disable RS0030 // MethodReference.Parameters is banned -- this class provides wrappers to use + var param = Method.Method.Parameters[MetadataIndex]; +#pragma warning restore RS0030 // Do not used banned APIs + if (!param.ParameterType.IsByReference) + return ReferenceKind.None; + if (param.IsIn) + return ReferenceKind.In; + if (param.IsOut) + return ReferenceKind.Out; + return ReferenceKind.Ref; + } + + public TypeReference ParameterType { + get { + if (IsImplicitThis) + return Method.Method.DeclaringType; +#pragma warning disable RS0030 // MethodReference.Parameters is banned -- this class provides wrappers to use + return Method.Method.Parameters[MetadataIndex].ParameterType; +#pragma warning restore RS0030 // Do not used banned APIs + } + } + +#pragma warning disable RS0030 // MethodReference.Parameters is banned -- this class provides wrappers to use + public partial string GetDisplayName () => IsImplicitThis ? "this" + : !string.IsNullOrEmpty (Method.Method.Parameters[MetadataIndex].Name) ? Method.Method.Parameters[MetadataIndex].Name + : $"#{Index}"; +#pragma warning restore RS0030 // Do not used banned APIs + + public ICustomAttributeProvider GetCustomAttributeProvider () + { + if (IsImplicitThis) + return Method.Method; +#pragma warning disable RS0030 // MethodReference.Parameters is banned -- this class provides wrappers to use + return Method.Method.Parameters[MetadataIndex]; +#pragma warning restore RS0030 // Do not used banned APIs + } + + public partial bool IsTypeOf (string typeName) => ParameterType.IsTypeOf (typeName); + + public bool IsTypeOf (WellKnownType type) => ParameterType.IsTypeOf (type); + } +} diff --git a/src/linker/Linker.Dataflow/ParameterReferenceValue.cs b/src/linker/Linker.Dataflow/ParameterReferenceValue.cs index 853b31664..c6ef6ec7e 100644 --- a/src/linker/Linker.Dataflow/ParameterReferenceValue.cs +++ b/src/linker/Linker.Dataflow/ParameterReferenceValue.cs @@ -1,12 +1,12 @@ // 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; +using ILLink.Shared.TypeSystemProxy; namespace ILLink.Shared.TrimAnalysis { - public partial record ParameterReferenceValue (MethodDefinition MethodDefinition, SourceParameterIndex ParameterIndex) - : ReferenceValue (MethodDefinition.Parameters[(int) ParameterIndex].ParameterType) + internal sealed partial record ParameterReferenceValue (ParameterProxy Parameter) + : ReferenceValue (Parameter.ParameterType) { public override SingleValue DeepCopy () { diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index 15c5f83d8..f47941755 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -93,16 +93,11 @@ namespace Mono.Linker.Dataflow Debug.Fail ("Invalid IL or a bug in the scanner"); } - protected override ValueWithDynamicallyAccessedMembers GetMethodThisParameterValue (MethodDefinition method) - => _annotations.GetMethodThisParameterValue (method); + protected override ValueWithDynamicallyAccessedMembers GetMethodParameterValue (ParameterProxy parameter) + => GetMethodParameterValue (parameter, _context.Annotations.FlowAnnotations.GetParameterAnnotation (parameter)); - 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); - } + MethodParameterValue GetMethodParameterValue (ParameterProxy parameter, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => _annotations.GetMethodParameterValue (parameter, dynamicallyAccessedMemberTypes); protected override MultiValue GetFieldValue (FieldDefinition field) => _annotations.GetFieldValue (field); @@ -120,9 +115,6 @@ namespace Mono.Linker.Dataflow protected override void HandleStoreParameter (MethodDefinition method, MethodParameterValue parameter, Instruction operation, MultiValue valueToStore) => HandleStoreValueWithDynamicallyAccessedMembers (parameter, operation, valueToStore); - protected override void HandleStoreMethodThisParameter (MethodDefinition method, MethodThisParameterValue thisParameter, Instruction operation, MultiValue valueToStore) - => HandleStoreValueWithDynamicallyAccessedMembers (thisParameter, operation, valueToStore); - protected override void HandleStoreMethodReturnValue (MethodDefinition method, MethodReturnValue returnValue, Instruction operation, MultiValue valueToStore) => HandleStoreValueWithDynamicallyAccessedMembers (returnValue, operation, valueToStore); @@ -446,9 +438,11 @@ namespace Mono.Linker.Dataflow private static bool ComDangerousMethod (MethodDefinition methodDefinition, LinkContext context) { bool comDangerousMethod = IsComInterop (methodDefinition.MethodReturnType, methodDefinition.ReturnType, context); +#pragma warning disable RS0030 // MethodDefinition.Parameters is banned. Here we iterate through the parameters and don't need to worry about the 'this' parameter. foreach (ParameterDefinition pd in methodDefinition.Parameters) { comDangerousMethod |= IsComInterop (pd, pd.ParameterType, context); } +#pragma warning restore RS0030 return comDangerousMethod; } diff --git a/src/linker/Linker.Dataflow/SingleValueExtensions.cs b/src/linker/Linker.Dataflow/SingleValueExtensions.cs index 9dcb0df70..2f976cd4a 100644 --- a/src/linker/Linker.Dataflow/SingleValueExtensions.cs +++ b/src/linker/Linker.Dataflow/SingleValueExtensions.cs @@ -44,7 +44,6 @@ namespace ILLink.Shared.TrimAnalysis case KnownStringValue: case ConstIntValue: case MethodParameterValue: - case MethodThisParameterValue: case MethodReturnValue: case GenericParameterValue: case RuntimeTypeHandleForGenericParameterValue: diff --git a/src/linker/Linker.Dataflow/ThisParameterReferenceValue.cs b/src/linker/Linker.Dataflow/ThisParameterReferenceValue.cs deleted file mode 100644 index 727181fbd..000000000 --- a/src/linker/Linker.Dataflow/ThisParameterReferenceValue.cs +++ /dev/null @@ -1,16 +0,0 @@ -// 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.Steps/AddBypassNGenStep.cs b/src/linker/Linker.Steps/AddBypassNGenStep.cs index e8706e1d6..20d11f64d 100644 --- a/src/linker/Linker.Steps/AddBypassNGenStep.cs +++ b/src/linker/Linker.Steps/AddBypassNGenStep.cs @@ -102,7 +102,7 @@ namespace Mono.Linker.Steps bypassNGenAttributeDef.Methods.Add (bypassNGenAttributeDefaultConstructor); } else { foreach (MethodDefinition method in bypassNGenAttributeDef.Methods) { - if (method.IsConstructor && !method.IsStatic && !method.HasParameters) { + if (method.IsConstructor && !method.IsStatic && !method.HasMetadataParameters ()) { bypassNGenAttributeDefaultConstructor = method; break; } diff --git a/src/linker/Linker.Steps/CodeRewriterStep.cs b/src/linker/Linker.Steps/CodeRewriterStep.cs index c71624451..6d70b1a09 100644 --- a/src/linker/Linker.Steps/CodeRewriterStep.cs +++ b/src/linker/Linker.Steps/CodeRewriterStep.cs @@ -157,8 +157,10 @@ namespace Mono.Linker.Steps { var body = new MethodBody (method); +#pragma warning disable RS0030 // MethodReference.Parameters is banned. This code already works and doesn't need to be changed if (method.HasParameters && method.Parameters.Any (l => l.IsOut)) throw new NotSupportedException ($"Cannot replace body of method '{method.GetDisplayName ()}' because it has an out parameter."); +#pragma warning restore RS0030 var il = body.GetLinkerILProcessor (); if (method.IsInstanceConstructor () && !method.DeclaringType.IsValueType) { diff --git a/src/linker/Linker.Steps/DescriptorMarker.cs b/src/linker/Linker.Steps/DescriptorMarker.cs index d48fac85d..ebacbf819 100644 --- a/src/linker/Linker.Steps/DescriptorMarker.cs +++ b/src/linker/Linker.Steps/DescriptorMarker.cs @@ -198,12 +198,12 @@ namespace Mono.Linker.Steps } sb.Append ("("); - if (meth.HasParameters) { - for (int i = 0; i < meth.Parameters.Count; i++) { - if (i > 0) + if (meth.HasMetadataParameters ()) { + int i = 0; + foreach (var p in meth.GetMetadataParameters ()) { + if (i++ > 0) sb.Append (","); - - sb.Append (meth.Parameters[i].ParameterType.FullName); + sb.Append (p.ParameterType.FullName); } } sb.Append (")"); diff --git a/src/linker/Linker.Steps/DiscoverCustomOperatorsHandler.cs b/src/linker/Linker.Steps/DiscoverCustomOperatorsHandler.cs index 5811555c6..b6ecb134c 100644 --- a/src/linker/Linker.Steps/DiscoverCustomOperatorsHandler.cs +++ b/src/linker/Linker.Steps/DiscoverCustomOperatorsHandler.cs @@ -164,7 +164,7 @@ namespace Mono.Linker.Steps case "True": case "False": // Parameter type of a unary operator must be the declaring type - if (method.Parameters.Count != 1 || NonNullableType (method.Parameters[0].ParameterType) != self) + if (method.GetMetadataParametersCount () != 1 || NonNullableType (method.GetParameter ((ParameterIndex) 0).ParameterType) != self) return false; // ++ and -- must return the declaring type if (operatorName is "Increment" or "Decrement" && NonNullableType (method.ReturnType) != self) @@ -187,10 +187,10 @@ namespace Mono.Linker.Steps case "GreaterThan": case "LessThanOrEqual": case "GreaterThanOrEqual": - if (method.Parameters.Count != 2) + if (method.GetMetadataParametersCount () != 2) return false; - var nnLeft = NonNullableType (method.Parameters[0].ParameterType); - var nnRight = NonNullableType (method.Parameters[1].ParameterType); + var nnLeft = NonNullableType (method.GetParameter ((ParameterIndex) 0).ParameterType); + var nnRight = NonNullableType (method.GetParameter ((ParameterIndex) 1).ParameterType); if (nnLeft == null || nnRight == null) return false; // << and >> must take the declaring type and int @@ -207,9 +207,9 @@ namespace Mono.Linker.Steps // Conversion operators case "Implicit": case "Explicit": - if (method.Parameters.Count != 1) + if (method.GetMetadataParametersCount () != 1) return false; - var nnSource = NonNullableType (method.Parameters[0].ParameterType); + var nnSource = NonNullableType (method.GetParameter ((ParameterIndex) 0).ParameterType); var nnTarget = NonNullableType (method.ReturnType); // Exactly one of source/target must be the declaring type if (nnSource == self == (nnTarget == self)) diff --git a/src/linker/Linker.Steps/LinkAttributesParser.cs b/src/linker/Linker.Steps/LinkAttributesParser.cs index 81b89e292..6de2fde31 100644 --- a/src/linker/Linker.Steps/LinkAttributesParser.cs +++ b/src/linker/Linker.Steps/LinkAttributesParser.cs @@ -145,7 +145,9 @@ namespace Mono.Linker.Steps var ctorN = new MethodDefinition (".ctor", ctorAttributes, voidType); var paramN = new ParameterDefinition (objectArrayType); +#pragma warning disable RS0030 // MethodReference.Parameters is banned. It's necessary to build the method definition here, though. ctorN.Parameters.Add (paramN); +#pragma warning restore RS0030 td.Methods.Add (ctorN); return _context.MarkedKnownMembers.RemoveAttributeInstancesAttributeDefinition = td; @@ -178,17 +180,16 @@ namespace Mono.Linker.Steps if (!method.IsInstanceConstructor ()) continue; - var parameters = method.Parameters; - if (args.Length != parameters.Count) + if (args.Length != method.GetMetadataParametersCount ()) continue; bool match = true; - for (int ii = 0; match && ii < args.Length; ++ii) { + foreach (var p in method.GetMetadataParameters ()) { // // No candidates betterness, only exact matches are supported // - var parameterType = _context.TryResolve (parameters[ii].ParameterType); - if (parameterType == null || parameterType != _context.TryResolve (args[ii].Type)) + var parameterType = _context.TryResolve (p.ParameterType); + if (parameterType == null || parameterType != _context.TryResolve (args[p.MetadataIndex].Type)) match = false; } @@ -496,6 +497,7 @@ namespace Mono.Linker.Steps var (attributes, origins) = ProcessAttributes (parameterNav, method); if (attributes != null && origins != null) { string paramName = GetAttribute (parameterNav, "name"); +#pragma warning disable RS0030 // MethodReference.Parameters is banned. It's easiest to leave existing code as is foreach (ParameterDefinition parameter in method.Parameters) { if (paramName == parameter.Name) { if (parameter.HasCustomAttributes || _attributeInfo.CustomAttributes.ContainsKey (parameter)) @@ -504,6 +506,7 @@ namespace Mono.Linker.Steps break; } } +#pragma warning restore RS0030 } } } @@ -531,6 +534,7 @@ namespace Mono.Linker.Steps return null; } +#pragma warning disable RS0030 // MethdReference.Parameters is banned. It's easiest to leave existing code as is. static string GetMethodSignature (MethodDefinition method, bool includeReturnType = false) { StringBuilder sb = new StringBuilder (); @@ -549,7 +553,7 @@ namespace Mono.Linker.Steps sb.Append (">"); } sb.Append ("("); - if (method.HasParameters) { + if (method.HasMetadataParameters ()) { for (int i = 0; i < method.Parameters.Count; i++) { if (i > 0) sb.Append (","); @@ -560,6 +564,7 @@ namespace Mono.Linker.Steps sb.Append (")"); return sb.ToString (); } +#pragma warning restore RS0030 protected override void ProcessProperty (TypeDefinition type, PropertyDefinition property, XPathNavigator nav, object? customData, bool fromSignature) { diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index 5d4065d35..71b6e48d4 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -1082,19 +1082,18 @@ namespace Mono.Linker.Steps continue; } - var mp = m.Parameters; - if (mp.Count != signature.Length) + if (m.GetMetadataParametersCount () != signature.Length) continue; - int i = 0; - for (; i < signature.Length; ++i) { - if (mp[i].ParameterType.FullName != signature[i].Trim ().ToCecilName ()) { - i = -1; + bool matched = true; + foreach (var p in m.GetMetadataParameters ()) { + if (p.ParameterType.FullName != signature[p.MetadataIndex].Trim ().ToCecilName ()) { + matched = false; break; } } - if (i < 0) + if (!matched) continue; MarkIndirectlyCalledMethod (m, reason, ScopeStack.CurrentScope.Origin); @@ -1316,7 +1315,7 @@ namespace Mono.Linker.Steps { TypeDefinition? type = inputType; while (type != null) { - MethodDefinition? method = type.Methods.FirstOrDefault (m => m.Name == methodname && !m.HasParameters); + MethodDefinition? method = type.Methods.FirstOrDefault (m => m.Name == methodname && !m.HasMetadataParameters ()); if (method != null) return method; @@ -2477,12 +2476,11 @@ namespace Mono.Linker.Steps if (!method.IsInstanceConstructor ()) return false; - var parameters = method.Parameters; - if (parameters.Count != 2) + if (method.GetMetadataParametersCount () != 2) return false; - return parameters[0].ParameterType.Name == "SerializationInfo" && - parameters[1].ParameterType.Name == "StreamingContext"; + return method.TryGetParameter ((ParameterIndex) 1)?.ParameterType.Name == "SerializationInfo" && + method.TryGetParameter ((ParameterIndex) 2)?.ParameterType.Name == "StreamingContext"; } protected internal bool MarkMethodsIf (Collection<MethodDefinition> methods, Func<MethodDefinition, bool> predicate, in DependencyInfo reason, in MessageOrigin origin) @@ -2521,8 +2519,12 @@ namespace Mono.Linker.Steps if (!type.HasMethods) return; - MarkMethodIf (type.Methods, m => - m.Name == "GetInstance" && m.IsStatic && m.Parameters.Count == 1 && m.Parameters[0].ParameterType.MetadataType == MetadataType.String, + MarkMethodIf (type.Methods, + m => + m.Name == "GetInstance" + && m.IsStatic + && m.GetMetadataParametersCount () == 1 + && m.GetParameter ((ParameterIndex) 0).ParameterType.MetadataType == MetadataType.String, reason, ScopeStack.CurrentScope.Origin); } @@ -3150,12 +3152,14 @@ namespace Mono.Linker.Steps else if (method.TryGetEvent (out EventDefinition? @event)) MarkEvent (@event, new DependencyInfo (DependencyKind.EventOfEventMethod, method)); - if (method.HasParameters) { + if (method.HasMetadataParameters ()) { +#pragma warning disable RS0030 // MethodReference.Parameters is banned. It's easiest to leave the code as is for now foreach (ParameterDefinition pd in method.Parameters) { MarkType (pd.ParameterType, new DependencyInfo (DependencyKind.ParameterType, method)); MarkCustomAttributes (pd, new DependencyInfo (DependencyKind.ParameterAttribute, method)); MarkMarshalSpec (pd, new DependencyInfo (DependencyKind.ParameterMarshalSpec, method)); } +#pragma warning restore RS0030 } if (method.HasOverrides) { @@ -3397,6 +3401,7 @@ namespace Mono.Linker.Steps MarkFields (method.DeclaringType, includeStaticFields, new DependencyInfo (DependencyKind.InteropMethodDependency, method)); } +#pragma warning disable RS0030 // MethodReference.Parameters is banned. It's easiest to leave this code as is for now foreach (ParameterDefinition pd in method.Parameters) { TypeReference paramTypeReference = pd.ParameterType; if (paramTypeReference is TypeSpecification paramTypeSpecification) { @@ -3413,6 +3418,7 @@ namespace Mono.Linker.Steps } } } +#pragma warning restore RS0030 } protected virtual bool ShouldParseMethodBody (MethodDefinition method) diff --git a/src/linker/Linker.Steps/SweepStep.cs b/src/linker/Linker.Steps/SweepStep.cs index de9a4f0bd..a42d74f29 100644 --- a/src/linker/Linker.Steps/SweepStep.cs +++ b/src/linker/Linker.Steps/SweepStep.cs @@ -431,17 +431,19 @@ namespace Mono.Linker.Steps SweepOverrides (method); - if (!method.HasParameters) + if (!method.HasMetadataParameters ()) continue; bool sweepNames = CanSweepNamesForMember (method, MetadataTrimming.ParameterName); +#pragma warning disable RS0030 // MethodReference.Parameters is banned. It makes sense to use when directly working with the Cecil type system though. foreach (var parameter in method.Parameters) { if (sweepNames) parameter.Name = null; SweepCustomAttributes (parameter); } +#pragma warning restore RS0030 } } void SweepOverrides (MethodDefinition method) diff --git a/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs b/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs index d6bc01798..43f59dd25 100644 --- a/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs +++ b/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs @@ -238,7 +238,7 @@ namespace Mono.Linker.Steps MethodResult? value; MethodDefinition method = callee.Method; - if (!method.HasParameters || callee.HasUnknownArguments) { + if (!method.HasMetadataParameters () || callee.HasUnknownArguments) { if (!_cache_method_results.TryGetValue (method, out value) && !IsDeepStack (callStack)) { value = AnalyzeMethodForConstantResult (callee, callStack); _cache_method_results.Add (method, value); @@ -310,21 +310,21 @@ namespace Mono.Linker.Steps static Instruction[]? GetArgumentsOnStack (MethodDefinition method, Collection<Instruction> instructions, int index) { - if (!method.HasParameters) + if (!method.HasMetadataParameters ()) return Array.Empty<Instruction> (); Instruction[]? result = null; - for (int i = method.Parameters.Count, pos = 0; i != 0; --i, ++pos) { + for (int i = method.GetMetadataParametersCount (), pos = 0; i != 0; --i, ++pos) { Instruction instr = instructions[index - i]; if (!IsConstantValue (instr)) return null; - result ??= new Instruction[method.Parameters.Count]; + result ??= new Instruction[method.GetMetadataParametersCount ()]; result[pos] = instr; } - if (result != null && HasJumpIntoTargetRange (instructions, index - method.Parameters.Count + 1, index)) + if (result != null && HasJumpIntoTargetRange (instructions, index - method.GetMetadataParametersCount () + 1, index)) return null; return result; @@ -415,7 +415,7 @@ namespace Mono.Linker.Steps if (type == null) return null; - return type.Methods.First (l => !l.HasParameters && l.IsStatic && l.Name == "get_Size"); + return type.Methods.First (l => !l.HasMetadataParameters () && l.IsStatic && l.Name == "get_Size"); } readonly struct CallInliner @@ -465,7 +465,7 @@ namespace Mono.Linker.Steps } if (!md.IsStatic) { - if (!md.HasParameters && CanInlineInstanceCall (instrs, i)) { + if (!md.HasMetadataParameters () && CanInlineInstanceCall (instrs, i)) { processor.Replace (i - 1, Instruction.Create (OpCodes.Nop)); processor.Replace (i, result.GetPrototype ()!); changed = true; @@ -474,11 +474,11 @@ namespace Mono.Linker.Steps continue; } - if (md.HasParameters) { + if (md.HasMetadataParameters ()) { if (!IsCalledWithoutSideEffects (md, instrs, i)) continue; - for (int p = 1; p <= md.Parameters.Count; ++p) { + for (int p = 1; p <= md.GetMetadataParametersCount (); ++p) { processor.Replace (i - p, Instruction.Create (OpCodes.Nop)); } } @@ -516,7 +516,7 @@ namespace Mono.Linker.Steps static bool IsCalledWithoutSideEffects (MethodDefinition method, Collection<Instruction> instructions, int index) { - for (int i = 1; i <= method.Parameters.Count; ++i) { + for (int i = 1; i <= method.GetMetadataParametersCount (); ++i) { if (!IsSideEffectFreeLoad (instructions[index - i])) return false; } @@ -1655,7 +1655,7 @@ namespace Mono.Linker.Steps return false; Instruction[]? args; - if (!md.HasParameters) { + if (!md.HasMetadataParameters ()) { args = Array.Empty<Instruction> (); } else { // @@ -1816,7 +1816,7 @@ namespace Mono.Linker.Steps Instruction[]? GetArgumentsOnStack (MethodDefinition method) { - int length = method.Parameters.Count; + int length = method.GetMetadataParametersCount (); Debug.Assert (length != 0); if (stack_instr?.Count < length) return null; diff --git a/src/linker/Linker/BCL.cs b/src/linker/Linker/BCL.cs index f3351ca87..a14cbc780 100644 --- a/src/linker/Linker/BCL.cs +++ b/src/linker/Linker/BCL.cs @@ -51,7 +51,7 @@ namespace Mono.Linker if (method.Name != "Dispose" || method.ReturnType.MetadataType != MetadataType.Void) return false; - if (method.HasParameters || method.HasGenericParameters || method.IsStatic) + if (method.HasMetadataParameters () || method.HasGenericParameters || method.IsStatic) return false; if (!method.IsFinal) diff --git a/src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs b/src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs index 0784e23a1..802b119c6 100644 --- a/src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs +++ b/src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs @@ -85,8 +85,10 @@ namespace Mono.Linker if (method.HasGenericParameters) builder.Append ("``").Append (method.GenericParameters.Count); - if (method.HasParameters || (method.CallingConvention == MethodCallingConvention.VarArg)) + if (method.HasMetadataParameters () || (method.CallingConvention == MethodCallingConvention.VarArg)) +#pragma warning disable RS0030 // MethodReference.Parameters is banned. This generates documentation signatures, so it's okay to use it here VisitParameters (method.Parameters, method.CallingConvention == MethodCallingConvention.VarArg, builder, resolver); +#pragma warning restore RS0030 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") { builder.Append ('~'); diff --git a/src/linker/Linker/DocumentationSignatureParser.cs b/src/linker/Linker/DocumentationSignatureParser.cs index 7ca001900..fe599d0ff 100644 --- a/src/linker/Linker/DocumentationSignatureParser.cs +++ b/src/linker/Linker/DocumentationSignatureParser.cs @@ -564,8 +564,10 @@ namespace Mono.Linker if (!isNameOnly || !acceptName) { // check parameters unless we are matching a name only +#pragma warning disable RS0030 // Do not used banned APIs if (!AllParametersMatch (method.Parameters, parameters, resolver)) continue; +#pragma warning restore RS0030 // Do not used banned APIs } results.Add (method); diff --git a/src/linker/Linker/ILParameterIndex.cs b/src/linker/Linker/ILParameterIndex.cs deleted file mode 100644 index 3d9da48f4..000000000 --- a/src/linker/Linker/ILParameterIndex.cs +++ /dev/null @@ -1,22 +0,0 @@ -// 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/KnownMembers.cs b/src/linker/Linker/KnownMembers.cs index 4e4b85c6a..6e2a3777a 100644 --- a/src/linker/Linker/KnownMembers.cs +++ b/src/linker/Linker/KnownMembers.cs @@ -1,6 +1,7 @@ // 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.TypeSystemProxy; using Mono.Cecil; namespace Mono.Linker @@ -15,10 +16,10 @@ namespace Mono.Linker public static bool IsNotSupportedExceptionCtorString (MethodDefinition method) { - if (!method.IsConstructor || method.IsStatic || !method.HasParameters) + if (!method.IsConstructor || method.IsStatic || !method.HasMetadataParameters ()) return false; - if (method.Parameters.Count != 1 || method.Parameters[0].ParameterType.MetadataType != MetadataType.String) + if (method.GetMetadataParametersCount () != 1 || method.GetParameter ((ParameterIndex) 1).ParameterType.MetadataType != MetadataType.String) return false; return true; diff --git a/src/linker/Linker/MethodBodyScanner.cs b/src/linker/Linker/MethodBodyScanner.cs index 318c0930b..8f2aa05a6 100644 --- a/src/linker/Linker/MethodBodyScanner.cs +++ b/src/linker/Linker/MethodBodyScanner.cs @@ -106,8 +106,8 @@ namespace Mono.Linker foreach (VariableDefinition var in body.Variables) AddIfResolved (types, var.VariableType); - foreach (var parameter in body.Method.Parameters) - AddIfResolved (types, parameter.ParameterType); + foreach (var param in method.GetParameters ()) + AddIfResolved (types, param.ParameterType); foreach (ExceptionHandler eh in body.ExceptionHandlers) { if (eh.HandlerType == ExceptionHandlerType.Catch) { @@ -128,8 +128,8 @@ namespace Mono.Linker var resolvedMethod = context.TryResolve (methodReference); if (resolvedMethod != null) { - if (resolvedMethod.HasParameters) { - foreach (var param in resolvedMethod.Parameters) + if (resolvedMethod.HasMetadataParameters ()) { + foreach (var param in resolvedMethod.GetParameters ()) AddIfResolved (types, param.ParameterType); } diff --git a/src/linker/Linker/MethodDefinitionExtensions.cs b/src/linker/Linker/MethodDefinitionExtensions.cs index 814d390d8..28ad55c65 100644 --- a/src/linker/Linker/MethodDefinitionExtensions.cs +++ b/src/linker/Linker/MethodDefinitionExtensions.cs @@ -1,16 +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. +using System; using System.Diagnostics.CodeAnalysis; +using ILLink.Shared.TypeSystemProxy; using Mono.Cecil; namespace Mono.Linker { - public static class MethodDefinitionExtensions + [SuppressMessage ("ApiDesign", "RS0030:Do not used banned APIs", Justification = "This class provides wrapper methods around the banned Parameters property")] + internal static class MethodDefinitionExtensions { public static bool IsDefaultConstructor (this MethodDefinition method) { - return IsInstanceConstructor (method) && !method.HasParameters; + return IsInstanceConstructor (method) && !method.HasMetadataParameters (); } public static bool IsInstanceConstructor (this MethodDefinition method) @@ -98,5 +101,50 @@ namespace Mono.Linker di.Scope = null; } } + + public static bool HasParameterOfType (this MethodDefinition method, ParameterIndex index, string typeName) + => method.TryGetParameter (index)?.ParameterType?.IsTypeOf (typeName) is true; + + /// <summary> + /// Tries to get the <see cref="ParameterProxy"/> representing the parameter at index <paramref name="index"/> of method <paramref name="method"/>. + /// Returns null if <paramref name="index"/> is not a valid parameter index for <paramref name="method"/>. + /// <see cref="GetParameter(MethodDefinition, ParameterIndex)"/> for a non-nullable version if you know the index is valid. + /// </summary> + public static ParameterProxy? TryGetParameter (this MethodDefinition method, ParameterIndex index) + { + if (method.GetParametersCount () <= (int) index || (int) index < 0) + return null; + return new (new (method), index); + } + + /// <summary> + /// Gets the <see cref="ParameterProxy"/> representing the parameter at index <paramref name="index"/> of method <paramref name="method"/>. + /// Throws if <paramref name="index"/> is not a valid parameter index for <paramref name="method"/>. + /// <see cref="TryGetParameter(MethodDefinition, ParameterIndex)"/> for a non-throwing version if you're not sure the parameter exists on the method. + /// </summary> + public static ParameterProxy GetParameter (this MethodDefinition method, ParameterIndex index) + { + if (method.TryGetParameter (index) is not ParameterProxy param) + throw new InvalidOperationException ($"Cannot get parameter #{(int) index} of method {method.GetDisplayName ()} with {method.GetParametersCount ()} parameters"); + return param; + } + + /// <summary> + /// Returns a foreach-enumerable collection of the parameters pushed onto the stack before the method call (including the implicit 'this' parameter) + /// </summary> + public static ParameterProxyEnumerable GetParameters (this MethodDefinition method) + { + int implicitThisOffset = method.HasImplicitThis () ? 1 : 0; + return new ParameterProxyEnumerable (0, method.Parameters.Count + implicitThisOffset, method); + } + + /// <summary> + /// Returns a list of ParameterProxy representing the parameters listed in the "Parameters" metadata section (i.e. not including the implicit 'this' parameter) + /// </summary> + public static ParameterProxyEnumerable GetMetadataParameters (this MethodDefinition method) + { + int implicitThisOffset = method.HasImplicitThis () ? 1 : 0; + return new ParameterProxyEnumerable (implicitThisOffset, method.Parameters.Count + implicitThisOffset, method); + } } } diff --git a/src/linker/Linker/MethodReferenceExtensions.cs b/src/linker/Linker/MethodReferenceExtensions.cs index 0911a84cd..740d5bbcc 100644 --- a/src/linker/Linker/MethodReferenceExtensions.cs +++ b/src/linker/Linker/MethodReferenceExtensions.cs @@ -2,13 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using ILLink.Shared; +using System.Collections.Generic; using ILLink.Shared.TypeSystemProxy; using Mono.Cecil; namespace Mono.Linker { - public static class MethodReferenceExtensions + internal static class MethodReferenceExtensions { public static string GetDisplayName (this MethodReference method) { @@ -43,11 +43,12 @@ namespace Mono.Linker // Append parameters sb.Append ("("); - if (method.HasParameters) { + if (method.HasMetadataParameters ()) { +#pragma warning disable RS0030 // MethodReference.Parameters is banned -- it's best to leave this as is for now for (int i = 0; i < method.Parameters.Count - 1; i++) sb.Append (method.Parameters[i].ParameterType.GetDisplayNameWithoutNamespace ()).Append (", "); - sb.Append (method.Parameters[method.Parameters.Count - 1].ParameterType.GetDisplayNameWithoutNamespace ()); +#pragma warning restore RS0030 // Do not used banned APIs } sb.Append (")"); @@ -83,51 +84,71 @@ namespace Mono.Linker return method.ReturnType.WithoutModifiers ().MetadataType == MetadataType.Void; } - public static TypeReference? GetInflatedParameterType (this MethodReference method, int index, LinkContext context) + public static TypeReference? GetInflatedParameterType (this MethodReference method, int parameterIndex, LinkContext context) { - var uninflatedParameterType = method.GetParameterType ((SourceParameterIndex) index); - if (method.DeclaringType is GenericInstanceType genericInstance) { - return TypeReferenceExtensions.InflateGenericType (genericInstance, uninflatedParameterType, context); - } - return uninflatedParameterType; +#pragma warning disable RS0030 // MethodReference.Parameters is banned -- it's best to leave this as is for now + if (method.DeclaringType is GenericInstanceType genericInstance) + return TypeReferenceExtensions.InflateGenericType (genericInstance, method.Parameters[parameterIndex].ParameterType, context); + + return method.Parameters[parameterIndex].ParameterType; +#pragma warning restore RS0030 // Do not used banned APIs } - public static TypeReference GetParameterType (this MethodReference method, SourceParameterIndex index) - => method.Parameters[(int) index].ParameterType; + /// <summary> + /// Gets the number of entries in the 'Parameters' section of a method's metadata (i.e. excludes the implicit 'this' from the count) + /// </summary> +#pragma warning disable RS0030 // MethodReference.Parameters is banned -- this provides a wrapper + public static int GetMetadataParametersCount (this MethodReference method) + => method.Parameters.Count; +#pragma warning restore RS0030 // Do not used banned APIs + + /// <summary> + /// Returns true if the method has any parameters in the .parameters section of the method's metadata + /// </summary> + public static bool HasMetadataParameters (this MethodReference method) + => method.GetMetadataParametersCount () != 0; + + /// <summary> + /// Returns a list of the parameters in the method's 'parameters' metadata section (i.e. excluding the implicit 'this' parameter) + /// </summary> +#pragma warning disable RS0030 // MethodReference.Parameters is banned -- this provides a wrapper + public static int GetParametersCount (this MethodReference method) + => method.Parameters.Count + (method.HasImplicitThis () ? 1 : 0); +#pragma warning restore RS0030 // Do not used banned APIs public static bool IsDeclaredOnType (this MethodReference method, string fullTypeName) { return method.DeclaringType.IsTypeOf (fullTypeName); } - public static bool HasParameterOfType (this MethodReference method, int parameterIndex, string fullTypeName) - { - return method.Parameters.Count > parameterIndex && method.Parameters[parameterIndex].ParameterType.IsTypeOf (fullTypeName); - } - public static bool HasImplicitThis (this MethodReference method) { return method.HasThis && !method.ExplicitThis; } /// <summary> - /// Returns the ReferenceKind of a parameter (in, out, ref, none) of a method. Uses the IL based index number (i.e. `this` is 0 if there is a `this`, then 1 is the first parameter) + /// Returns an IEnumerable of the ReferenceKind of each parameter, with the first being for the implicit 'this' parameter if it exists + /// Used for better performance when it's only necessary to get the ReferenceKind of all parameters and nothing else. /// </summary> - public static ReferenceKind ParameterReferenceKind (this MethodReference method, int index) + public static IEnumerable<ReferenceKind> GetParameterReferenceKinds (this MethodReference method) { - if (method.HasImplicitThis ()) { - if (index == 0) - return method.DeclaringType.IsValueType ? ReferenceKind.Ref : ReferenceKind.None; - index--; + if (method.HasImplicitThis ()) + yield return method.DeclaringType.IsValueType ? ReferenceKind.Ref : ReferenceKind.None; +#pragma warning disable RS0030 // MethodReference.Parameters is banned -- this provides a wrapper + foreach (var parameter in method.Parameters) + yield return GetReferenceKind (parameter); +#pragma warning restore RS0030 // Do not used banned APIs + + static ReferenceKind GetReferenceKind (ParameterDefinition param) + { + if (!param.ParameterType.IsByReference) + return ReferenceKind.None; + if (param.IsIn) + return ReferenceKind.In; + if (param.IsOut) + return ReferenceKind.Out; + return ReferenceKind.Ref; } - var param = method.Parameters[index]; - if (!param.ParameterType.IsByReference) - return ReferenceKind.None; - if (param.IsIn) - return ReferenceKind.In; - if (param.IsOut) - return ReferenceKind.Out; - return ReferenceKind.Ref; } } } diff --git a/src/linker/Linker/ParameterHelpers.cs b/src/linker/Linker/ParameterHelpers.cs index eeca9c28c..ba4bec06e 100644 --- a/src/linker/Linker/ParameterHelpers.cs +++ b/src/linker/Linker/ParameterHelpers.cs @@ -2,7 +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; using Mono.Cecil.Cil; @@ -10,7 +10,7 @@ namespace Mono.Linker { public static class ParameterHelpers { - public static ILParameterIndex GetILParameterIndex (MethodDefinition thisMethod, Instruction operation) + public static ParameterIndex GetParameterIndex (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... @@ -31,58 +31,18 @@ namespace Mono.Linker or Code.Ldarga_S => GetParamSequence (), - _ => throw new ArgumentException ($"{nameof (ILParameterIndex)} expected an ldarg or starg instruction, got {operation.OpCode.Name}") + _ => throw new ArgumentException ($"{nameof (GetParameterIndex)} expected an ldarg or starg instruction, got {operation.OpCode.Name}") }; - ILParameterIndex GetLdargParamIndex () + ParameterIndex GetLdargParamIndex () { - return (ILParameterIndex) (code - Code.Ldarg_0); + return (ParameterIndex) (code - Code.Ldarg_0); } - ILParameterIndex GetParamSequence () + ParameterIndex GetParamSequence () { ParameterDefinition param = (ParameterDefinition) operation.Operand; - return (ILParameterIndex) param.Sequence; + return (ParameterIndex) 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 75613e2c0..c8ef8cc8f 100644 --- a/src/linker/Linker/TypeMapInfo.cs +++ b/src/linker/Linker/TypeMapInfo.cs @@ -30,6 +30,7 @@ // using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Mono.Cecil; namespace Mono.Linker @@ -317,9 +318,10 @@ namespace Mono.Linker return null; } + [SuppressMessage ("ApiDesign", "RS0030:Do not used banned APIs", Justification = "It's best to leave working code alone.")] bool MethodMatch (MethodReference candidate, MethodReference method) { - if (candidate.HasParameters != method.HasParameters) + if (candidate.HasParameters != method.HasMetadataParameters ()) return false; if (candidate.Name != method.Name) @@ -335,7 +337,7 @@ namespace Mono.Linker !TypeMatch (candidateReturnType, methodReturnType)) return false; - if (!candidate.HasParameters) + if (!candidate.HasMetadataParameters ()) return true; var cp = candidate.Parameters; diff --git a/src/linker/Linker/TypeReferenceExtensions.cs b/src/linker/Linker/TypeReferenceExtensions.cs index 17fcc766b..d93a7578d 100644 --- a/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/linker/Linker/TypeReferenceExtensions.cs @@ -291,8 +291,10 @@ namespace Mono.Linker CallingConvention = methodDef.CallingConvention }; +#pragma warning disable RS0030 // MethodReference.Parameters is banned. It makes sense to use when needing to directly use Cecil's api. foreach (var parameter in methodDef.Parameters) method.Parameters.Add (new ParameterDefinition (parameter.Name, parameter.Attributes, parameter.ParameterType)); +#pragma warning restore RS0030 foreach (var gp in methodDef.GenericParameters) method.GenericParameters.Add (new GenericParameter (gp.Name, method)); @@ -308,7 +310,7 @@ namespace Mono.Linker public static bool HasDefaultConstructor (this TypeDefinition type, LinkContext context) { foreach (var m in type.Methods) { - if (m.HasParameters) + if (m.HasMetadataParameters ()) continue; var definition = context.Resolve (m); @@ -322,7 +324,7 @@ namespace Mono.Linker public static MethodReference GetDefaultInstanceConstructor (this TypeDefinition type, LinkContext context) { foreach (var m in type.Methods) { - if (m.HasParameters) + if (m.HasMetadataParameters ()) continue; var definition = context.Resolve (m); diff --git a/src/linker/Linker/TypeReferenceWalker.cs b/src/linker/Linker/TypeReferenceWalker.cs index c4ad8e58d..700e2c716 100644 --- a/src/linker/Linker/TypeReferenceWalker.cs +++ b/src/linker/Linker/TypeReferenceWalker.cs @@ -96,9 +96,10 @@ namespace Mono.Linker foreach (var mo in m.Overrides) WalkMethodReference (mo); } - - if (m.HasParameters) +#pragma warning disable RS0030 // MethodReference.Parameters is banned - It's best to leave this as is + if (m.HasMetadataParameters ()) WalkTypeScope (m.Parameters); +#pragma warning restore RS0030 if (m.HasBody) WalkTypeScope (m.Body); @@ -216,8 +217,10 @@ namespace Mono.Linker WalkScopeOfTypeReference (tr); } - if (mr.HasParameters) { + if (mr.HasMetadataParameters ()) { +#pragma warning disable RS0030 // MethedReference.Parameters is banned. Best to leave working code as is. WalkTypeScope (mr.Parameters); +#pragma warning restore RS0030 // Do not used banned APIs } } |