diff --git a/docs/error-codes.md b/docs/error-codes.md index 94169850a548..b3d95dad80a3 100644 --- a/docs/error-codes.md +++ b/docs/error-codes.md @@ -163,9 +163,9 @@ error and warning codes. - The linker found a call to a method which is annotated with 'RequiresUnreferencedCodeAttribute' which can break functionality of a trimmed application. -#### `IL2027`: Attribute 'attribute' should only be used once on 'method'. +#### `IL2027`: Attribute 'attribute' should only be used once on 'member'. -- The linker found multiple instances of attribute 'attribute' on 'method'. This attribute is only allowed to have one instance, linker will only use the fist instance and ignore the rest. +- The linker found multiple instances of attribute 'attribute' on 'member'. This attribute is only allowed to have one instance, linker will only use the fist instance and ignore the rest. #### `IL2028`: Attribute 'attribute' on 'method' doesn't have a required constructor argument. @@ -185,4 +185,24 @@ error and warning codes. #### `IL2032`: Argument 'argument' specified in 'XML document location' could not be transformed to the constructor parameter type -- The number of arguments correspond to a certain type constructor, but the type of arguments specified in the xml does not match the type of arguments in the constructor. \ No newline at end of file +- The number of arguments correspond to a certain type constructor, but the type of arguments specified in the xml does not match the type of arguments in the constructor. + +#### `IL2033`: Deprecated PreserveDependencyAttribute on 'member'. Use DynamicDependencyAttribute instead. + +- PreserveDependencyAttribute was an internal attribute that was never officially supported. Instead, use the similar DynamicDependencyAttribute. + +#### `IL2034`: Invalid DynamicDependencyAttribute on 'member' + +- The input contains an invalid use of DynamicDependencyAttribute. Ensure that you are using one of the officially supported constructors. + +#### `IL2035`: Unresolved assembly 'assemblyName' in DynamicDependencyAttribute on 'member' + +- The assembly string given in a DynamicDependencyAttribute constructor could not be resolved. Ensure that the argument specifies a valid asembly name, and that the assembly is available to the linker. + +#### `IL2036`: Unresolved type 'typeName' in DynamicDependencyAttribute on 'member' + +- The type in a DynamicDependencyAttribute constructor could not be resolved. Ensure that the argument specifies a valid type name or type reference, that the type exists in the specified assembly, and that the assembly is available to the linker. + +### `IL2037`: No members were resolved for 'memberSignature/memberTypes' in DynamicDependencyAttribute on 'member' + +- The member signature or DynamicallyAccessedMemberTypes in a DynamicDependencyAttribute constructor did not resolve to any members on the type. If you using a signature, ensure that it refers to an existing member, and that it uses the format defined at https://github.com/dotnet/csharplang/blob/master/spec/documentation-comments.md#id-string-format. If using DynamicallyAccessedMemberTypes, ensure that the type contains members of the specified member types. diff --git a/src/linker/Linker.Steps/DynamicDependencyLookupStep.cs b/src/linker/Linker.Steps/DynamicDependencyLookupStep.cs new file mode 100644 index 000000000000..1fc37225aa91 --- /dev/null +++ b/src/linker/Linker.Steps/DynamicDependencyLookupStep.cs @@ -0,0 +1,98 @@ +// 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; +using System.Diagnostics; +using Mono.Cecil; +using System.Diagnostics.CodeAnalysis; + +#nullable enable + +namespace Mono.Linker.Steps +{ + public class DynamicDependencyLookupStep : LoadReferencesStep + { + protected override void ProcessAssembly (AssemblyDefinition assembly) + { + var module = assembly.MainModule; + + foreach (var type in module.Types) { + ProcessType (type); + } + } + + void ProcessType (TypeDefinition type) + { + if (type.HasMethods) { + foreach (var method in type.GetMethods ()) { + var methodDefinition = method.Resolve (); + if (methodDefinition?.HasCustomAttributes != true) + continue; + + ProcessDynamicDependencyAttributes (methodDefinition); + } + } + + if (type.HasFields) { + foreach (var field in type.Fields) { + var fieldDefinition = field.Resolve (); + if (fieldDefinition?.HasCustomAttributes != true) + continue; + + ProcessDynamicDependencyAttributes (fieldDefinition); + } + } + + if (type.HasNestedTypes) { + foreach (var nestedType in type.NestedTypes) { + ProcessType (nestedType); + } + } + } + + void ProcessDynamicDependencyAttributes (IMemberDefinition member) + { + Debug.Assert (member is MethodDefinition || member is FieldDefinition); + + foreach (var ca in member.CustomAttributes) { + if (!IsPreserveDependencyAttribute (ca.AttributeType)) + continue; +#if FEATURE_ILLINK + Context.LogWarning ($"Deprecated PreserveDependencyAttribute on '{member}'. Use DynamicDependencyAttribute instead.", 2033, MessageOrigin.TryGetOrigin (member)); +#endif + if (ca.ConstructorArguments.Count != 3) + continue; + + if (!(ca.ConstructorArguments[2].Value is string assemblyName)) + continue; + + var assembly = Context.Resolve (new AssemblyNameReference (assemblyName, new Version ())); + if (assembly == null) + continue; + ProcessReferences (assembly); + } + + var dynamicDependencies = Context.Annotations.GetLinkerAttributes (member); + Debug.Assert (dynamicDependencies != null); + + foreach (var dynamicDependency in dynamicDependencies) { + if (dynamicDependency.AssemblyName == null) + continue; + + var assembly = Context.Resolve (new AssemblyNameReference (dynamicDependency.AssemblyName, new Version ())); + if (assembly == null) { + Context.LogWarning ($"Unresolved assembly '{dynamicDependency.AssemblyName}' in DynamicDependencyAttribute on '{member}'", 2035, MessageOrigin.TryGetOrigin (member)); + continue; + } + + ProcessReferences (assembly); + } + } + + public static bool IsPreserveDependencyAttribute (TypeReference tr) + { + return tr.Name == "PreserveDependencyAttribute" && tr.Namespace == "System.Runtime.CompilerServices"; + } + } +} diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index fab55393be2d..79571bfa1464 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -30,6 +30,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text.RegularExpressions; using Mono.Cecil; @@ -54,7 +55,10 @@ public partial class MarkStep : IStep #if DEBUG static readonly DependencyKind[] _entireTypeReasons = new DependencyKind[] { DependencyKind.NestedType, +#if !FEATURE_ILLINK DependencyKind.PreservedDependency, +#endif + DependencyKind.DynamicDependency, DependencyKind.TypeInAssembly, DependencyKind.AccessedViaReflection, DependencyKind.BaseType, @@ -71,7 +75,10 @@ public partial class MarkStep : IStep DependencyKind.InteropMethodDependency, DependencyKind.Ldtoken, DependencyKind.MemberOfType, +#if !FEATURE_ILLINK DependencyKind.PreservedDependency, +#endif + DependencyKind.DynamicDependency, DependencyKind.ReferencedBySpecialAttribute, DependencyKind.TypePreserve, }; @@ -87,6 +94,7 @@ public partial class MarkStep : IStep DependencyKind.CustomAttributeArgumentValue, DependencyKind.DeclaringType, DependencyKind.DeclaringTypeOfCalledMethod, + DependencyKind.DynamicDependency, DependencyKind.ElementType, DependencyKind.FieldType, DependencyKind.GenericArgumentType, @@ -130,7 +138,10 @@ public partial class MarkStep : IStep DependencyKind.Newobj, DependencyKind.Override, DependencyKind.OverrideOnInstantiatedType, +#if !FEATURE_ILLINK DependencyKind.PreservedDependency, +#endif + DependencyKind.DynamicDependency, DependencyKind.PreservedMethod, DependencyKind.ReferencedBySpecialAttribute, DependencyKind.SerializationMethodForType, @@ -367,7 +378,7 @@ void ProcessQueue () } catch (Exception e) when (!(e is LinkerFatalErrorException)) { throw new LinkerFatalErrorException ( MessageContainer.CreateErrorMessage ($"Error processing method '{method.FullName}' in assembly '{method.Module.Name}'", 1005, - origin: MessageOrigin.TryGetOrigin (method, 0)), e); + origin: MessageOrigin.TryGetOrigin (method)), e); } } } @@ -522,9 +533,8 @@ void MarkCustomAttributes (ICustomAttributeProvider provider, in DependencyInfo bool markOnUse = _context.KeepUsedAttributeTypesOnly && Annotations.GetAction (GetAssemblyFromCustomAttributeProvider (provider)) == AssemblyAction.Link; foreach (CustomAttribute ca in provider.CustomAttributes) { - if (ProcessLinkerSpecialAttribute (ca, provider, reason)) { + if (ProcessLinkerSpecialAttribute (ca, provider, reason)) continue; - } if (markOnUse) { _lateMarkedAttributes.Enqueue ((new AttributeProviderPair (ca, provider), reason)); @@ -534,24 +544,120 @@ void MarkCustomAttributes (ICustomAttributeProvider provider, in DependencyInfo MarkCustomAttribute (ca, reason); MarkSpecialCustomAttributeDependencies (ca, provider); } + + if (!(provider is MethodDefinition || provider is FieldDefinition)) + return; + + var dynamicDependencies = _context.Annotations.GetLinkerAttributes ((IMemberDefinition) provider); + + foreach (var dynamicDependency in dynamicDependencies) + MarkDynamicDependency (dynamicDependency, (MemberReference) provider); } protected virtual bool ProcessLinkerSpecialAttribute (CustomAttribute ca, ICustomAttributeProvider provider, in DependencyInfo reason) { - if (IsUserDependencyMarker (ca.AttributeType) && provider is MemberReference mr) { + var isPreserveDependency = IsUserDependencyMarker (ca.AttributeType); + var isDynamicDependency = ca.AttributeType.IsTypeOf (); + + if (!((isPreserveDependency || isDynamicDependency) && provider is MemberReference mr)) + return false; + + if (isPreserveDependency) MarkUserDependency (mr, ca); - if (_context.KeepDependencyAttributes || Annotations.GetAction (mr.Module.Assembly) != AssemblyAction.Link) { - MarkCustomAttribute (ca, reason); - } else { - // Record the custom attribute so that it has a reason, without actually marking it. - Tracer.AddDirectDependency (ca, reason, marked: false); + if (_context.KeepDependencyAttributes || Annotations.GetAction (mr.Module.Assembly) != AssemblyAction.Link) { + MarkCustomAttribute (ca, reason); + } else { + // Record the custom attribute so that it has a reason, without actually marking it. + Tracer.AddDirectDependency (ca, reason, marked: false); + } + + return true; + } + + void MarkDynamicDependency (DynamicDependency dynamicDependency, MemberReference context) + { + Debug.Assert (context is MethodDefinition || context is FieldDefinition); + AssemblyDefinition assembly; + if (dynamicDependency.AssemblyName != null) { + assembly = _context.GetLoadedAssembly (dynamicDependency.AssemblyName); + if (assembly == null) { + _context.LogWarning ($"Unresolved assembly '{dynamicDependency.AssemblyName}' in DynamicDependencyAttribute on '{context}'", 2035, MessageOrigin.TryGetOrigin (context.Resolve ())); + return; } + } else { + assembly = context.Module.Assembly; + Debug.Assert (assembly != null); + } - return true; + TypeDefinition type; + if (dynamicDependency.TypeName is string typeName) { + type = DocumentationSignatureParser.GetTypeByDocumentationSignature (assembly, typeName); + if (type == null) { + _context.LogWarning ($"Unresolved type '{typeName}' in DynamicDependencyAttribute on '{context}'", 2036, MessageOrigin.TryGetOrigin (context.Resolve ())); + return; + } + } else if (dynamicDependency.Type is TypeReference typeReference) { + type = typeReference.Resolve (); + if (type == null) { + _context.LogWarning ($"Unresolved type '{typeReference}' in DynamicDependencyAtribute on '{context}'", 2036, MessageOrigin.TryGetOrigin (context.Resolve ())); + return; + } + } else { + type = context.DeclaringType.Resolve (); + if (type == null) { + _context.LogWarning ($"Unresolved type '{context.DeclaringType}' in DynamicDependencyAttribute on '{context}'", 2036, MessageOrigin.TryGetOrigin (context.Resolve ())); + return; + } } - return false; + IEnumerable members; + if (dynamicDependency.MemberSignature is string memberSignature) { + members = DocumentationSignatureParser.GetMembersByDocumentationSignature (type, memberSignature); + if (!members.Any ()) { + _context.LogWarning ($"No members were resolved for '{memberSignature}' in DynamicDependencyAttribute on '{context}'", 2037, MessageOrigin.TryGetOrigin (context.Resolve ())); + return; + } + } else { + var memberTypes = dynamicDependency.MemberTypes; + members = DynamicallyAccessedMembersBinder.GetDynamicallyAccessedMembers (type, memberTypes); + if (!members.Any ()) { + _context.LogWarning ($"No members were resolved for '{memberTypes}' in DynamicDependencyAttribute on '{context}'", 2037, MessageOrigin.TryGetOrigin (context.Resolve ())); + return; + } + } + + MarkMembers (type, members, new DependencyInfo (DependencyKind.DynamicDependency, dynamicDependency.OriginalAttribute)); + } + + void MarkMembers (TypeDefinition typeDefinition, IEnumerable members, DependencyInfo reason) + { + foreach (var member in members) { + switch (member) { + case TypeDefinition type: + MarkType (type, reason); + break; + case MethodDefinition method: + MarkMethod (method, reason); + break; + case FieldDefinition field: + MarkField (field, reason); + break; + case PropertyDefinition property: + MarkProperty (property, reason); + MarkMethodIfNotNull (property.GetMethod, reason); + MarkMethodIfNotNull (property.SetMethod, reason); + MarkMethodsIf (property.OtherMethods, m => true, reason); + break; + case EventDefinition @event: + MarkEvent (@event, reason); + MarkMethodsIf (@event.OtherMethods, m => true, reason); + break; + case null: + MarkEntireType (typeDefinition, includeBaseTypes: true, reason); + break; + } + } } protected static AssemblyDefinition GetAssemblyFromCustomAttributeProvider (ICustomAttributeProvider provider) @@ -571,27 +677,13 @@ protected static AssemblyDefinition GetAssemblyFromCustomAttributeProvider (ICus protected virtual bool IsUserDependencyMarker (TypeReference type) { - return PreserveDependencyLookupStep.IsPreserveDependencyAttribute (type); + return DynamicDependencyLookupStep.IsPreserveDependencyAttribute (type); } protected virtual void MarkUserDependency (MemberReference context, CustomAttribute ca) { - if (ca.HasProperties && ca.Properties[0].Name == "Condition") { - var condition = ca.Properties[0].Argument.Value as string; - switch (condition) { - case "": - case null: - break; - case "DEBUG": - if (!_context.KeepMembersForDebugger) - return; - - break; - default: - // Don't have yet a way to match the general condition so everything is excluded - return; - } - } + if (!DynamicDependency.ShouldProcess (_context, ca)) + return; AssemblyDefinition assembly; var args = ca.ConstructorArguments; @@ -2284,7 +2376,7 @@ void MarkNewCodeDependencies (MethodDefinition method) var baseType = method.DeclaringType.BaseType.Resolve (); if (!MarkDefaultConstructor (baseType, new DependencyInfo (DependencyKind.BaseDefaultCtorForStubbedMethod, method))) throw new LinkerFatalErrorException (MessageContainer.CreateErrorMessage ($"Cannot stub constructor on '{method.DeclaringType}' when base type does not have default constructor", - 1006, origin: MessageOrigin.TryGetOrigin (method, 0))); + 1006, origin: MessageOrigin.TryGetOrigin (method))); break; @@ -2608,11 +2700,6 @@ protected virtual void MarkInterfaceImplementation (InterfaceImplementation ifac Annotations.Mark (iface, new DependencyInfo (DependencyKind.InterfaceImplementationOnType, type)); } - bool HasManuallyTrackedDependency (MethodBody methodBody) - { - return PreserveDependencyLookupStep.HasPreserveDependencyAttribute (methodBody.Method); - } - // // Extension point for reflection logic handling customization // @@ -2626,9 +2713,6 @@ protected virtual bool ProcessReflectionDependency (MethodBody body, Instruction // protected virtual void MarkReflectionLikeDependencies (MethodBody body, bool requiresReflectionMethodBodyScanner) { - if (HasManuallyTrackedDependency (body)) - return; - if (requiresReflectionMethodBodyScanner) { var scanner = new ReflectionMethodBodyScanner (_context, this, _flowAnnotations); scanner.ScanAndProcessReturnValue (body); diff --git a/src/linker/Linker.Steps/PreserveDependencyLookupStep.cs b/src/linker/Linker.Steps/PreserveDependencyLookupStep.cs deleted file mode 100644 index 9d3eb8d8dc24..000000000000 --- a/src/linker/Linker.Steps/PreserveDependencyLookupStep.cs +++ /dev/null @@ -1,100 +0,0 @@ -// -// PreserveDependencyLookupStep.cs -// -// Author: -// Marek Safar (marek.safar@gmail.com) -// -// Copyright (C) 2018 Microsoft Corporation -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using Mono.Cecil; -using Mono.Collections.Generic; - -namespace Mono.Linker.Steps -{ - public class PreserveDependencyLookupStep : LoadReferencesStep - { - protected override void ProcessAssembly (AssemblyDefinition assembly) - { - var module = assembly.MainModule; - - foreach (var type in module.Types) { - if (type.HasMethods) { - foreach (var method in type.GetMethods ()) { - var md = method.Resolve (); - if (md?.HasCustomAttributes != true) - continue; - - ProcessPreserveDependencyAttribute (md.CustomAttributes); - } - } - - if (type.HasFields) { - foreach (var field in type.Fields) { - var md = field.Resolve (); - if (md?.HasCustomAttributes != true) - continue; - - ProcessPreserveDependencyAttribute (md.CustomAttributes); - } - } - } - } - - public static bool IsPreserveDependencyAttribute (TypeReference tr) - { - return tr.Name == "PreserveDependencyAttribute" && tr.Namespace == "System.Runtime.CompilerServices"; - } - - public static bool HasPreserveDependencyAttribute (MethodDefinition method) - { - if (!method.HasCustomAttributes) - return false; - - foreach (var ca in method.CustomAttributes) { - if (IsPreserveDependencyAttribute (ca.AttributeType)) - return true; - } - - return false; - } - - void ProcessPreserveDependencyAttribute (Collection attributes) - { - foreach (var ca in attributes) { - if (!IsPreserveDependencyAttribute (ca.AttributeType)) - continue; - - if (ca.ConstructorArguments.Count != 3) - continue; - - if (!(ca.ConstructorArguments[2].Value is string assemblyName)) - continue; - - var newDependency = Context.Resolve (new AssemblyNameReference (assemblyName, new Version ())); - if (newDependency != null) - ProcessReferences (newDependency); - } - } - } -} diff --git a/src/linker/Linker/Annotations.cs b/src/linker/Linker/Annotations.cs index 7b68e51d2997..63d948878a0b 100644 --- a/src/linker/Linker/Annotations.cs +++ b/src/linker/Linker/Annotations.cs @@ -55,7 +55,7 @@ public partial class AnnotationStore protected readonly Dictionary> override_methods = new Dictionary> (); protected readonly Dictionary> base_methods = new Dictionary> (); protected readonly Dictionary symbol_readers = new Dictionary (); - readonly Dictionary method_linker_attributes = new Dictionary (); + readonly Dictionary linker_attributes = new Dictionary (); readonly Dictionary> custom_annotations = new Dictionary> (); protected readonly Dictionary> resources_to_remove = new Dictionary> (); @@ -64,7 +64,6 @@ public partial class AnnotationStore protected readonly HashSet marked_instantiated = new HashSet (); protected readonly HashSet indirectly_called = new HashSet (); - public AnnotationStore (LinkContext context) => this.context = context; public bool ProcessSatelliteAssemblies { get; set; } @@ -429,34 +428,42 @@ public bool SetPreservedStaticCtor (TypeDefinition type) return marked_types_with_cctor.Add (type); } - public bool HasLinkerAttribute (MethodDefinition method) where T : Attribute + public bool HasLinkerAttribute (IMemberDefinition member) where T : Attribute { - if (!method_linker_attributes.TryGetValue (method, out var linkerAttributeInformation)) { - linkerAttributeInformation = new LinkerAttributesInformation (context, method); - method_linker_attributes.Add (method, linkerAttributeInformation); + // Avoid setting up and inserting LinkerAttributesInformation for members without attributes. + if (!member.HasCustomAttributes) + return false; + + if (!linker_attributes.TryGetValue (member, out var linkerAttributeInformation)) { + linkerAttributeInformation = new LinkerAttributesInformation (context, member); + linker_attributes.Add (member, linkerAttributeInformation); } return linkerAttributeInformation.HasAttribute (); } - public IEnumerable GetLinkerAttributes (MethodDefinition method) where T : Attribute + public IEnumerable GetLinkerAttributes (IMemberDefinition member) where T : Attribute { - if (!method_linker_attributes.TryGetValue (method, out var linkerAttributeInformation)) { - linkerAttributeInformation = new LinkerAttributesInformation (context, method); - method_linker_attributes.Add (method, linkerAttributeInformation); + // Avoid setting up and inserting LinkerAttributesInformation for members without attributes. + if (!member.HasCustomAttributes) + return Enumerable.Empty (); + + if (!linker_attributes.TryGetValue (member, out var linkerAttributeInformation)) { + linkerAttributeInformation = new LinkerAttributesInformation (context, member); + linker_attributes.Add (member, linkerAttributeInformation); } return linkerAttributeInformation.GetAttributes (); } - public bool TryGetLinkerAttribute (MethodDefinition method, out T attribute) where T : Attribute + public bool TryGetLinkerAttribute (IMemberDefinition member, out T attribute) where T : Attribute { - var attributes = GetLinkerAttributes (method); + var attributes = GetLinkerAttributes (member); if (attributes.Count () > 1) { - context.LogWarning ($"Attribute '{typeof (T).FullName}' should only be used once on '{method}'.", - 2027, MessageOrigin.TryGetOrigin (method, 0)); + context.LogWarning ($"Attribute '{typeof (T).FullName}' should only be used once on '{member}'.", 2027, MessageOrigin.TryGetOrigin (member)); } + Debug.Assert (attributes.Count () <= 1); attribute = attributes.FirstOrDefault (); return attribute != null; } diff --git a/src/linker/Linker/AssemblyUtilities.cs b/src/linker/Linker/AssemblyUtilities.cs index 62a1284130db..9eda057e92a9 100644 --- a/src/linker/Linker/AssemblyUtilities.cs +++ b/src/linker/Linker/AssemblyUtilities.cs @@ -1,4 +1,3 @@ -using System; using Mono.Cecil; namespace Mono.Linker diff --git a/src/linker/Linker/DependencyInfo.cs b/src/linker/Linker/DependencyInfo.cs index 5a4d6453a82e..07db29568726 100644 --- a/src/linker/Linker/DependencyInfo.cs +++ b/src/linker/Linker/DependencyInfo.cs @@ -109,6 +109,7 @@ public enum DependencyKind OverrideOnInstantiatedType = 66, // instantiated type -> override method on the type // Linker-specific behavior (preservation hints, patterns, user inputs, linker outputs, etc.) + DynamicDependency = 67, // DynamicDependency attribute -> member PreservedDependency = 67, // PreserveDependency attribute -> member AccessedViaReflection = 68, // method -> detected member accessed via reflection from that method PreservedMethod = 69, // type/method -> preserved method (explicitly preserved in Annotations by XML or other steps) diff --git a/src/linker/Linker/DocumentationSignatureParser.cs b/src/linker/Linker/DocumentationSignatureParser.cs index ae16ae53ad7f..36a3027582a7 100644 --- a/src/linker/Linker/DocumentationSignatureParser.cs +++ b/src/linker/Linker/DocumentationSignatureParser.cs @@ -53,6 +53,28 @@ public static IEnumerable GetMembersForDocumentationSignature return results; } + // Takes a documentation signature (not including the documentation member type prefix) and resolves it to a type + // in the assembly. + public static TypeDefinition? GetTypeByDocumentationSignature (AssemblyDefinition assembly, string signature) + { + int index = 0; + var results = new List (); + DocumentationSignatureParser.ParseSignaturePart (signature, ref index, assembly.MainModule, DocumentationSignatureParser.MemberType.Type, results); + Debug.Assert (results.Count <= 1); + return results.Count == 0 ? null : (TypeDefinition) results[0]; + } + + // Takes a member signature (not including the declaring type) and returns the matching members on the type. + public static IEnumerable GetMembersByDocumentationSignature (TypeDefinition type, string signature) + { + int index = 0; + var results = new List (); + var nameBuilder = new StringBuilder (); + var (name, arity) = DocumentationSignatureParser.ParseTypeOrNamespaceName (signature, ref index, nameBuilder); + DocumentationSignatureParser.GetMatchingMembers (signature, ref index, type.Module, type, name, arity, DocumentationSignatureParser.MemberType.All, results); + return results; + } + static string GetSignaturePart (TypeReference type) { var builder = new StringBuilder (); diff --git a/src/linker/Linker/Driver.cs b/src/linker/Linker/Driver.cs index 1978d9e03f8f..1f7928d9fc66 100644 --- a/src/linker/Linker/Driver.cs +++ b/src/linker/Linker/Driver.cs @@ -593,7 +593,7 @@ protected int SetupContext (ILogger customLogger = null) p.AddStepAfter (typeof (LoadReferencesStep), new LoadI18nAssemblies (assemblies)); if (assemblies != I18nAssemblies.None) - p.AddStepAfter (typeof (PreserveDependencyLookupStep), new PreserveCalendarsStep (assemblies)); + p.AddStepAfter (typeof (DynamicDependencyLookupStep), new PreserveCalendarsStep (assemblies)); #endif if (_needAddBypassNGenStep) @@ -633,8 +633,8 @@ protected int SetupContext (ILogger customLogger = null) // dynamically adds steps: // ResolveFromXmlStep [optional, possibly many] // BodySubstituterStep [optional, possibly many] - // PreserveDependencyLookupStep - // [mono only] PreselveCalendarsStep [optional] + // DynamicDependencyLookupStep + // [mono only] PreserveCalendarsStep [optional] // TypeMapStep // BodySubstituterStep [optional] // RemoveSecurityStep [optional] @@ -1060,7 +1060,7 @@ static Pipeline GetStandardPipeline () Pipeline p = new Pipeline (); p.AppendStep (new LoadReferencesStep ()); p.AppendStep (new BlacklistStep ()); - p.AppendStep (new PreserveDependencyLookupStep ()); + p.AppendStep (new DynamicDependencyLookupStep ()); p.AppendStep (new TypeMapStep ()); p.AppendStep (new MarkStep ()); p.AppendStep (new SweepStep ()); diff --git a/src/linker/Linker/DynamicDependency.cs b/src/linker/Linker/DynamicDependency.cs new file mode 100644 index 000000000000..5f683d25ab31 --- /dev/null +++ b/src/linker/Linker/DynamicDependency.cs @@ -0,0 +1,145 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Mono.Cecil; + +#nullable enable + +namespace Mono.Linker +{ + /// Tracks dependencies created via DynamicDependencyAttribute in the linker. + /// This is almost identical to DynamicDependencyAttribute, but it holds a + /// TypeReference instead of a Type, and it has a reference to the original + /// CustomAttribute for dependency tracing. It is also a place for helper + /// methods related to the attribute. + internal class DynamicDependency : Attribute + { + public CustomAttribute? OriginalAttribute { get; set; } + public DynamicDependency (string memberSignature) + { + MemberSignature = memberSignature; + } + + public DynamicDependency (string memberSignature, TypeReference type) + { + MemberSignature = memberSignature; + Type = type; + } + + public DynamicDependency (string memberSignature, string typeName, string assemblyName) + { + MemberSignature = memberSignature; + TypeName = typeName; + AssemblyName = assemblyName; + } + + public DynamicDependency (DynamicallyAccessedMemberTypes memberTypes, TypeReference type) + { + MemberTypes = memberTypes; + Type = type; + } + + public DynamicDependency (DynamicallyAccessedMemberTypes memberTypes, string typeName, string assemblyName) + { + MemberTypes = memberTypes; + TypeName = typeName; + AssemblyName = assemblyName; + } + + public string? MemberSignature { get; } + + public DynamicallyAccessedMemberTypes MemberTypes { get; } + + public TypeReference? Type { get; } + + public string? TypeName { get; } + + public string? AssemblyName { get; } + + public string? Condition { get; set; } + + public static DynamicDependency? ProcessAttribute (LinkContext context, ICustomAttributeProvider provider, CustomAttribute customAttribute) + { + if (!(provider is IMemberDefinition member)) + return null; + + if (!(member is MethodDefinition || member is FieldDefinition)) + return null; + + // Don't honor the Condition until we have figured out the behavior for DynamicDependencyAttribute: + // https://github.com/mono/linker/issues/1231 + // if (!ShouldProcess (context, customAttribute)) + // return null; + + var dynamicDependency = GetDynamicDependency (context, customAttribute); + if (dynamicDependency != null) + return dynamicDependency; + + context.LogWarning ($"Invalid DynamicDependencyAttribute on '{member}'", 2030, MessageOrigin.TryGetOrigin (member)); + return null; + } + + static DynamicDependency? GetDynamicDependency (LinkContext context, CustomAttribute ca) + { + var args = ca.ConstructorArguments; + if (args.Count < 1 || args.Count > 3) + return null; + + // First argument is string or DynamicallyAccessedMemberTypes + string? memberSignature = args[0].Value as string; + if (args.Count == 1) + return memberSignature == null ? null : new DynamicDependency (memberSignature); + DynamicallyAccessedMemberTypes? memberTypes = null; + if (memberSignature == null) { + var argType = args[0].Type; + if (!argType.IsTypeOf ()) + return null; + try { + memberTypes = (DynamicallyAccessedMemberTypes) args[0].Value; + } catch (InvalidCastException) { } + if (memberTypes == null) + return null; + } + + // Second argument is Type for ctors with two args, string for ctors with three args + if (args.Count == 2) { + if (!(args[1].Value is TypeReference type)) + return null; + return memberSignature == null ? new DynamicDependency (memberTypes!.Value, type) : new DynamicDependency (memberSignature, type); + } + Debug.Assert (args.Count == 3); + if (!(args[1].Value is string typeName)) + return null; + + // Third argument is assembly name + if (!(args[2].Value is string assemblyName)) + return null; + + var dynamicDependency = memberSignature == null ? new DynamicDependency (memberTypes!.Value, typeName, assemblyName) : new DynamicDependency (memberSignature, typeName, assemblyName); + dynamicDependency.OriginalAttribute = ca; + + return dynamicDependency; + } + + public static bool ShouldProcess (LinkContext context, CustomAttribute ca) + { + if (ca.HasProperties && ca.Properties[0].Name == "Condition") { + var condition = ca.Properties[0].Argument.Value as string; + switch (condition) { + case "": + case null: + return true; + case "DEBUG": + if (!context.KeepMembersForDebugger) + return false; + + break; + default: + // Don't have yet a way to match the general condition so everything is excluded + return false; + } + } + return true; + } + } +} \ No newline at end of file diff --git a/src/linker/Linker/LinkerAttributesInformation.cs b/src/linker/Linker/LinkerAttributesInformation.cs index fe8f92a8ee49..3f4f562a1871 100644 --- a/src/linker/Linker/LinkerAttributesInformation.cs +++ b/src/linker/Linker/LinkerAttributesInformation.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using Mono.Cecil; @@ -14,32 +15,38 @@ readonly struct LinkerAttributesInformation { readonly Dictionary> _linkerAttributes; - public LinkerAttributesInformation (LinkContext context, MethodDefinition method) + public LinkerAttributesInformation (LinkContext context, ICustomAttributeProvider provider) { _linkerAttributes = null; - if (method.HasCustomAttributes) { - foreach (var customAttribute in method.CustomAttributes) { + if (provider.HasCustomAttributes) { + foreach (var customAttribute in provider.CustomAttributes) { var attributeType = customAttribute.AttributeType; Attribute attributeValue = null; - if (attributeType.Name == "RequiresUnreferencedCodeAttribute" && attributeType.Namespace == "System.Diagnostics.CodeAnalysis") { - attributeValue = ProcessRequiresUnreferencedCodeAttribute (context, method, customAttribute); - } + if (attributeType.IsTypeOf ()) + attributeValue = ProcessRequiresUnreferencedCodeAttribute (context, provider, customAttribute); + else if (attributeType.IsTypeOf ()) + attributeValue = DynamicDependency.ProcessAttribute (context, provider, customAttribute); + AddAttribute (ref _linkerAttributes, attributeValue); + } + } + } - if (attributeValue != null) { - if (_linkerAttributes == null) - _linkerAttributes = new Dictionary> (); + static void AddAttribute (ref Dictionary> attributes, Attribute attributeValue) + { + if (attributeValue == null) + return; - Type attributeValueType = attributeValue.GetType (); - if (!_linkerAttributes.TryGetValue (attributeValueType, out var attributeList)) { - attributeList = new List (); - _linkerAttributes.Add (attributeValueType, attributeList); - } + if (attributes == null) + attributes = new Dictionary> (); - attributeList.Add (attributeValue); - } - } + Type attributeValueType = attributeValue.GetType (); + if (!attributes.TryGetValue (attributeValueType, out var attributeList)) { + attributeList = new List (); + attributes.Add (attributeValueType, attributeList); } + + attributeList.Add (attributeValue); } public bool HasAttribute () where T : Attribute @@ -59,8 +66,11 @@ public IEnumerable GetAttributes () where T : Attribute return attributeList.Cast (); } - static Attribute ProcessRequiresUnreferencedCodeAttribute (LinkContext context, MethodDefinition method, CustomAttribute customAttribute) + static Attribute ProcessRequiresUnreferencedCodeAttribute (LinkContext context, ICustomAttributeProvider provider, CustomAttribute customAttribute) { + if (!(provider is MethodDefinition method)) + return null; + if (customAttribute.HasConstructorArguments) { string message = (string) customAttribute.ConstructorArguments[0].Value; string url = null; @@ -74,7 +84,7 @@ static Attribute ProcessRequiresUnreferencedCodeAttribute (LinkContext context, } context.LogWarning ($"Attribute '{typeof (RequiresUnreferencedCodeAttribute).FullName}' on '{method}' doesn't have a required constructor argument.", - 2028, MessageOrigin.TryGetOrigin (method, 0)); + 2028, MessageOrigin.TryGetOrigin (method)); return null; } } diff --git a/src/linker/Linker/MessageOrigin.cs b/src/linker/Linker/MessageOrigin.cs index 213464f1dbf0..76a17c4f37bc 100644 --- a/src/linker/Linker/MessageOrigin.cs +++ b/src/linker/Linker/MessageOrigin.cs @@ -26,12 +26,12 @@ public MessageOrigin (string fileName, int sourceLine = 0, int sourceColumn = 0) MemberDefinition = null; } - public MessageOrigin (IMemberDefinition memberDefinition, int sourceLine = 0, int sourceColumn = 0) + public MessageOrigin (IMemberDefinition memberDefinition) { FileName = null; MemberDefinition = memberDefinition; - SourceLine = sourceLine; - SourceColumn = sourceColumn; + SourceLine = 0; + SourceColumn = 0; } private MessageOrigin (string fileName, IMemberDefinition memberDefinition, int sourceLine = 0, int sourceColumn = 0) @@ -42,8 +42,11 @@ private MessageOrigin (string fileName, IMemberDefinition memberDefinition, int SourceColumn = sourceColumn; } - public static MessageOrigin TryGetOrigin (MethodDefinition sourceMethod, int ilOffset) + public static MessageOrigin TryGetOrigin (IMemberDefinition sourceMember, int ilOffset = 0) { + if (!(sourceMember is MethodDefinition sourceMethod)) + return new MessageOrigin (sourceMember); + if (sourceMethod.DebugInformation.HasSequencePoints) { SequencePoint correspondingSequencePoint = sourceMethod.DebugInformation.SequencePoints .Where (s => s.Offset <= ilOffset)?.Last (); diff --git a/src/linker/Linker/TypeDefinitionExtensions.cs b/src/linker/Linker/TypeDefinitionExtensions.cs index c00db8fe8619..d33b37013b1e 100644 --- a/src/linker/Linker/TypeDefinitionExtensions.cs +++ b/src/linker/Linker/TypeDefinitionExtensions.cs @@ -1,4 +1,3 @@ - using System; using Mono.Cecil; diff --git a/src/linker/Linker/TypeReferenceExtensions.cs b/src/linker/Linker/TypeReferenceExtensions.cs index 8498d788969d..d6dab7873f50 100644 --- a/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/linker/Linker/TypeReferenceExtensions.cs @@ -259,5 +259,11 @@ public static bool IsTypeOf (this TypeReference type, string ns, string name) return type.Name == name && type.Namespace == ns; } + + public static bool IsTypeOf (this TypeReference tr) + { + var type = typeof (T); + return tr.Name == type.Name && tr.Namespace == tr.Namespace; + } } } diff --git a/src/linker/System.Diagnostics.CodeAnalysis/DynamicDependencyAttribute.cs b/src/linker/System.Diagnostics.CodeAnalysis/DynamicDependencyAttribute.cs new file mode 100644 index 000000000000..b029c6cc949e --- /dev/null +++ b/src/linker/System.Diagnostics.CodeAnalysis/DynamicDependencyAttribute.cs @@ -0,0 +1,14 @@ +// 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. + +#nullable enable + +namespace System.Diagnostics.CodeAnalysis +{ + /// This is an internal version of the attribute in the framework at https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/DynamicDependencyAttribute.cs + /// We currently only use the type as a generic parameter, so the implementation isn't copied. + internal sealed class DynamicDependencyAttribute : Attribute + { + } +} diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Mono.Linker.Tests.Cases.Expectations.csproj b/test/Mono.Linker.Tests.Cases.Expectations/Mono.Linker.Tests.Cases.Expectations.csproj index 769832748ef5..656d7fd9bc63 100644 --- a/test/Mono.Linker.Tests.Cases.Expectations/Mono.Linker.Tests.Cases.Expectations.csproj +++ b/test/Mono.Linker.Tests.Cases.Expectations/Mono.Linker.Tests.Cases.Expectations.csproj @@ -1,5 +1,9 @@ + + latest + + netcoreapp3.0 diff --git a/test/Mono.Linker.Tests.Cases.Expectations/System.Diagnostics.CodeAnalysis/DynamicDependencyAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/System.Diagnostics.CodeAnalysis/DynamicDependencyAttribute.cs new file mode 100644 index 000000000000..107521234047 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases.Expectations/System.Diagnostics.CodeAnalysis/DynamicDependencyAttribute.cs @@ -0,0 +1,136 @@ +// 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. + +#nullable enable + +namespace System.Diagnostics.CodeAnalysis +{ + /// + /// States a dependency that one member has on another. + /// + /// + /// This can be used to inform tooling of a dependency that is otherwise not evident purely from + /// metadata and IL, for example a member relied on via reflection. + /// + /// + /// This is a copy of the enum definition in the framework at https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/DynamicDependencyAttribute.cs + /// + [AttributeUsage ( + AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method, + AllowMultiple = true, Inherited = false)] + public sealed class DynamicDependencyAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on the same type as the consumer. + /// + /// The signature of the member depended on. + public DynamicDependencyAttribute (string memberSignature) + { + MemberSignature = memberSignature; + } + + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on a . + /// + /// The signature of the member depended on. + /// The containing . + public DynamicDependencyAttribute (string memberSignature, Type type) + { + MemberSignature = memberSignature; + Type = type; + } + + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on a type in an assembly. + /// + /// The signature of the member depended on. + /// The full name of the type containing the specified member. + /// The assembly name of the type containing the specified member. + public DynamicDependencyAttribute (string memberSignature, string typeName, string assemblyName) + { + MemberSignature = memberSignature; + TypeName = typeName; + AssemblyName = assemblyName; + } + + /// + /// Initializes a new instance of the class + /// with the specified types of members on a . + /// + /// The types of members depended on. + /// The containing the specified members. + public DynamicDependencyAttribute (DynamicallyAccessedMemberTypes memberTypes, Type type) + { + MemberTypes = memberTypes; + Type = type; + } + + /// + /// Initializes a new instance of the class + /// with the specified types of members on a type in an assembly. + /// + /// The types of members depended on. + /// The full name of the type containing the specified members. + /// The assembly name of the type containing the specified members. + public DynamicDependencyAttribute (DynamicallyAccessedMemberTypes memberTypes, string typeName, string assemblyName) + { + MemberTypes = memberTypes; + TypeName = typeName; + AssemblyName = assemblyName; + } + + /// + /// Gets the signature of the member depended on. + /// + /// + /// Either must be a valid string or + /// must not equal , but not both. + /// + public string? MemberSignature { get; } + + /// + /// Gets the which specifies the type + /// of members depended on. + /// + /// + /// Either must be a valid string or + /// must not equal , but not both. + /// + public DynamicallyAccessedMemberTypes MemberTypes { get; } + + /// + /// Gets the containing the specified member. + /// + /// + /// If neither nor are specified, + /// the type of the consumer is assumed. + /// + public Type? Type { get; } + + /// + /// Gets the full name of the type containing the specified member. + /// + /// + /// If neither nor are specified, + /// the type of the consumer is assumed. + /// + public string? TypeName { get; } + + /// + /// Gets the assembly name of the specified type. + /// + /// + /// is only valid when is specified. + /// + public string? AssemblyName { get; } + + /// + /// Gets or sets the condition in which the dependency is applicable, e.g. "DEBUG". + /// + public string? Condition { get; set; } + } +} diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyInCopyAssembly.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyInCopyAssembly.cs new file mode 100644 index 000000000000..deef452cdee2 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyInCopyAssembly.cs @@ -0,0 +1,18 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies +{ + public class DynamicDependencyInCopyAssembly + { + [DynamicDependency ("ExtraMethod1")] + public DynamicDependencyInCopyAssembly () + { + } + + static void ExtraMethod1 () + { + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInAssemblyLibrary.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInAssemblyLibrary.cs new file mode 100644 index 000000000000..fc625b8694f1 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInAssemblyLibrary.cs @@ -0,0 +1,16 @@ +using System; +namespace Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies +{ + public class DynamicDependencyMethodInAssemblyLibrary + { + public DynamicDependencyMethodInAssemblyLibrary () + { + } + + private void Foo () + { + } + + private int privateField; + } +} diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyBase.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyBase.cs new file mode 100644 index 000000000000..33910f8eb0eb --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyBase.cs @@ -0,0 +1,7 @@ +namespace Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies +{ + public abstract class DynamicDependencyMethodInNonReferencedAssemblyBase + { + public abstract string Method (); + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyBase2.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyBase2.cs new file mode 100644 index 000000000000..12a1569daacc --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyBase2.cs @@ -0,0 +1,10 @@ +namespace Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies +{ + public class DynamicDependencyMethodInNonReferencedAssemblyBase2 : DynamicDependencyMethodInNonReferencedAssemblyBase + { + public override string Method () + { + return "Base2"; + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyChainedLibrary.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyChainedLibrary.cs new file mode 100644 index 000000000000..48b315f7d44b --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyChainedLibrary.cs @@ -0,0 +1,19 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies +{ + public class DynamicDependencyMethodInNonReferencedAssemblyChainedLibrary : DynamicDependencyMethodInNonReferencedAssemblyBase + { + public override string Method () + { + Dependency (); + return "Dependency"; + } + + [DynamicDependency (".ctor()", "Mono.Linker.Tests.Cases.Advanced.Dependencies.DynamicDependencyMethodInNonReferencedAssemblyBase2", "base2")] + public static void Dependency () + { + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyChainedReferenceLibrary.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyChainedReferenceLibrary.cs new file mode 100644 index 000000000000..d0392bb6a3d1 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyChainedReferenceLibrary.cs @@ -0,0 +1,11 @@ +namespace Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies +{ + public class DynamicDependencyMethodInNonReferencedAssemblyChainedReferenceLibrary : DynamicDependencyMethodInNonReferencedAssemblyBase + { + public override string Method () + { + DynamicDependencyMethodInNonReferencedAssemblyChainedLibrary.Dependency (); + return "Dependency"; + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyLibrary.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyLibrary.cs new file mode 100644 index 000000000000..b2d1f083aea7 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyLibrary.cs @@ -0,0 +1,14 @@ +namespace Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies +{ + public class DynamicDependencyMethodInNonReferencedAssemblyLibrary : DynamicDependencyMethodInNonReferencedAssemblyBase + { + public override string Method () + { + return "Dependency"; + } + + private void UnusedMethod () + { + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyLibrary.xml b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyLibrary.xml new file mode 100644 index 000000000000..5cad4f63d0b6 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyMethodInNonReferencedAssemblyLibrary.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyOnUnusedMethodInNonReferencedAssemblyWithCopyUsedAction_Lib.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyOnUnusedMethodInNonReferencedAssemblyWithCopyUsedAction_Lib.cs new file mode 100644 index 000000000000..e74ee7393ced --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/Dependencies/DynamicDependencyOnUnusedMethodInNonReferencedAssemblyWithCopyUsedAction_Lib.cs @@ -0,0 +1,9 @@ +namespace Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies +{ + public class DynamicDependencyOnUnusedMethodInNonReferencedAssemblyWithCopyUsedAction_Lib + { + public static void MethodPreservedViaDependencyAttribute () + { + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyField.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyField.cs new file mode 100644 index 000000000000..edef9e077b4e --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyField.cs @@ -0,0 +1,29 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies +{ + public class DynamicDependencyField + { + public static void Main () + { + var b = new B (); + b.field = 3; + } + + [KeptMember (".ctor()")] + class B + { + [Kept] + [DynamicDependency ("ExtraMethod1")] + public int field; + + [Kept] + static void ExtraMethod1 () + { + } + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyFromCopiedAssembly.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyFromCopiedAssembly.cs new file mode 100644 index 000000000000..e35c2a10636a --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyFromCopiedAssembly.cs @@ -0,0 +1,23 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies +{ + [SetupLinkerAction ("copy", "lib")] + [SetupCompileBefore ("lib.dll", new[] { "Dependencies/DynamicDependencyInCopyAssembly.cs" })] + [KeptAllTypesAndMembersInAssembly ("lib.dll")] + public class DynamicDependencyFromCopiedAssembly + { + public static void Main () + { + Test (); + } + + [Kept] + static void Test () + { + var b = new DynamicDependencyInCopyAssembly (); + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyKeptOption.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyKeptOption.cs new file mode 100644 index 000000000000..e93ad71244e2 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyKeptOption.cs @@ -0,0 +1,30 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies +{ + [SetupLinkerArgument ("--keep-dep-attributes", "true")] + class DynamicDependencyKeptOption + { + public static void Main () + { + B.Test (); + } + + class B + { + [Kept] + int field; + + [Kept] + [KeptAttributeAttribute (typeof (DynamicDependencyAttribute))] + + [DynamicDependency ("field")] + public static void Test () + { + } + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMemberSignatureWildcard.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMemberSignatureWildcard.cs new file mode 100644 index 000000000000..677843e64610 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMemberSignatureWildcard.cs @@ -0,0 +1,23 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies +{ + [LogContains ("IL2037: No members were resolved for '*' in DynamicDependencyAttribute on 'System.Void Mono.Linker.Tests.Cases.DynamicDependencies.DynamicDependencyMemberSignatureWildcard::Dependency()'")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/DynamicDependencyMethodInAssemblyLibrary.cs" })] + public class DynamicDependencyMemberSignatureWildcard + { + public static void Main () + { + Dependency (); + } + + [Kept] + [DynamicDependency ("*", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInAssemblyLibrary", "library")] + static void Dependency () + { + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMemberTypes.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMemberTypes.cs new file mode 100644 index 000000000000..f3c9629394e0 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMemberTypes.cs @@ -0,0 +1,280 @@ +using System; +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies +{ + class DynamicDependencyMemberTypes + { + public static void Main () + { + B.Method (); + } + + static class B + { + [Kept] + [DynamicDependency (DynamicallyAccessedMemberTypes.DefaultConstructor, typeof (TypeWithAutoImplementedDefaultConstructor))] + [DynamicDependency (DynamicallyAccessedMemberTypes.DefaultConstructor, typeof (TypeWithDefaultConstructor))] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicConstructors, typeof (TypeWithPublicConstructor))] + [DynamicDependency (DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof (TypeWithNonPublicConstructor))] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicMethods, typeof (TypeWithPublicMethod))] + [DynamicDependency (DynamicallyAccessedMemberTypes.NonPublicMethods, typeof (TypeWithNonPublicMethod))] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicFields, typeof (TypeWithPublicField))] + [DynamicDependency (DynamicallyAccessedMemberTypes.NonPublicFields, typeof (TypeWithNonPublicField))] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicNestedTypes, typeof (TypeWithPublicNestedType))] + [DynamicDependency (DynamicallyAccessedMemberTypes.NonPublicNestedTypes, typeof (TypeWithNonPublicNestedType))] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicProperties, typeof (TypeWithPublicProperty))] + [DynamicDependency (DynamicallyAccessedMemberTypes.NonPublicProperties, typeof (TypeWithNonPublicProperty))] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicEvents, typeof (TypeWithPublicEvent))] + [DynamicDependency (DynamicallyAccessedMemberTypes.NonPublicEvents, typeof (TypeWithNonPublicEvent))] + [DynamicDependency (DynamicallyAccessedMemberTypes.All, typeof (TypeWithAll))] + [DynamicDependency (DynamicallyAccessedMemberTypes.None, typeof (TypeWithNone))] + public static void Method () + { + } + } + + + [KeptMember (".ctor()")] + class TypeWithAutoImplementedDefaultConstructor + { + public void Method () { } + + public int field; + } + + class TypeWithDefaultConstructor + { + [Kept] + public TypeWithDefaultConstructor () { } + + public TypeWithDefaultConstructor (int i) { } + } + + class TypeWithPublicConstructor + { + [Kept] + public TypeWithPublicConstructor (int i) { } + + [Kept] + public TypeWithPublicConstructor (string s) { } + + TypeWithPublicConstructor () { } + } + + class TypeWithNonPublicConstructor + { + public TypeWithNonPublicConstructor (int i) { } + + [Kept] + TypeWithNonPublicConstructor () { } + + [Kept] + TypeWithNonPublicConstructor (string s) { } + } + + + class TypeWithPublicMethod + { + [Kept] + public void PublicMethod () { } + + void PrivateMethod () { } + + public int field; + } + + class TypeWithNonPublicMethod + { + [Kept] + void PrivateMethod () { } + + public void NonPublicMethod () { } + } + + class TypeWithPublicField + { + [Kept] + public int publicField; + + string nonPublicField; + + public void Method () { } + } + + class TypeWithNonPublicField + { + [Kept] + int nonPublicField; + + public string publicField; + + void Method () { } + } + + [Kept] + class TypeWithPublicNestedType + { + [Kept] + public class PublicNestedType + { + public void Method () { } + public int field; + + void NonPublicMethod () { } + } + + public void Method () { } + + void NonPublicMethod () { } + + class NonPublicNestedType + { + } + } + + [Kept] + class TypeWithNonPublicNestedType + { + [Kept] + class NonPublicNestedType + { + public void Method () { } + + public int field; + + void NonPublicMethod () { } + } + + public void Method () { } + + void NonPublicMethod () { } + + public class PublicNestedType + { + } + } + + class TypeWithPublicProperty + { + [Kept] + [KeptBackingField] + public int Property { [Kept] get; [Kept] set; } + + int NonPublicProperty { get; set; } + + public void Method () { } + } + + class TypeWithNonPublicProperty + { + [Kept] + [KeptBackingField] + int NonPublicProperty { [Kept] get; [Kept] set; } + + public int PublicProperty { get; set; } + + void NonPublicMethod () { } + } + + class TypeWithPublicEvent + { + [Kept] + [KeptBackingField] + [KeptEventAddMethod] + [KeptEventRemoveMethod] + public event EventHandler PublicEvent; + + event EventHandler NonPublicEvent; + + public void Method () { } + } + + class TypeWithNonPublicEvent + { + [Kept] + [KeptBackingField] + [KeptEventAddMethod] + [KeptEventRemoveMethod] + event EventHandler NonPublicEvent; + + public event EventHandler PublicEven; + + void NonPublicMethod () { } + } + + [KeptMember (".ctor()")] + class TypeWithAll + { + [Kept] + public void PublicMethod () { } + + [Kept] + void NonPublicMehod () { } + + [Kept] + public int publicField; + + [Kept] + int nonPublicField; + + [Kept] + [KeptBackingField] + public int PublicProperty { [Kept] get; [Kept] set; } + + [Kept] + [KeptBackingField] + int NonPublicProperty { [Kept] get; [Kept] set; } + + [Kept] + [KeptBackingField] + [KeptEventAddMethod] + [KeptEventRemoveMethod] + public event EventHandler PublicEvent; + + [Kept] + [KeptBackingField] + [KeptEventAddMethod] + [KeptEventRemoveMethod] + event EventHandler NonPublicEvent; + + [Kept] + [KeptMember (".ctor()")] + public class PublicNestedType + { + [Kept] + [KeptMember (".ctor()")] + class RecursiveNestedType + { + [Kept] + void Method () { } + } + } + + [Kept] + [KeptMember (".ctor()")] + class NonPublicNestedType + { + [Kept] + [KeptMember (".ctor()")] + class RecursiveNestedType + { + [Kept] + void Method () { } + } + } + } + + public class TypeWithNone + { + public void Method () { } + + public int field; + + public class NestedType { } + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethod.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethod.cs new file mode 100644 index 000000000000..9a1085fd3a89 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethod.cs @@ -0,0 +1,147 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies +{ + [LogContains ("IL2037: No members were resolved for 'MissingMethod' in DynamicDependencyAttribute on 'System.Void Mono.Linker.Tests.Cases.DynamicDependencies.DynamicDependencyMethod/B::Broken()'")] + [LogContains ("IL2037: No members were resolved for 'Dependency2``1(``0,System.Int32,System.Object)' in DynamicDependencyAttribute on 'System.Void Mono.Linker.Tests.Cases.DynamicDependencies.DynamicDependencyMethod/B::Broken()'")] + [LogContains ("IL2037: No members were resolved for '#ctor()' in DynamicDependencyAttribute on 'System.Void Mono.Linker.Tests.Cases.DynamicDependencies.DynamicDependencyMethod/B::Broken()'")] + [LogContains ("IL2037: No members were resolved for '#cctor()' in DynamicDependencyAttribute on 'System.Void Mono.Linker.Tests.Cases.DynamicDependencies.DynamicDependencyMethod/B::Broken()'")] + class DynamicDependencyMethod + { + public static void Main () + { + new B (); // Needed to avoid lazy body marking stubbing + + B.Method (); + B.SameContext (); + B.Broken (); + B.Conditional (); + } + + [KeptMember (".ctor()")] + class B + { + [Kept] + int field; + + [Kept] + void Method2 (out sbyte arg) + { + arg = 1; + } + + [Kept] + [DynamicDependency ("Dependency1()", typeof (C))] + [DynamicDependency ("Dependency2``1(``0[],System.Int32", typeof (C))] + [DynamicDependency ("#ctor()", typeof (C))] // To avoid lazy body marking stubbing + [DynamicDependency ("field", typeof (C))] + [DynamicDependency ("NextOne(Mono.Linker.Tests.Cases.DynamicDependencies.DynamicDependencyMethod.Nested@)", typeof (Nested))] + [DynamicDependency ("#cctor()", typeof (Nested))] + // Dependency on a property itself should be expressed as a dependency on one or both accessor methods + [DynamicDependency ("get_Property()", typeof (C))] + [DynamicDependency ("M``1(Mono.Linker.Tests.Cases.DynamicDependencies.DynamicDependencyMethod.Complex.S{" + + "Mono.Linker.Tests.Cases.DynamicDependencies.DynamicDependencyMethod.Complex.G{" + + "Mono.Linker.Tests.Cases.DynamicDependencies.DynamicDependencyMethod.Complex.A,``0}}" + + "[0:,0:,0:][][][0:,0:]@)", typeof (Complex))] + public static void Method () + { + } + + [Kept] + [DynamicDependency ("field")] + [DynamicDependency ("Method2(System.SByte@)")] + public static void SameContext () + { + } + + [Kept] + [DynamicDependency ("MissingMethod", typeof (C))] + [DynamicDependency ("Dependency2``1(``0,System.Int32,System.Object)", typeof (C))] + [DynamicDependency ("")] + [DynamicDependency ("#ctor()", typeof (NestedStruct))] + [DynamicDependency ("#cctor()", typeof (C))] + public static void Broken () + { + } + + [Kept] + [DynamicDependency ("ConditionalTest()", typeof (C), Condition = "don't have it")] + public static void Conditional () + { + } + } + + class Nested + { + [Kept] + private static void NextOne (ref Nested arg1) + { + } + + [Kept] + static Nested () + { + + } + } + + class Complex + { + [Kept] + public struct S { } + [Kept] + public class A { } + [Kept] + public class G { } + + [Kept] + public void M (ref S>[,][][][,,] a) + { + } + } + + struct NestedStruct + { + public string Name; + + public NestedStruct (string name) + { + Name = name; + } + } + } + + [KeptMember (".ctor()")] + class C + { + [Kept] + internal string field; + + [Kept] + internal void Dependency1 () + { + } + + internal void Dependency1 (long arg1) + { + } + + [Kept] + internal void Dependency2 (T[] arg1, int arg2) + { + } + + [Kept] + [KeptBackingField] + internal string Property { [Kept] get; set; } + + // For now, Condition has no effect: https://github.com/mono/linker/issues/1231 + [Kept] + internal void ConditionalTest () + { + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInAssembly.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInAssembly.cs new file mode 100644 index 000000000000..be03791c4132 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInAssembly.cs @@ -0,0 +1,25 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies +{ + [KeptMemberInAssembly ("library.dll", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInAssemblyLibrary", ".ctor()")] + [KeptMemberInAssembly ("library.dll", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInAssemblyLibrary", "privateField")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/DynamicDependencyMethodInAssemblyLibrary.cs" })] + public class DynamicDependencyMethodInAssembly + { + public static void Main () + { + Dependency (); + } + + [Kept] + [DynamicDependency ("#ctor()", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInAssemblyLibrary", "library")] + [DynamicDependency (DynamicallyAccessedMemberTypes.NonPublicFields, "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInAssemblyLibrary", "library")] + static void Dependency () + { + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInNonReferencedAssembly.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInNonReferencedAssembly.cs new file mode 100644 index 000000000000..103c7b33a8bc --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInNonReferencedAssembly.cs @@ -0,0 +1,42 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies +{ + [SetupCompileBefore ("base.dll", new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyBase.cs" })] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyLibrary.cs" }, references: new[] { "base.dll" }, addAsReference: false)] + [KeptAssembly ("base.dll")] + [KeptAssembly ("library.dll")] + [KeptMemberInAssembly ("base.dll", typeof (DynamicDependencyMethodInNonReferencedAssemblyBase), "Method()")] + [KeptMemberInAssembly ("library.dll", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInNonReferencedAssemblyLibrary", "Method()")] + public class DynamicDependencyMethodInNonReferencedAssembly + { + public static void Main () + { + var obj = new Foo (); + var val = obj.Method (); + Dependency (); + } + + [Kept] + [DynamicDependency ("#ctor()", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInNonReferencedAssemblyLibrary", "library")] + static void Dependency () + { + } + + [Kept] + [KeptMember (".ctor()")] + [KeptBaseType (typeof (DynamicDependencyMethodInNonReferencedAssemblyBase))] + class Foo : DynamicDependencyMethodInNonReferencedAssemblyBase + { + [Kept] + public override string Method () + { + return "Foo"; + } + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInNonReferencedAssemblyChained.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInNonReferencedAssemblyChained.cs new file mode 100644 index 000000000000..07acabf169ae --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInNonReferencedAssemblyChained.cs @@ -0,0 +1,46 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies +{ + [IgnoreTestCase ("Currently failing")] + [SetupCompileBefore ("base.dll", new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyBase.cs" })] + [SetupCompileBefore ("base2.dll", new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyBase2.cs" }, references: new[] { "base.dll" }, addAsReference: false)] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyChainedLibrary.cs" }, references: new[] { "base.dll" }, addAsReference: false)] + [KeptAssembly ("base.dll")] + [KeptAssembly ("base2.dll")] + [KeptAssembly ("library.dll")] + [KeptMemberInAssembly ("base.dll", typeof (DynamicDependencyMethodInNonReferencedAssemblyBase), "Method()")] + [KeptMemberInAssembly ("base2.dll", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInNonReferencedAssemblyBase2", "Method()")] + [KeptMemberInAssembly ("library.dll", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInNonReferencedAssemblyChainedLibrary", "Method()")] + public class DynamicDependencyMethodInNonReferencedAssemblyChained + { + public static void Main () + { + var obj = new Foo (); + var val = obj.Method (); + Dependency (); + } + + [Kept] + [DynamicDependency (".ctor()", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInNonReferencedAssemblyChainedLibrary", "library")] + static void Dependency () + { + } + + [Kept] + [KeptMember (".ctor()")] + [KeptBaseType (typeof (DynamicDependencyMethodInNonReferencedAssemblyBase))] + class Foo : DynamicDependencyMethodInNonReferencedAssemblyBase + { + [Kept] + public override string Method () + { + return "Foo"; + } + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInNonReferencedAssemblyChainedReference.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInNonReferencedAssemblyChainedReference.cs new file mode 100644 index 000000000000..26a25634a3df --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInNonReferencedAssemblyChainedReference.cs @@ -0,0 +1,49 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies +{ + [IgnoreTestCase ("Currently failing")] + [SetupCompileBefore ("base.dll", new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyBase.cs" })] + [SetupCompileBefore ("base2.dll", new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyBase2.cs" }, references: new[] { "base.dll" }, addAsReference: false)] + [SetupCompileBefore ("reference.dll", new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyChainedLibrary.cs" }, references: new[] { "base.dll" }, addAsReference: false)] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyChainedReferenceLibrary.cs" }, references: new[] { "base.dll", "reference.dll" }, addAsReference: false)] + [KeptAssembly ("base.dll")] + [KeptAssembly ("base2.dll")] + [KeptAssembly ("library.dll")] + [KeptAssembly ("reference.dll")] + [KeptMemberInAssembly ("base.dll", typeof (DynamicDependencyMethodInNonReferencedAssemblyBase), "Method()")] + [KeptMemberInAssembly ("base2.dll", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInNonReferencedAssemblyBase2", "Method()")] + [KeptMemberInAssembly ("library.dll", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInNonReferencedAssemblyChainedReferenceLibrary", "Method()")] + [KeptMemberInAssembly ("reference.dll", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInNonReferencedAssemblyChainedLibrary", "Method()")] + public class DynamicDependencyMethodInNonReferencedAssemblyChainedReference + { + public static void Main () + { + var obj = new Foo (); + var val = obj.Method (); + Dependency (); + } + + [Kept] + [DynamicDependency (".ctor()", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInNonReferencedAssemblyChainedReferenceLibrary", "library")] + static void Dependency () + { + } + + [Kept] + [KeptMember (".ctor()")] + [KeptBaseType (typeof (DynamicDependencyMethodInNonReferencedAssemblyBase))] + class Foo : DynamicDependencyMethodInNonReferencedAssemblyBase + { + [Kept] + public override string Method () + { + return "Foo"; + } + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInNonReferencedAssemblyWithEmbeddedXml.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInNonReferencedAssemblyWithEmbeddedXml.cs new file mode 100644 index 000000000000..334cfb2c4be4 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyMethodInNonReferencedAssemblyWithEmbeddedXml.cs @@ -0,0 +1,49 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies +{ + /// + /// This is an acceptable bug with the currently implementation. Embedded link xml files will not be processed + /// + [IgnoreDescriptors (false)] + [SetupCompileBefore ("base.dll", new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyBase.cs" })] + [SetupCompileBefore ( + "DynamicDependencyMethodInNonReferencedAssemblyLibrary.dll", + new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyLibrary.cs" }, + references: new[] { "base.dll" }, + resources: new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyLibrary.xml" }, + addAsReference: false)] + [KeptAssembly ("base.dll")] + [RemovedMemberInAssembly ("DynamicDependencyMethodInNonReferencedAssemblyLibrary.dll", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInNonReferencedAssemblyLibrary", "UnusedMethod()")] + public class DynamicDependencyMethodInNonReferencedAssemblyWithEmbeddedXml + { + public static void Main () + { + var obj = new Foo (); + var val = obj.Method (); + Dependency (); + } + + [Kept] + [DynamicDependency ("#ctor()", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInNonReferencedAssemblyLibrary", "DynamicDependencyMethodInNonReferencedAssemblyLibrary")] + static void Dependency () + { + } + + [Kept] + [KeptMember (".ctor()")] + [KeptBaseType (typeof (DynamicDependencyMethodInNonReferencedAssemblyBase))] + class Foo : DynamicDependencyMethodInNonReferencedAssemblyBase + { + [Kept] + public override string Method () + { + return "Foo"; + } + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyOnUnusedMethodInNonReferencedAssembly.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyOnUnusedMethodInNonReferencedAssembly.cs new file mode 100644 index 000000000000..e76c1db84008 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyOnUnusedMethodInNonReferencedAssembly.cs @@ -0,0 +1,39 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies +{ + [SetupCompileBefore ("base.dll", new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyBase.cs" })] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyLibrary.cs" }, references: new[] { "base.dll" }, addAsReference: false)] + [KeptAssembly ("base.dll")] + [RemovedAssembly ("library.dll")] + [KeptMemberInAssembly ("base.dll", typeof (DynamicDependencyMethodInNonReferencedAssemblyBase), "Method()")] + public class DynamicDependencyOnUnusedMethodInNonReferencedAssembly + { + public static void Main () + { + var obj = new Foo (); + var val = obj.Method (); + } + + [DynamicDependency (".ctor()", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInNonReferencedAssemblyLibrary", "library")] + static void Dependency () + { + } + + [Kept] + [KeptMember (".ctor()")] + [KeptBaseType (typeof (DynamicDependencyMethodInNonReferencedAssemblyBase))] + class Foo : DynamicDependencyMethodInNonReferencedAssemblyBase + { + [Kept] + public override string Method () + { + return "Foo"; + } + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyOnUnusedMethodInNonReferencedAssemblyWithCopyUsedAction.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyOnUnusedMethodInNonReferencedAssemblyWithCopyUsedAction.cs new file mode 100644 index 000000000000..81fd282bcf7d --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyOnUnusedMethodInNonReferencedAssemblyWithCopyUsedAction.cs @@ -0,0 +1,22 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies +{ + [SetupLinkerUserAction ("copyused")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/DynamicDependencyOnUnusedMethodInNonReferencedAssemblyWithCopyUsedAction_Lib.cs" }, addAsReference: false)] + [RemovedAssembly ("library.dll")] + public class DynamicDependencyOnUnusedMethodInNonReferencedAssemblyWithCopyUsedAction + { + public static void Main () + { + } + + [DynamicDependency ("MethodPreservedViaDependencyAttribute()", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyOnUnusedMethodInNonReferencedAssemblyWithCopyUsedAction_Lib", "library")] + static void Dependency () + { + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyOnUnusedMethodInNonReferencedAssemblyWithEmbeddedXml.cs b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyOnUnusedMethodInNonReferencedAssemblyWithEmbeddedXml.cs new file mode 100644 index 000000000000..973630da8b87 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DynamicDependencies/DynamicDependencyOnUnusedMethodInNonReferencedAssemblyWithEmbeddedXml.cs @@ -0,0 +1,47 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DynamicDependencies +{ + /// + /// This test is here to ensure that link xml embedded in an assembly used by a [DynamicDependency] is not processed if the dependency is not used + /// + [IgnoreDescriptors (false)] + [SetupCompileBefore ("base.dll", new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyBase.cs" })] + [SetupCompileBefore ( + "DynamicDependencyMethodInNonReferencedAssemblyLibrary.dll", + new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyLibrary.cs" }, + references: new[] { "base.dll" }, + resources: new[] { "Dependencies/DynamicDependencyMethodInNonReferencedAssemblyLibrary.xml" }, + addAsReference: false)] + [KeptAssembly ("base.dll")] + [RemovedAssembly ("DynamicDependencyMethodInNonReferencedAssemblyLibrary.dll")] + public class DynamicDependencyOnUnusedMethodInNonReferencedAssemblyWithEmbeddedXml + { + public static void Main () + { + var obj = new Foo (); + var val = obj.Method (); + } + + [DynamicDependency ("#ctor()", "Mono.Linker.Tests.Cases.DynamicDependencies.Dependencies.DynamicDependencyMethodInNonReferencedAssemblyLibrary", "DynamicDependencyMethodInNonReferencedAssemblyLibrary")] + static void Dependency () + { + } + + [Kept] + [KeptMember (".ctor()")] + [KeptBaseType (typeof (DynamicDependencyMethodInNonReferencedAssemblyBase))] + class Foo : DynamicDependencyMethodInNonReferencedAssemblyBase + { + [Kept] + public override string Method () + { + return "Foo"; + } + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/DynamicDependencyPreservesInterfaceMethod.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/DynamicDependencyPreservesInterfaceMethod.cs new file mode 100644 index 000000000000..03a7e8fee88e --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/DynamicDependencyPreservesInterfaceMethod.cs @@ -0,0 +1,38 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtor +{ + /// + /// The interface can still be removed in this case because PreserveDependency is just preserving Foo() on the current type + /// + public class DynamicDependencyPreservesInterfaceMethod + { + public static void Main () + { + StaticMethodOnlyUsed.StaticMethod (); + } + + interface IUnusedInterface + { + void Foo (); + } + + [Kept] + class StaticMethodOnlyUsed : IUnusedInterface + { + [Kept] + public void Foo () + { + } + + [Kept] + [DynamicDependency ("Foo")] + public static void StaticMethod () + { + } + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyDeprecated.cs b/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyDeprecated.cs new file mode 100644 index 000000000000..a5cbc2d99d91 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyDeprecated.cs @@ -0,0 +1,130 @@ +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.PreserveDependencies +{ +#if !NETCOREAPP + [IgnoreTestCase ("This test checks that PreserveDependency correctly issues a warning on .NET Core where it is deprecated.")] +#endif + [SetupCompileBefore ("FakeSystemAssembly.dll", new[] { "Dependencies/PreserveDependencyAttribute.cs" })] + [LogContains ("IL2033: Deprecated PreserveDependencyAttribute on 'System.Void Mono.Linker.Tests.Cases.PreserveDependencies.PreserveDependencyDeprecated/B::Method()'. Use DynamicDependencyAttribute instead.")] + [LogContains ("IL2033: Deprecated PreserveDependencyAttribute on 'System.Void Mono.Linker.Tests.Cases.PreserveDependencies.PreserveDependencyDeprecated/B::SameContext()'. Use DynamicDependencyAttribute instead.")] + [LogContains ("IL2033: Deprecated PreserveDependencyAttribute on 'System.Void Mono.Linker.Tests.Cases.PreserveDependencies.PreserveDependencyDeprecated/B::Broken()'. Use DynamicDependencyAttribute instead.")] + [LogContains ("IL2033: Deprecated PreserveDependencyAttribute on 'System.Void Mono.Linker.Tests.Cases.PreserveDependencies.PreserveDependencyDeprecated/B::Conditional()'. Use DynamicDependencyAttribute instead.")] + class PreserveDependencyDeprecated + { + public static void Main () + { + new B (); // Needed to avoid lazy body marking stubbing + + B.Method (); + B.SameContext (); + B.Broken (); + B.Conditional (); + } + + [KeptMember (".ctor()")] + class B + { + [Kept] + int field; + + [Kept] + void Method2 (out sbyte arg) + { + arg = 1; + } + + [Kept] + [PreserveDependency ("Dependency1()", "Mono.Linker.Tests.Cases.PreserveDependencies.D")] + [PreserveDependency ("Dependency2`1 ( T[] , System.Int32 ) ", "Mono.Linker.Tests.Cases.PreserveDependencies.D")] + [PreserveDependency (".ctor()", "Mono.Linker.Tests.Cases.PreserveDependencies.D")] // To avoid lazy body marking stubbing + [PreserveDependency ("field", "Mono.Linker.Tests.Cases.PreserveDependencies.D")] + [PreserveDependency ("NextOne (Mono.Linker.Tests.Cases.PreserveDependencies.PreserveDependencyDeprecated+Nested&)", "Mono.Linker.Tests.Cases.PreserveDependencies.PreserveDependencyDeprecated+Nested")] + [PreserveDependency (".cctor()", "Mono.Linker.Tests.Cases.PreserveDependencies.PreserveDependencyDeprecated+Nested")] + // Dependency on a property itself should be expressed as a dependency on one or both accessor methods + [PreserveDependency ("get_Property()", "Mono.Linker.Tests.Cases.PreserveDependencies.D")] + public static void Method () + { + } + + [Kept] + [PreserveDependency ("field")] + [PreserveDependency ("Method2 (System.SByte&)")] + public static void SameContext () + { + } + + [Kept] + [PreserveDependency ("MissingType", "Mono.Linker.Tests.Cases.PreserveDependencies.MissingType")] + [PreserveDependency ("MissingMethod", "Mono.Linker.Tests.Cases.PreserveDependencies.D")] + [PreserveDependency ("Dependency2`1 (T, System.Int32, System.Object)", "Mono.Linker.Tests.Cases.PreserveDependencies.D")] + [PreserveDependency ("")] + [PreserveDependency (".ctor()", "Mono.Linker.Tests.Cases.PreserveDependencies.PreserveDependencyDeprecated+NestedStruct")] + [PreserveDependency (".cctor()", "Mono.Linker.Tests.Cases.PreserveDependencies.D")] + public static void Broken () + { + } + + [Kept] + [PreserveDependency ("ConditionalTest()", "Mono.Linker.Tests.Cases.PreserveDependencies.D", Condition = "don't have it")] + public static void Conditional () + { + } + } + + class Nested + { + [Kept] + private static void NextOne (ref Nested arg1) + { + } + + [Kept] + static Nested () + { + + } + } + + struct NestedStruct + { + public string Name; + + public NestedStruct (string name) + { + Name = name; + } + } + } + + [KeptMember (".ctor()")] + class D + { + [Kept] + internal string field; + + [Kept] + internal void Dependency1 () + { + } + + internal void Dependency1 (long arg1) + { + } + + [Kept] + internal void Dependency2 (T[] arg1, int arg2) + { + } + + [Kept] + [KeptBackingField] + internal string Property { [Kept] get; set; } + + internal void ConditionalTest () + { + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyMethod.cs b/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyMethod.cs index c34ec6842426..009802148f4f 100644 --- a/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyMethod.cs +++ b/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyMethod.cs @@ -4,7 +4,6 @@ namespace Mono.Linker.Tests.Cases.PreserveDependencies { - [SetupCompileBefore ("FakeSystemAssembly.dll", new[] { "Dependencies/PreserveDependencyAttribute.cs" })] [LogContains ("Could not resolve 'Mono.Linker.Tests.Cases.PreserveDependencies.MissingType' type dependency")] [LogContains ("Could not resolve dependency member 'MissingMethod' declared in type 'Mono.Linker.Tests.Cases.PreserveDependencies.C'")] diff --git a/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyMethodInAssembly.cs b/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyMethodInAssembly.cs index c41022d9df77..8f5ca86d6721 100644 --- a/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyMethodInAssembly.cs +++ b/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyMethodInAssembly.cs @@ -6,7 +6,7 @@ namespace Mono.Linker.Tests.Cases.PreserveDependencies { [KeptMemberInAssembly ("library.dll", "Mono.Linker.Tests.Cases.PreserveDependencies.Dependencies.PreserveDependencyMethodInAssemblyLibrary", ".ctor()")] [SetupCompileBefore ("FakeSystemAssembly.dll", new[] { "Dependencies/PreserveDependencyAttribute.cs" })] - [SetupCompileBefore ("library.dll", new[] { "Dependencies/PreserveDependencyMethodInAssemblyLibrary.cs" }, new[] { "FakeSystemAssembly.dll" })] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/PreserveDependencyMethodInAssemblyLibrary.cs" })] public class PreserveDependencyMethodInAssembly { public static void Main () diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/WorksWithDynamicDependency.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/WorksWithDynamicDependency.cs new file mode 100644 index 000000000000..0a78b8ad5fb9 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/WorksWithDynamicDependency.cs @@ -0,0 +1,37 @@ +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.UnreachableBody +{ + [SetupLinkerArgument ("--enable-opt", "unreachablebodies")] + public class WorksWithDynamicDependency + { + public static void Main () + { + Foo.StaticMethod (); + } + + [Kept] + class Foo + { + [Kept] + [DynamicDependency ("InstanceMethod()")] + public static void StaticMethod () + { + } + + [Kept] + [ExpectBodyModified] + public void InstanceMethod () + { + UsedByMethod (); + } + + void UsedByMethod () + { + } + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests/TestCases/TestDatabase.cs b/test/Mono.Linker.Tests/TestCases/TestDatabase.cs index 5b3cce796f86..f46086e43d0c 100644 --- a/test/Mono.Linker.Tests/TestCases/TestDatabase.cs +++ b/test/Mono.Linker.Tests/TestCases/TestDatabase.cs @@ -92,6 +92,11 @@ public static IEnumerable PreserveDependenciesTests () return NUnitCasesBySuiteName ("PreserveDependencies"); } + public static IEnumerable DynamicDependenciesTests () + { + return NUnitCasesBySuiteName ("DynamicDependencies"); + } + public static IEnumerable LibrariesTests () { return NUnitCasesBySuiteName ("Libraries"); diff --git a/test/Mono.Linker.Tests/TestCases/TestSuites.cs b/test/Mono.Linker.Tests/TestCases/TestSuites.cs index dab23fcbf931..739084f0bd09 100644 --- a/test/Mono.Linker.Tests/TestCases/TestSuites.cs +++ b/test/Mono.Linker.Tests/TestCases/TestSuites.cs @@ -109,6 +109,12 @@ public void PreserveDependenciesTests (TestCase testCase) Run (testCase); } + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.DynamicDependenciesTests))] + public void DynamicDependenciesTests (TestCase testCase) + { + Run (testCase); + } + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.SymbolsTests))] public void SymbolsTests (TestCase testCase) {