diff options
author | Marek Safar <marek.safar@gmail.com> | 2021-05-08 09:26:38 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-08 09:26:38 +0300 |
commit | fafb6cf6a385a8c753faa174b9ab7c3600a9d494 (patch) | |
tree | 15df162f348d2c147acc2e7b9afc8ecd525beb78 | |
parent | a5f4931bc577ca94d05cd6b64e810c5cc4f035bb (diff) |
Trim parameter names metadata when they are not needed. (#1988)
Co-authored-by: Vitek Karas <vitek.karas@microsoft.com>
25 files changed, 340 insertions, 33 deletions
diff --git a/docs/error-codes.md b/docs/error-codes.md index 8b24628e0..cb75e00e8 100644 --- a/docs/error-codes.md +++ b/docs/error-codes.md @@ -157,6 +157,8 @@ the error code. For example: - The type name used with attribute type is not one of the supported types. +#### `IL1046`: Invalid metadata '{name}' option + ---- ## Warning Codes diff --git a/src/ILLink.Tasks/LinkTask.cs b/src/ILLink.Tasks/LinkTask.cs index b3f369a0d..20adaf035 100644 --- a/src/ILLink.Tasks/LinkTask.cs +++ b/src/ILLink.Tasks/LinkTask.cs @@ -213,6 +213,11 @@ namespace ILLink.Tasks /// </summary> public ITaskItem[] CustomSteps { get; set; } + /// <summary> + /// A list selected metadata which should not be trimmed. It maps to 'keep-metadata' option + /// </summary> + public ITaskItem[] KeepMetadata { get; set; } + private const string DotNetHostPathEnvironmentName = "DOTNET_HOST_PATH"; private string _dotnetPath; @@ -425,6 +430,11 @@ namespace ILLink.Tasks } } + if (KeepMetadata != null) { + foreach (var metadata in KeepMetadata) + args.Append ("--keep-metadata ").AppendLine (Quote (metadata.ItemSpec)); + } + if (_removeSymbols == false) args.AppendLine ("-b"); diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index ef3eaff02..ff63e562b 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -2171,7 +2171,7 @@ namespace Mono.Linker.Dataflow { var source = reflectionContext.Source; var offset = reflectionContext.Instruction?.Offset; - reflectionContext.RecordRecognizedPattern (method, () => _markStep.MarkIndirectlyCalledMethod (method, new DependencyInfo (dependencyKind, source), new MessageOrigin (source, offset))); + reflectionContext.RecordRecognizedPattern (method, () => _markStep.MarkMethodVisibleToReflection (method, new DependencyInfo (dependencyKind, source), new MessageOrigin (source, offset))); } void MarkNestedType (ref ReflectionPatternContext reflectionContext, TypeDefinition nestedType, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index 7d549207b..1a5ae1bc8 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -1585,18 +1585,26 @@ namespace Mono.Linker.Steps MarkMethodsIf (type.Methods, HasOnSerializeOrDeserializeAttribute, new DependencyInfo (DependencyKind.SerializationMethodForType, type), type); } - protected internal virtual TypeDefinition MarkTypeVisibleToReflection (TypeReference type, TypeDefinition definition, DependencyInfo reason, IMemberDefinition sourceLocationMember) + protected internal virtual TypeDefinition MarkTypeVisibleToReflection (TypeReference type, TypeDefinition definition, in DependencyInfo reason, IMemberDefinition sourceLocationMember) { // If a type is visible to reflection, we need to stop doing optimization that could cause observable difference // in reflection APIs. This includes APIs like MakeGenericType (where variant castability of the produced type // could be incorrect) or IsAssignableFrom (where assignability of unconstructed types might change). Annotations.MarkRelevantToVariantCasting (definition); + Annotations.MarkReflectionUsed (definition); + MarkImplicitlyUsedFields (definition); return MarkType (type, reason, sourceLocationMember); } + internal void MarkMethodVisibleToReflection (MethodDefinition method, in DependencyInfo reason, in MessageOrigin origin) + { + MarkIndirectlyCalledMethod (method, reason, origin); + Annotations.MarkReflectionUsed (method); + } + /// <summary> /// Marks the specified <paramref name="reference"/> as referenced. /// </summary> diff --git a/src/linker/Linker.Steps/RootAssemblyInputStep.cs b/src/linker/Linker.Steps/RootAssemblyInputStep.cs index 317b32d1d..e2aa901b7 100644 --- a/src/linker/Linker.Steps/RootAssemblyInputStep.cs +++ b/src/linker/Linker.Steps/RootAssemblyInputStep.cs @@ -82,6 +82,9 @@ namespace Mono.Linker.Steps CodeOptimizations.RemoveLinkAttributes | CodeOptimizations.RemoveSubstitutions | CodeOptimizations.RemoveDynamicDependencyAttribute, assembly.Name.Name); + + // No metadata trimming + Context.MetadataTrimming = MetadataTrimming.None; break; case AssemblyRootMode.AllMembers: Context.Annotations.SetAction (assembly, AssemblyAction.Copy); diff --git a/src/linker/Linker.Steps/SweepStep.cs b/src/linker/Linker.Steps/SweepStep.cs index e5139f458..babbf5657 100644 --- a/src/linker/Linker.Steps/SweepStep.cs +++ b/src/linker/Linker.Steps/SweepStep.cs @@ -277,6 +277,11 @@ namespace Mono.Linker.Steps return Annotations.IsMarked (assembly.MainModule); } + bool CanSweepNamesForMember (IMemberDefinition member, MetadataTrimming metadataTrimming) + { + return (Context.MetadataTrimming & metadataTrimming) != 0 && !Annotations.IsReflectionUsed (member); + } + protected virtual void RemoveAssembly (AssemblyDefinition assembly) { Annotations.SetAction (assembly, AssemblyAction.Delete); @@ -451,8 +456,14 @@ namespace Mono.Linker.Steps if (!method.HasParameters) continue; - foreach (var parameter in method.Parameters) + bool sweepNames = CanSweepNamesForMember (method, MetadataTrimming.ParameterName); + + foreach (var parameter in method.Parameters) { + if (sweepNames) + parameter.Name = null; + SweepCustomAttributes (parameter); + } } } diff --git a/src/linker/Linker/Annotations.cs b/src/linker/Linker/Annotations.cs index ff3b6bc09..2bcbca3eb 100644 --- a/src/linker/Linker/Annotations.cs +++ b/src/linker/Linker/Annotations.cs @@ -66,6 +66,7 @@ namespace Mono.Linker protected readonly HashSet<TypeDefinition> marked_instantiated = new HashSet<TypeDefinition> (); protected readonly HashSet<MethodDefinition> indirectly_called = new HashSet<MethodDefinition> (); protected readonly HashSet<TypeDefinition> types_relevant_to_variant_casting = new HashSet<TypeDefinition> (); + readonly HashSet<IMemberDefinition> reflection_used = new (); public AnnotationStore (LinkContext context) { @@ -223,6 +224,16 @@ namespace Mono.Linker return indirectly_called.Contains (method); } + public void MarkReflectionUsed (IMemberDefinition member) + { + reflection_used.Add (member); + } + + public bool IsReflectionUsed (IMemberDefinition method) + { + return reflection_used.Contains (method); + } + public void MarkInstantiated (TypeDefinition type) { marked_instantiated.Add (type); diff --git a/src/linker/Linker/Driver.cs b/src/linker/Linker/Driver.cs index d0456a604..4eb785200 100644 --- a/src/linker/Linker/Driver.cs +++ b/src/linker/Linker/Driver.cs @@ -171,6 +171,7 @@ namespace Mono.Linker bool new_mvid_used = false; bool deterministic_used = false; bool keepCompilersResources = false; + MetadataTrimming metadataTrimming = MetadataTrimming.Any; List<BaseStep> inputs = CreateDefaultResolvers (); @@ -335,6 +336,18 @@ namespace Mono.Linker continue; + case "--keep-metadata": { + string mname = null; + if (!GetStringParam (token, l => mname = l)) + return -1; + + if (!TryGetMetadataTrimming (mname, out var type)) + return -1; + + metadataTrimming &= ~type; + continue; + } + case "--disable-serialization-discovery": if (!GetBoolParam (token, l => context.DisableSerializationDiscovery = l)) return -1; @@ -650,7 +663,7 @@ namespace Mono.Linker return -1; } - + context.MetadataTrimming = metadataTrimming; // Default to deterministic output if (!new_mvid_used && !deterministic_used) { @@ -1088,6 +1101,25 @@ namespace Mono.Linker return false; } + bool TryGetMetadataTrimming (string text, out MetadataTrimming metadataTrimming) + { + switch (text.ToLowerInvariant ()) { + case "all": + metadataTrimming = MetadataTrimming.Any; + return true; + case "none": + metadataTrimming = MetadataTrimming.None; + return true; + case "parametername": + metadataTrimming = MetadataTrimming.ParameterName; + return true; + } + + context.LogError ($"Invalid metadata value '{text}'", 1046); + metadataTrimming = 0; + return false; + } + protected static bool GetWarningSuppressionWriterFileOutputKind (string text, out WarningSuppressionWriter.FileOutputKind fileOutputKind) { switch (text.ToLowerInvariant ()) { @@ -1248,8 +1280,12 @@ namespace Mono.Linker Console.WriteLine (" --enable-opt NAME [ASM] Enable one of the additional optimizations globaly or for a specific assembly name"); Console.WriteLine (" sealer: Any method or type which does not have override is marked as sealed"); Console.WriteLine (" --explicit-reflection Adds to members never used through reflection DisablePrivateReflection attribute. Defaults to false"); - Console.WriteLine (" --keep-dep-attributes Keep attributes used for manual dependency tracking. Defaults to false"); Console.WriteLine (" --feature FEATURE VALUE Apply any optimizations defined when this feature setting is a constant known at link time"); + Console.WriteLine (" --keep-compilers-resources Keep assembly resources used for F# compilation resources. Defaults to false"); + Console.WriteLine (" --keep-dep-attributes Keep attributes used for manual dependency tracking. Defaults to false"); + Console.WriteLine (" --keep-metadata NAME Keep metadata which would otherwise be removed if not used"); + Console.WriteLine (" all: Metadata for any member are all kept"); + Console.WriteLine (" parametername: All parameter names are kept"); Console.WriteLine (" --new-mvid Generate a new guid for each linked assembly (short -g). Defaults to true"); Console.WriteLine (" --strip-descriptors Remove XML descriptor resources for linked assemblies. Defaults to true"); Console.WriteLine (" --strip-security Remove metadata and code related to Code Access Security. Defaults to true"); @@ -1260,7 +1296,6 @@ namespace Mono.Linker Console.WriteLine (" --link-attributes FILE Supplementary custom attribute definitions for attributes controlling the linker behavior."); Console.WriteLine (" --ignore-link-attributes Skips reading embedded attributes. Defaults to false"); Console.WriteLine (" --strip-link-attributes Remove XML link attributes resources for linked assemblies. Defaults to true"); - Console.WriteLine (" --keep-compilers-resources Keep assembly resources used for F# compilation resources. Defaults to false"); Console.WriteLine (); Console.WriteLine ("Analyzer"); diff --git a/src/linker/Linker/FeatureSettings.cs b/src/linker/Linker/FeatureSettings.cs index 5e5fa4b14..fc99664f0 100644 --- a/src/linker/Linker/FeatureSettings.cs +++ b/src/linker/Linker/FeatureSettings.cs @@ -33,7 +33,7 @@ namespace Mono.Linker return false; } - if (context.FeatureSettings == null || !context.FeatureSettings.TryGetValue (feature, out bool featureSetting)) + if (!context.FeatureSettings.TryGetValue (feature, out bool featureSetting)) return bIsDefault; return bValue == featureSetting; diff --git a/src/linker/Linker/LinkContext.cs b/src/linker/Linker/LinkContext.cs index 9a51d7392..fea6e82b3 100644 --- a/src/linker/Linker/LinkContext.cs +++ b/src/linker/Linker/LinkContext.cs @@ -94,6 +94,8 @@ namespace Mono.Linker set { _outputDirectory = value; } } + public MetadataTrimming MetadataTrimming { get; set; } + public AssemblyAction TrimAction { get; set; } public AssemblyAction DefaultAction { get; set; } @@ -127,7 +129,7 @@ namespace Mono.Linker public bool IgnoreLinkAttributes { get; set; } - public Dictionary<string, bool> FeatureSettings { get; private set; } + public Dictionary<string, bool> FeatureSettings { get; init; } public List<string> AttributeDefinitions { get; private set; } @@ -208,6 +210,7 @@ namespace Mono.Linker _parameters = new Dictionary<string, string> (StringComparer.Ordinal); _customAttributes = new CustomAttributeSource (this); _cachedWarningMessageContainers = new List<MessageContainer> (); + FeatureSettings = new Dictionary<string, bool> (StringComparer.Ordinal); SymbolReaderProvider = new DefaultSymbolReaderProvider (false); @@ -247,14 +250,14 @@ namespace Mono.Linker public void SetFeatureValue (string feature, bool value) { Debug.Assert (!String.IsNullOrEmpty (feature)); - if (FeatureSettings == null) { - FeatureSettings = new Dictionary<string, bool> { { feature, value } }; - return; - } - FeatureSettings[feature] = value; } + public bool HasFeatureValue (string feature, bool value) + { + return FeatureSettings.TryGetValue (feature, out bool fvalue) && value == fvalue; + } + public void AddAttributeDefinitionFile (string file) { if (AttributeDefinitions == null) { diff --git a/src/linker/Linker/MetadataTrimming.cs b/src/linker/Linker/MetadataTrimming.cs new file mode 100644 index 000000000..c8240def4 --- /dev/null +++ b/src/linker/Linker/MetadataTrimming.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Mono.Linker +{ + [Flags] + public enum MetadataTrimming + { + None = 0, + ParameterName = 1, + + Any = ParameterName + } +} diff --git a/test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.cs b/test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.cs index 3b70208fa..23544a538 100644 --- a/test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.cs +++ b/test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.cs @@ -522,6 +522,19 @@ namespace ILLink.Tasks.Tests } } + + [Fact] + public void TestKeepCustomMetadata () + { + var task = new MockTask () { + KeepMetadata = new ITaskItem[] { new TaskItem ("parametername") } + }; + + using (var driver = task.CreateDriver ()) { + Assert.Equal (MetadataTrimming.None, driver.Context.MetadataTrimming); + } + } + [Theory] [InlineData ("copy")] [InlineData ("link")] diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RemovedNameValueAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RemovedNameValueAttribute.cs new file mode 100644 index 000000000..1e25715fe --- /dev/null +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RemovedNameValueAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + /// <summary> + /// Verifies that name of the member is removed + /// </summary> + [AttributeUsage (AttributeTargets.All, AllowMultiple = false, Inherited = false)] + public class RemovedNameValueAttribute : BaseExpectedLinkedBehaviorAttribute + { + } +} diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/VerifyMetadataNamesAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/VerifyMetadataNamesAttribute.cs new file mode 100644 index 000000000..8b8df3d33 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/VerifyMetadataNamesAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + [AttributeUsage (AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public class VerifyMetadataNamesAttribute : BaseExpectedLinkedBehaviorAttribute + { + public VerifyMetadataNamesAttribute () + { + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs index b56225c9b..3889225a2 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs @@ -468,7 +468,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow typeof (T).RequiresNone (); } - [UnrecognizedReflectionAccessPattern (typeof (GenericParameterDataFlow), nameof (MethodRequiresPublicMethods) + "<T>()::T", messageCode: "IL2091")] + [UnrecognizedReflectionAccessPattern (typeof (GenericParameterDataFlow), nameof (MethodRequiresPublicMethods) + "<#1>()::T", messageCode: "IL2091")] static void MethodRequiresPublicFieldsPassThrough< [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () { @@ -477,8 +477,8 @@ namespace Mono.Linker.Tests.Cases.DataFlow MethodRequiresNothing<T> (); } - [UnrecognizedReflectionAccessPattern (typeof (GenericParameterDataFlow), nameof (MethodRequiresPublicFields) + "<T>()::T", messageCode: "IL2091")] - [UnrecognizedReflectionAccessPattern (typeof (GenericParameterDataFlow), nameof (MethodRequiresPublicMethods) + "<T>()::T", messageCode: "IL2091")] + [UnrecognizedReflectionAccessPattern (typeof (GenericParameterDataFlow), nameof (MethodRequiresPublicFields) + "<#1>()::T", messageCode: "IL2091")] + [UnrecognizedReflectionAccessPattern (typeof (GenericParameterDataFlow), nameof (MethodRequiresPublicMethods) + "<#1>()::T", messageCode: "IL2091")] static void MethodRequiresNothingPassThrough<T> () { MethodRequiresPublicFields<T> (); @@ -550,7 +550,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow class TypeWithInstantiatedGenericMethodViaGenericParameter<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TOuter> : BaseTypeWithGenericMethod, IInterfaceWithGenericMethod { - [UnrecognizedReflectionAccessPattern (typeof (BaseTypeWithGenericMethod), nameof (BaseTypeWithGenericMethod.StaticRequiresPublicMethods) + "<T>()::T", + [UnrecognizedReflectionAccessPattern (typeof (BaseTypeWithGenericMethod), nameof (BaseTypeWithGenericMethod.StaticRequiresPublicMethods) + "<#1>()::T", messageCode: "IL2091", message: new string[] { "TInner", "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter<TOuter>.StaticRequiresPublicFields<TInner>()", @@ -562,7 +562,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow StaticRequiresPublicMethods<TInner> (); } - [UnrecognizedReflectionAccessPattern (typeof (BaseTypeWithGenericMethod), nameof (BaseTypeWithGenericMethod.StaticRequiresPublicMethods) + "<T>()::T", + [UnrecognizedReflectionAccessPattern (typeof (BaseTypeWithGenericMethod), nameof (BaseTypeWithGenericMethod.StaticRequiresPublicMethods) + "<#1>()::T", messageCode: "IL2091", message: new string[] { "TOuter", "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter<TOuter>", @@ -580,7 +580,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow StaticRequiresMultipleGenericParams<TOuter, TestType> (); } - [UnrecognizedReflectionAccessPattern (typeof (BaseTypeWithGenericMethod), nameof (BaseTypeWithGenericMethod.StaticRequiresMultipleGenericParams) + "<TFields,TMethods>()::TMethods", + [UnrecognizedReflectionAccessPattern (typeof (BaseTypeWithGenericMethod), nameof (BaseTypeWithGenericMethod.StaticRequiresMultipleGenericParams) + "<#2>()::TMethods", messageCode: "IL2091", message: new string[] { "TOuter", "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter<TOuter>", @@ -591,7 +591,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow StaticRequiresMultipleGenericParams<TestType, TOuter> (); } - [UnrecognizedReflectionAccessPattern (typeof (BaseTypeWithGenericMethod), nameof (BaseTypeWithGenericMethod.InstanceRequiresPublicMethods) + "<T>()::T", + [UnrecognizedReflectionAccessPattern (typeof (BaseTypeWithGenericMethod), nameof (BaseTypeWithGenericMethod.InstanceRequiresPublicMethods) + "<#1>()::T", messageCode: "IL2091", message: new string[] { "TInner", "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter<TOuter>.InstanceRequiresPublicFields<TInner>()", @@ -603,7 +603,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow InstanceRequiresPublicMethods<TInner> (); } - [UnrecognizedReflectionAccessPattern (typeof (BaseTypeWithGenericMethod), nameof (BaseTypeWithGenericMethod.InstanceRequiresPublicMethods) + "<T>()::T", + [UnrecognizedReflectionAccessPattern (typeof (BaseTypeWithGenericMethod), nameof (BaseTypeWithGenericMethod.InstanceRequiresPublicMethods) + "<#1>()::T", messageCode: "IL2091", message: new string[] { "TOuter", "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter<TOuter>", @@ -639,7 +639,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow typeof (T).RequiresPublicMethods (); } - [UnrecognizedReflectionAccessPattern (typeof (IInterfaceWithGenericMethod), nameof (IInterfaceWithGenericMethod.InterfaceRequiresPublicMethods) + "<T>()::T", + [UnrecognizedReflectionAccessPattern (typeof (IInterfaceWithGenericMethod), nameof (IInterfaceWithGenericMethod.InterfaceRequiresPublicMethods) + "<#1>()::T", messageCode: "IL2091", message: new string[] { "TOuter", "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter<TOuter>", diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/MethodParametersDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/MethodParametersDataFlow.cs index fe11b6b03..01047bc7a 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/MethodParametersDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/MethodParametersDataFlow.cs @@ -14,6 +14,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow // Note: this test's goal is to validate that the product correctly reports unrecognized patterns // - so the main validation is done by the UnrecognizedReflectionAccessPattern attributes. [SkipKeptItemsValidation] + [SetupLinkerArgument ("--keep-metadata", "parametername")] public class MethodParametersDataFlow { public static void Main () diff --git a/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs b/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs index af105d086..e9e7ecf29 100644 --- a/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs +++ b/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs @@ -8,6 +8,7 @@ namespace Mono.Linker.Tests.Cases.Libraries { [SetupLinkerArgument ("-a", "test.exe", "library")] [SetupLinkerArgument ("--enable-opt", "ipconstprop")] + [VerifyMetadataNames] public class RootLibrary { private int field; diff --git a/test/Mono.Linker.Tests.Cases/Metadata/NamesAreKept.cs b/test/Mono.Linker.Tests.Cases/Metadata/NamesAreKept.cs new file mode 100644 index 000000000..5dabdf1f9 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Metadata/NamesAreKept.cs @@ -0,0 +1,29 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Metadata +{ + [SetupLinkerArgument ("--keep-metadata", "parametername")] + [VerifyMetadataNames] + public class NamesAreKept + { + public static void Main () + { + var n = new N (5); + N.Foo ("aa"); + } + + class N + { + [Kept] + public N (int arg) + { + } + + [Kept] + public static void Foo (string str) + { + } + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/Metadata/NamesAreRemoved.cs b/test/Mono.Linker.Tests.Cases/Metadata/NamesAreRemoved.cs new file mode 100644 index 000000000..af793ee6c --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Metadata/NamesAreRemoved.cs @@ -0,0 +1,27 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Metadata +{ + public class NamesAreRemoved + { + public static void Main () + { + var n = new N (5); + N.Foo (null, 1); + } + + class N + { + [Kept] + public N ([RemovedNameValueAttribute] int arg) + { + } + + [Kept] + public static void Foo ([RemovedNameValueAttribute] string str, [RemovedNameValueAttribute] long _) + { + } + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs b/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs index 6da378265..7b5f97803 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs @@ -454,10 +454,10 @@ namespace Mono.Linker.Tests.Cases.Reflection } [Kept] - [UnrecognizedReflectionAccessPattern (typeof (Activator), nameof (Activator.CreateInstance) + "<T>", new Type[0], messageCode: "IL2091")] + [UnrecognizedReflectionAccessPattern (typeof (Activator), nameof (Activator.CreateInstance) + "<#1>", new Type[0], messageCode: "IL2091")] #if NETCOREAPP // Warnings are currently duplicated in NETCORE (annotation and intrinsics) - but they're not identical in this case, so we have to list both cases - [UnrecognizedReflectionAccessPattern (typeof (Activator), nameof (Activator.CreateInstance) + "<T>()::T", messageCode: "IL2091")] + [UnrecognizedReflectionAccessPattern (typeof (Activator), nameof (Activator.CreateInstance) + "<#1>()::T", messageCode: "IL2091")] #endif private static void TestCreateInstanceOfTWithNoConstraint<T> () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ParametersUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/ParametersUsedViaReflection.cs new file mode 100644 index 000000000..57b16569a --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Reflection/ParametersUsedViaReflection.cs @@ -0,0 +1,75 @@ +using System; +using System.Reflection; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Reflection +{ + public class ParametersUsedViaReflection + { + public static void Main () + { + TestMethodParameters (); + TestClassParameters (); + } + + [Kept] + static void TestMethodParameters () + { + var method = typeof (GetMethod_Name).GetMethod ("OnlyCalledViaReflection"); + var name = method.GetParameters ()[0].Name; + + GetMethod_Name.CalledDirectly (11); + GetMethod_Name.CalledDirectly2<string> (1); + } + + [Kept] + static void TestClassParameters () + { + var type = Type.GetType ("Mono.Linker.Tests.Cases.Reflection.ParametersUsedViaReflection/GenericClass1`1"); + + var type2 = new GenericClass2<int> (); + } + + [Kept] + class GetMethod_Name + { + [Kept] + public static int OnlyCalledViaReflection (int firstName) + { + return 2; + } + + [Kept] + public static int OnlyCalledViaReflection (int arg1, int arg2) + { + return 3; + } + + [Kept] + public static void CalledDirectly ([RemovedNameValue] int firstArg) + { + } + + [Kept] + public static void CalledDirectly2</*[RemovedNameValue]*/LongGenericName> ([RemovedNameValue] int firstArg) + { + } + } + + [Kept] + public class GenericClass1<TKey> + { + } + + [Kept] + public class GenericClass2</*[RemovedNameValue]*/TRKey> + { + [Kept] + public GenericClass2 () + { + } + } + } + +} diff --git a/test/Mono.Linker.Tests/Extensions/CecilExtensions.cs b/test/Mono.Linker.Tests/Extensions/CecilExtensions.cs index f18803377..c6ed39935 100644 --- a/test/Mono.Linker.Tests/Extensions/CecilExtensions.cs +++ b/test/Mono.Linker.Tests/Extensions/CecilExtensions.cs @@ -126,12 +126,7 @@ namespace Mono.Linker.Tests.Extensions var builder = new StringBuilder (); builder.Append (method.Name); if (method.HasGenericParameters) { - builder.Append ('<'); - - for (int i = 0; i < method.GenericParameters.Count - 1; i++) - builder.Append ($"{method.GenericParameters[i]},"); - - builder.Append ($"{method.GenericParameters[method.GenericParameters.Count - 1]}>"); + builder.Append ($"<#{method.GenericParameters.Count}>"); } builder.Append ("("); diff --git a/test/Mono.Linker.Tests/TestCases/TestDatabase.cs b/test/Mono.Linker.Tests/TestCases/TestDatabase.cs index 73e5ed671..fcab71eb7 100644 --- a/test/Mono.Linker.Tests/TestCases/TestDatabase.cs +++ b/test/Mono.Linker.Tests/TestCases/TestDatabase.cs @@ -212,6 +212,11 @@ namespace Mono.Linker.Tests.TestCases return NUnitCasesBySuiteName ("LinkAttributes"); } + public static IEnumerable<TestCaseData> MetadataTests () + { + return NUnitCasesBySuiteName ("Metadata"); + } + public static TestCaseCollector CreateCollector () { GetDirectoryPaths (out string rootSourceDirectory, out string testCaseAssemblyPath); diff --git a/test/Mono.Linker.Tests/TestCases/TestSuites.cs b/test/Mono.Linker.Tests/TestCases/TestSuites.cs index ae2d45090..3860cccbc 100644 --- a/test/Mono.Linker.Tests/TestCases/TestSuites.cs +++ b/test/Mono.Linker.Tests/TestCases/TestSuites.cs @@ -254,6 +254,12 @@ namespace Mono.Linker.Tests.TestCases Run (testCase); } + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.MetadataTests))] + public void MetadataTests (TestCase testCase) + { + Run (testCase); + } + protected virtual void Run (TestCase testCase) { var runner = new TestRunner (new ObjectFactory ()); diff --git a/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs index 421e4ac1a..a4a4b1f64 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs @@ -18,11 +18,15 @@ namespace Mono.Linker.Tests.TestCasesRunner readonly HashSet<string> verifiedGeneratedFields = new HashSet<string> (); readonly HashSet<string> verifiedEventMethods = new HashSet<string> (); readonly HashSet<string> verifiedGeneratedTypes = new HashSet<string> (); + bool checkNames; public AssemblyChecker (AssemblyDefinition original, AssemblyDefinition linked) { this.originalAssembly = original; this.linkedAssembly = linked; + + checkNames = original.MainModule.GetTypeReferences ().Any (attr => + attr.Name == nameof (RemovedNameValueAttribute)); } public void Verify () @@ -107,8 +111,13 @@ namespace Mono.Linker.Tests.TestCasesRunner return; } + bool prev = checkNames; + checkNames |= original.HasAttribute (nameof (VerifyMetadataNamesAttribute)); + VerifyTypeDefinitionKept (original, linked); + checkNames = prev; + if (original.HasAttribute (nameof (CreatedMemberAttribute))) { foreach (var attr in original.CustomAttributes.Where (l => l.AttributeType.Name == nameof (CreatedMemberAttribute))) { var newName = original.FullName + "::" + attr.ConstructorArguments[0].Value.ToString (); @@ -163,7 +172,8 @@ namespace Mono.Linker.Tests.TestCasesRunner foreach (var m in original.Methods) { if (verifiedEventMethods.Contains (m.FullName)) continue; - VerifyMethod (m, linked?.Methods.FirstOrDefault (l => m.GetSignature () == l.GetSignature ())); + var msign = m.GetSignature (); + VerifyMethod (m, linked?.Methods.FirstOrDefault (l => msign == l.GetSignature ())); linkedMembers.Remove (m.FullName); } } @@ -765,7 +775,18 @@ namespace Mono.Linker.Tests.TestCasesRunner if (src.HasGenericParameters) { for (int i = 0; i < src.GenericParameters.Count; ++i) { // TODO: Verify constraints - VerifyCustomAttributes (src.GenericParameters[i], linked.GenericParameters[i]); + var srcp = src.GenericParameters[i]; + var lnkp = linked.GenericParameters[i]; + VerifyCustomAttributes (srcp, lnkp); + + if (checkNames) { + if (srcp.CustomAttributes.Any (attr => attr.AttributeType.Name == nameof (RemovedNameValueAttribute))) { + string name = (src.GenericParameterType == GenericParameterType.Method ? "!!" : "!") + srcp.Position; + Assert.AreEqual (name, lnkp.Name, "Expected empty generic parameter name"); + } else { + Assert.AreEqual (srcp.Name, lnkp.Name, "Mismatch in generic parameter name"); + } + } } } } @@ -775,7 +796,17 @@ namespace Mono.Linker.Tests.TestCasesRunner Assert.AreEqual (src.HasParameters, linked.HasParameters); if (src.HasParameters) { for (int i = 0; i < src.Parameters.Count; ++i) { - VerifyCustomAttributes (src.Parameters[i], linked.Parameters[i]); + var srcp = src.Parameters[i]; + var lnkp = linked.Parameters[i]; + + VerifyCustomAttributes (srcp, lnkp); + + if (checkNames) { + if (srcp.CustomAttributes.Any (attr => attr.AttributeType.Name == nameof (RemovedNameValueAttribute))) + Assert.IsEmpty (lnkp.Name, "Expected empty parameter name"); + else + Assert.AreEqual (srcp.Name, lnkp.Name, "Mismatch in parameter name"); + } } } } |