diff options
author | Mateo Torres-Ruiz <mateoatr@users.noreply.github.com> | 2021-04-01 14:56:37 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-01 14:56:37 +0300 |
commit | 1d96189abb1b2cfe00dff817c63b84b25534746a (patch) | |
tree | 37bb5a8f110a36727054c3f6dc4acc6aa1dccaac | |
parent | f4147aa53a425420f725c9cb5dbd36552df99372 (diff) |
Change behavior of copy action to not rewrite assemblies (#1869)
Co-authored-by: Marek Safar <marek.safar@gmail.com>
43 files changed, 755 insertions, 143 deletions
diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index b73865d7f..5eb428ed3 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -966,7 +966,7 @@ namespace Mono.Linker.Dataflow } foreach (var typeNameValue in methodParams[0].UniqueValues ()) { if (typeNameValue is KnownStringValue knownStringValue) { - TypeReference foundTypeRef = _context.TypeNameResolver.ResolveTypeName (knownStringValue.Contents); + TypeReference foundTypeRef = _context.TypeNameResolver.ResolveTypeName (knownStringValue.Contents, out AssemblyDefinition typeAssembly); TypeDefinition foundType = foundTypeRef?.ResolveToMainTypeDefinition (); if (foundType == null) { // Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back. @@ -974,6 +974,7 @@ namespace Mono.Linker.Dataflow } else { reflectionContext.RecordRecognizedPattern (foundType, () => _markStep.MarkTypeVisibleToReflection (foundTypeRef, new DependencyInfo (DependencyKind.AccessedViaReflection, callingMethodDefinition), callingMethodDefinition)); methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (foundType)); + _context.MarkingHelpers.MarkMatchingExportedType (foundType, typeAssembly, new DependencyInfo (DependencyKind.AccessedViaReflection, foundType)); } } else if (typeNameValue == NullValue.Instance) { reflectionContext.RecordHandledPattern (); @@ -1977,7 +1978,7 @@ namespace Mono.Linker.Dataflow } else if (uniqueValue is SystemTypeValue systemTypeValue) { MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, systemTypeValue.TypeRepresented, requiredMemberTypes); } else if (uniqueValue is KnownStringValue knownStringValue) { - TypeReference typeRef = _context.TypeNameResolver.ResolveTypeName (knownStringValue.Contents); + TypeReference typeRef = _context.TypeNameResolver.ResolveTypeName (knownStringValue.Contents, out AssemblyDefinition typeAssembly); TypeDefinition foundType = typeRef?.ResolveToMainTypeDefinition (); if (foundType == null) { // Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back. @@ -1985,6 +1986,7 @@ namespace Mono.Linker.Dataflow } else { MarkType (ref reflectionContext, typeRef); MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, foundType, requiredMemberTypes); + _context.MarkingHelpers.MarkMatchingExportedType (foundType, typeAssembly, new DependencyInfo (DependencyKind.DynamicallyAccessedMember, foundType)); } } else if (uniqueValue == NullValue.Instance) { // Ignore - probably unreachable path as it would fail at runtime anyway. diff --git a/src/linker/Linker.Steps/DescriptorMarker.cs b/src/linker/Linker.Steps/DescriptorMarker.cs index e9479dddc..5915f974e 100644 --- a/src/linker/Linker.Steps/DescriptorMarker.cs +++ b/src/linker/Linker.Steps/DescriptorMarker.cs @@ -45,6 +45,9 @@ namespace Mono.Linker.Steps if (GetTypePreserve (nav) == TypePreserve.All) { foreach (var type in assembly.MainModule.Types) MarkAndPreserveAll (type); + + foreach (var exportedType in assembly.MainModule.ExportedTypes) + _context.MarkingHelpers.MarkExportedType (exportedType, assembly.MainModule, new DependencyInfo (DependencyKind.XmlDescriptor, assembly.MainModule)); } else { ProcessTypes (assembly, nav, warnOnUnresolvedTypes); ProcessNamespaces (assembly, nav); diff --git a/src/linker/Linker.Steps/LinkAttributesParser.cs b/src/linker/Linker.Steps/LinkAttributesParser.cs index 5a0551b9c..5d5516719 100644 --- a/src/linker/Linker.Steps/LinkAttributesParser.cs +++ b/src/linker/Linker.Steps/LinkAttributesParser.cs @@ -264,7 +264,7 @@ namespace Mono.Linker.Steps if (!typeref.IsTypeOf ("System", "Type")) goto default; - TypeReference type = _context.TypeNameResolver.ResolveTypeName (svalue); + TypeReference type = _context.TypeNameResolver.ResolveTypeName (svalue, out _); if (type == null) { _context.LogError ($"Could not resolve custom attribute type value '{svalue}'", 1044, _xmlDocumentLocation); return null; @@ -284,7 +284,7 @@ namespace Mono.Linker.Steps if (string.IsNullOrEmpty (typeName)) typeName = "System.String"; - TypeReference typeref = _context.TypeNameResolver.ResolveTypeName (typeName); + TypeReference typeref = _context.TypeNameResolver.ResolveTypeName (typeName, out _); if (typeref == null) { _context.LogError ($"The type '{typeName}' used with attribute value '{iterator.Current.Value}' could not be found", 1041, _xmlDocumentLocation); return null; diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index 8609ade89..d0799e0ab 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -189,6 +189,7 @@ namespace Mono.Linker.Steps } public AnnotationStore Annotations => _context.Annotations; + public MarkingHelpers MarkingHelpers => _context.MarkingHelpers; public Tracer Tracer => _context.Tracer; public virtual void Process (LinkContext context) @@ -365,40 +366,12 @@ namespace Mono.Linker.Steps void Process () { - while (ProcessPrimaryQueue () || ProcessMarkedPending () || ProcessLazyAttributes () || ProcessLateMarkedAttributes () || MarkFullyPreservedAssemblies () || ProcessInternalsVisibleAttributes ()) { - - // deal with [TypeForwardedTo] pseudo-attributes - - // This marks all exported types that resolve to a marked type. Note that it - // may mark unused exported types that happen to resolve to a type which was - // marked from a different reference. This seems incorrect. - // Note also that we may still remove type forwarder assemblies with marked exports in SweepStep, - // if they don't contain other used code. - // https://github.com/mono/linker/issues/1740 - foreach (AssemblyDefinition assembly in _context.GetAssemblies ()) { - if (!assembly.MainModule.HasExportedTypes) - continue; - - foreach (var exported in assembly.MainModule.ExportedTypes) { - bool isForwarder = exported.IsForwarder; - var declaringType = exported.DeclaringType; - while (!isForwarder && (declaringType != null)) { - isForwarder = declaringType.IsForwarder; - declaringType = declaringType.DeclaringType; - } - - if (!isForwarder) - continue; - TypeDefinition type = exported.Resolve (); - if (type == null) - continue; - if (!Annotations.IsMarked (type)) - continue; - var di = new DependencyInfo (DependencyKind.ExportedType, type); - _context.MarkingHelpers.MarkExportedType (exported, assembly.MainModule, di); - } - } - } + while (ProcessPrimaryQueue () || + ProcessMarkedPending () || + ProcessLazyAttributes () || + ProcessLateMarkedAttributes () || + MarkFullyPreservedAssemblies () || + ProcessInternalsVisibleAttributes ()) ; ProcessPendingTypeChecks (); } @@ -858,6 +831,8 @@ namespace Mono.Linker.Steps _context.LogWarning ($"Unresolved type '{typeName}' in DynamicDependencyAttribute", 2036, context); return; } + + MarkingHelpers.MarkMatchingExportedType (type, assembly, new DependencyInfo (DependencyKind.DynamicDependency, type)); } else if (dynamicDependency.Type is TypeReference typeReference) { type = typeReference.Resolve (); if (type == null) { @@ -947,14 +922,20 @@ namespace Mono.Linker.Steps assembly = null; } - TypeDefinition td; + TypeDefinition td = null; if (args.Count >= 2 && args[1].Value is string typeName) { - td = _context.TypeNameResolver.ResolveTypeName (assembly ?? (context as MemberReference).Module.Assembly, typeName)?.Resolve (); + AssemblyDefinition assemblyDef = assembly ?? (context as MemberReference).Module.Assembly; + TypeReference tr = _context.TypeNameResolver.ResolveTypeName (assemblyDef, typeName); + if (tr != null) + td = tr.Resolve (); + if (td == null) { _context.LogWarning ( $"Could not resolve dependency type '{typeName}' specified in a `PreserveDependency` attribute", 2004, context); return; } + + MarkingHelpers.MarkMatchingExportedType (td, assemblyDef, new DependencyInfo (DependencyKind.PreservedDependency, ca)); } else { td = context.DeclaringType.Resolve (); } @@ -1357,15 +1338,20 @@ namespace Mono.Linker.Steps { Debug.Assert (Annotations.IsProcessed (assembly)); + ModuleDefinition module = assembly.MainModule; MarkCustomAttributes (assembly, new DependencyInfo (DependencyKind.AssemblyOrModuleAttribute, assembly), null); - MarkCustomAttributes (assembly.MainModule, new DependencyInfo (DependencyKind.AssemblyOrModuleAttribute, assembly.MainModule), null); + MarkCustomAttributes (module, new DependencyInfo (DependencyKind.AssemblyOrModuleAttribute, module), null); - if (assembly.MainModule.HasExportedTypes) { - // TODO: This needs more work accross all steps + foreach (TypeDefinition type in module.Types) + MarkEntireType (type, includeBaseTypes: false, includeInterfaceTypes: false, new DependencyInfo (DependencyKind.TypeInAssembly, assembly), null); + + foreach (ExportedType exportedType in module.ExportedTypes) { + MarkingHelpers.MarkExportedType (exportedType, module, new DependencyInfo (DependencyKind.ExportedType, assembly)); + MarkingHelpers.MarkForwardedScope (new TypeReference (exportedType.Namespace, exportedType.Name, module, exportedType.Scope)); } - foreach (TypeDefinition type in assembly.MainModule.Types) - MarkEntireType (type, includeBaseTypes: false, includeInterfaceTypes: false, new DependencyInfo (DependencyKind.TypeInAssembly, assembly), null); + foreach (TypeReference typeReference in module.GetTypeReferences ()) + MarkingHelpers.MarkForwardedScope (typeReference); } void ProcessModuleType (AssemblyDefinition assembly) @@ -1867,21 +1853,24 @@ namespace Mono.Linker.Steps if (args.Count < 1) return; - TypeDefinition tdef = null; + TypeDefinition typeDefinition = null; switch (attribute.ConstructorArguments[0].Value) { case string s: - tdef = _context.TypeNameResolver.ResolveTypeName (s)?.Resolve (); + typeDefinition = _context.TypeNameResolver.ResolveTypeName (s, out AssemblyDefinition assemblyDefinition)?.Resolve (); + if (typeDefinition != null) + MarkingHelpers.MarkMatchingExportedType (typeDefinition, assemblyDefinition, new DependencyInfo (DependencyKind.CustomAttribute, provider)); + break; case TypeReference type: - tdef = type.Resolve (); + typeDefinition = type.Resolve (); break; } - if (tdef == null) + if (typeDefinition == null) return; Tracer.AddDirectDependency (attribute, new DependencyInfo (DependencyKind.CustomAttribute, provider), marked: false); - MarkMethodsIf (tdef.Methods, predicate, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), sourceLocationMember); + MarkMethodsIf (typeDefinition.Methods, predicate, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), sourceLocationMember); } void MarkTypeWithDebuggerDisplayAttribute (TypeDefinition type, CustomAttribute attribute) diff --git a/src/linker/Linker.Steps/OutputStep.cs b/src/linker/Linker.Steps/OutputStep.cs index f56b2c750..5ac66ab83 100644 --- a/src/linker/Linker.Steps/OutputStep.cs +++ b/src/linker/Linker.Steps/OutputStep.cs @@ -271,42 +271,24 @@ namespace Mono.Linker.Steps protected virtual void CopyAssembly (AssemblyDefinition assembly, string directory) { - // Special case. When an assembly has embedded pdbs, link symbols is not enabled, and the assembly's action is copy, - // we want to match the behavior of assemblies with the other symbol types and end up with an assembly that does not have symbols. - // In order to do that, we can't simply copy files. We need to write the assembly without symbols - if (assembly.MainModule.HasSymbols && !Context.LinkSymbols && assembly.MainModule.SymbolReader is EmbeddedPortablePdbReader) { - WriteAssembly (assembly, directory, new WriterParameters ()); - return; - } - FileInfo fi = GetOriginalAssemblyFileInfo (assembly); string target = Path.GetFullPath (Path.Combine (directory, fi.Name)); string source = fi.FullName; + if (source == target) return; - CopyFileAndRemoveReadOnly (source, target); - + File.Copy (source, target, true); if (!Context.LinkSymbols) return; var mdb = source + ".mdb"; if (File.Exists (mdb)) - CopyFileAndRemoveReadOnly (mdb, target + ".mdb"); + File.Copy (mdb, target + ".mdb", true); var pdb = Path.ChangeExtension (source, "pdb"); if (File.Exists (pdb)) - CopyFileAndRemoveReadOnly (pdb, Path.ChangeExtension (target, "pdb")); - } - - static void CopyFileAndRemoveReadOnly (string src, string dest) - { - File.Copy (src, dest, true); - - System.IO.FileAttributes attrs = File.GetAttributes (dest); - - if ((attrs & System.IO.FileAttributes.ReadOnly) == System.IO.FileAttributes.ReadOnly) - File.SetAttributes (dest, attrs & ~System.IO.FileAttributes.ReadOnly); + File.Copy (pdb, Path.ChangeExtension (target, "pdb"), true); } protected virtual string GetAssemblyFileName (AssemblyDefinition assembly, string directory) diff --git a/src/linker/Linker.Steps/SweepStep.cs b/src/linker/Linker.Steps/SweepStep.cs index 209e8c17b..1698d5d13 100644 --- a/src/linker/Linker.Steps/SweepStep.cs +++ b/src/linker/Linker.Steps/SweepStep.cs @@ -97,13 +97,13 @@ namespace Mono.Linker.Steps { var action = Annotations.GetAction (assembly); switch (action) { - case AssemblyAction.Skip: + case AssemblyAction.Copy: case AssemblyAction.Delete: case AssemblyAction.Link: case AssemblyAction.Save: + case AssemblyAction.Skip: return; - case AssemblyAction.Copy: case AssemblyAction.CopyUsed: case AssemblyAction.AddBypassNGen: case AssemblyAction.AddBypassNGenUsed: @@ -121,7 +121,6 @@ namespace Mono.Linker.Steps switch (action) { case AssemblyAction.CopyUsed: - case AssemblyAction.Copy: // // Assembly has a reference to another assembly which has been fully removed. This can // happen when for example the reference assembly is 'copy-used' and it's not needed. @@ -172,22 +171,14 @@ namespace Mono.Linker.Steps break; case AssemblyAction.CopyUsed: - Annotations.SetAction (assembly, AssemblyAction.Copy); - goto case AssemblyAction.Copy; + AssemblyAction assemblyAction = AssemblyAction.Copy; + if (!Context.KeepTypeForwarderOnlyAssemblies && SweepTypeForwarders (assembly)) + assemblyAction = AssemblyAction.Save; - case AssemblyAction.Copy: - // - // Facade assemblies can have unused forwarders pointing to - // removed type (when facades are kept) - // - // main.exe -> facade.dll -> lib.dll - // link | copy | link - // - // when main.exe has unused reference to type in lib.dll - // - if (SweepTypeForwarders (assembly)) - Annotations.SetAction (assembly, AssemblyAction.Save); + Annotations.SetAction (assembly, assemblyAction); + break; + case AssemblyAction.Copy: break; case AssemblyAction.Link: diff --git a/src/linker/Linker/MarkingHelpers.cs b/src/linker/Linker/MarkingHelpers.cs index f59ffd162..ca2a9b36c 100644 --- a/src/linker/Linker/MarkingHelpers.cs +++ b/src/linker/Linker/MarkingHelpers.cs @@ -11,12 +11,33 @@ namespace Mono.Linker _context = context; } - public void MarkExportedType (ExportedType type, ModuleDefinition module, in DependencyInfo reason) + public void MarkMatchingExportedType (TypeDefinition typeToMatch, AssemblyDefinition assembly, in DependencyInfo reason) { - if (!_context.Annotations.MarkProcessed (type, reason)) + if (typeToMatch == null || assembly == null) return; - if (_context.KeepTypeForwarderOnlyAssemblies) - _context.Annotations.Mark (module, new DependencyInfo (DependencyKind.ModuleOfExportedType, type)); + + if (assembly.MainModule.GetMatchingExportedType (typeToMatch, out var exportedType)) + MarkExportedType (exportedType, assembly.MainModule, reason); + } + + public void MarkExportedType (ExportedType exportedType, ModuleDefinition module, in DependencyInfo reason) + { + if (!_context.Annotations.MarkProcessed (exportedType, reason)) + return; + + _context.Annotations.Mark (module, reason); + } + + public void MarkForwardedScope (TypeReference typeReference) + { + if (typeReference == null) + return; + + if (typeReference.Scope is AssemblyNameReference) { + var assembly = _context.Resolve (typeReference.Scope); + if (assembly != null && assembly.MainModule.GetMatchingExportedType (typeReference.Resolve (), out var exportedType)) + MarkExportedType (exportedType, assembly.MainModule, new DependencyInfo (DependencyKind.ExportedType, typeReference)); + } } } } diff --git a/src/linker/Linker/ModuleDefinitionExtensions.cs b/src/linker/Linker/ModuleDefinitionExtensions.cs index f846c96f4..a79c1efd7 100644 --- a/src/linker/Linker/ModuleDefinitionExtensions.cs +++ b/src/linker/Linker/ModuleDefinitionExtensions.cs @@ -11,6 +11,21 @@ namespace Mono.Linker (module.Attributes & ModuleAttributes.ILLibrary) != 0; } + public static bool GetMatchingExportedType (this ModuleDefinition module, TypeDefinition typeDefinition, out ExportedType exportedType) + { + exportedType = null; + if (!module.HasExportedTypes || typeDefinition == null) + return false; + + foreach (var et in module.ExportedTypes) + if (et.Resolve () == typeDefinition) { + exportedType = et; + return true; + } + + return false; + } + public static TypeDefinition ResolveType (this ModuleDefinition module, string typeFullName) { if (typeFullName == null) diff --git a/src/linker/Linker/TypeNameResolver.cs b/src/linker/Linker/TypeNameResolver.cs index 3eaa5556b..1dac10ef8 100644 --- a/src/linker/Linker/TypeNameResolver.cs +++ b/src/linker/Linker/TypeNameResolver.cs @@ -13,8 +13,9 @@ namespace Mono.Linker _context = context; } - public TypeReference ResolveTypeName (string typeNameString) + public TypeReference ResolveTypeName (string typeNameString, out AssemblyDefinition typeAssembly) { + typeAssembly = null; if (string.IsNullOrEmpty (typeNameString)) return null; @@ -28,13 +29,19 @@ namespace Mono.Linker } if (parsedTypeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) { - return ResolveTypeName (null, assemblyQualifiedTypeName); + typeAssembly = _context.TryResolve (assemblyQualifiedTypeName.AssemblyName.Name); + if (typeAssembly == null) + return null; + + return ResolveTypeName (typeAssembly, assemblyQualifiedTypeName.TypeName); } foreach (var assemblyDefinition in _context.GetReferencedAssemblies ()) { var foundType = ResolveTypeName (assemblyDefinition, parsedTypeName); - if (foundType != null) + if (foundType != null) { + typeAssembly = assemblyDefinition; return foundType; + } } return null; diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveAnExportedType.cs b/test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveAnExportedType.cs index 90017d03f..b083b446f 100644 --- a/test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveAnExportedType.cs +++ b/test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveAnExportedType.cs @@ -10,9 +10,9 @@ namespace Mono.Linker.Tests.Cases.LinkXml // Add another assembly in that uses the forwarder just to make things a little more complex [SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/CanPreserveAnExportedType_Forwarder.cs" }, references: new[] { "Library.dll" })] - [RemovedAssembly ("Forwarder.dll")] [KeptMemberInAssembly ("Library.dll", typeof (CanPreserveAnExportedType_Library), "Field1", "Method()", ".ctor()")] [SetupLinkerDescriptorFile ("CanPreserveAnExportedType.xml")] + [KeptTypeInAssembly ("Forwarder.dll", typeof (CanPreserveAnExportedType_Library))] class CanPreserveAnExportedType { public static void Main () diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveExportedTypesUsingRegex.cs b/test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveExportedTypesUsingRegex.cs index 9a54cce19..d84f952a0 100644 --- a/test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveExportedTypesUsingRegex.cs +++ b/test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveExportedTypesUsingRegex.cs @@ -10,9 +10,9 @@ namespace Mono.Linker.Tests.Cases.LinkXml // Add another assembly in that uses the forwarder just to make things a little more complex [SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/CanPreserveAnExportedType_Forwarder.cs" }, references: new[] { "Library.dll" })] - [RemovedAssembly ("Forwarder.dll")] [KeptMemberInAssembly ("Library.dll", typeof (CanPreserveAnExportedType_Library), "Field1", "Method()", ".ctor()")] [SetupLinkerDescriptorFile ("CanPreserveExportedTypesUsingRegex.xml")] + [KeptTypeInAssembly ("Forwarder.dll", typeof (CanPreserveAnExportedType_Library))] class CanPreserveExportedTypesUsingRegex { public static void Main () diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKept.cs b/test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKept.cs index 44328fb19..c990ddbfa 100644 --- a/test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKept.cs +++ b/test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKept.cs @@ -5,15 +5,17 @@ namespace Mono.Linker.Tests.Cases.LinkXml { [SetupLinkerDescriptorFile ("UsedNonRequiredExportedTypeIsKept.xml")] - [SetupCompileBefore ("lib.dll", new[] { "Dependencies/UsedNonRequiredExportedTypeIsKept_lib.cs" })] - [SetupCompileAfter ("libfwd.dll", new[] { "Dependencies/UsedNonRequiredExportedTypeIsKept_lib.cs" })] - [SetupCompileAfter ("lib.dll", new[] { "Dependencies/UsedNonRequiredExportedTypeIsKept_fwd.cs" }, references: new[] { "libfwd.dll" })] + [SetupCompileBefore ("libfwd.dll", new[] { "Dependencies/UsedNonRequiredExportedTypeIsKept_lib.cs" })] + [SetupCompileAfter ("lib.dll", new[] { "Dependencies/UsedNonRequiredExportedTypeIsKept_lib.cs" })] + [SetupCompileAfter ("libfwd.dll", new[] { "Dependencies/UsedNonRequiredExportedTypeIsKept_fwd.cs" }, references: new[] { "lib.dll" })] + + // Note that forwarders which are referenced from within a descriptor XML file are kept -- any exported type referenced through a type + // name string should be kept. + [KeptMemberInAssembly ("libfwd.dll", typeof (UsedNonRequiredExportedTypeIsKept_Used1), "field")] + [KeptMemberInAssembly ("libfwd.dll", typeof (UsedNonRequiredExportedTypeIsKept_Used2), "Method()")] + [KeptMemberInAssembly ("libfwd.dll", typeof (UsedNonRequiredExportedTypeIsKept_Used3), "Method()")] + [KeptAssembly ("lib.dll")] - [KeptAssembly ("libfwd.dll")] - [KeptMemberInAssembly ("libfwd.dll", typeof (UsedNonRequiredExportedTypeIsKept_Used1), "field", ExpectationAssemblyName = "lib.dll")] - [KeptMemberInAssembly ("libfwd.dll", typeof (UsedNonRequiredExportedTypeIsKept_Used2), "Method()", ExpectationAssemblyName = "lib.dll")] - [KeptMemberInAssembly ("libfwd.dll", typeof (UsedNonRequiredExportedTypeIsKept_Used3), "Method()", ExpectationAssemblyName = "lib.dll")] - [RemovedAssembly ("lib.dll")] public class UsedNonRequiredExportedTypeIsKept { public static void Main () diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKept.xml b/test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKept.xml index 223744d49..659c05db5 100644 --- a/test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKept.xml +++ b/test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKept.xml @@ -1,5 +1,5 @@ <linker> - <assembly fullname="lib"> + <assembly fullname="libfwd"> <type fullname="Mono.Linker.Tests.Cases.LinkXml.UsedNonRequiredExportedTypeIsKept_Used1" preserve="fields" required="false"/> <type fullname="Mono.Linker.Tests.Cases.LinkXml.UsedNonRequiredExportedTypeIsKept_Used2" preserve="methods" required="false"/> <type fullname="Mono.Linker.Tests.Cases.LinkXml.UsedNonRequiredExportedTypeIsKept_Used3" preserve="all" required="false"/> diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKeptWhenRooted.cs b/test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKeptWhenRooted.cs index eec9e5bfc..96630532a 100644 --- a/test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKeptWhenRooted.cs +++ b/test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKeptWhenRooted.cs @@ -4,16 +4,16 @@ using Mono.Linker.Tests.Cases.Expectations.Metadata; namespace Mono.Linker.Tests.Cases.LinkXml { [SetupLinkerDescriptorFile ("UsedNonRequiredExportedTypeIsKeptWhenRooted.xml")] - [SetupLinkerArgument ("-a", "lib.dll", "visible")] + [SetupLinkerArgument ("-a", "libfwd.dll", "visible")] - [SetupCompileBefore ("lib.dll", new[] { "Dependencies/UsedNonRequiredExportedTypeIsKeptWhenRooted_lib.cs" })] - [SetupCompileAfter ("libfwd.dll", new[] { "Dependencies/UsedNonRequiredExportedTypeIsKeptWhenRooted_lib.cs" })] - [SetupCompileAfter ("lib.dll", new[] { "Dependencies/UsedNonRequiredExportedTypeIsKeptWhenRooted_fwd.cs" }, references: new[] { "libfwd.dll" })] + [SetupCompileBefore ("libfwd.dll", new[] { "Dependencies/UsedNonRequiredExportedTypeIsKeptWhenRooted_lib.cs" })] + [SetupCompileAfter ("lib.dll", new[] { "Dependencies/UsedNonRequiredExportedTypeIsKeptWhenRooted_lib.cs" })] + [SetupCompileAfter ("libfwd.dll", new[] { "Dependencies/UsedNonRequiredExportedTypeIsKeptWhenRooted_fwd.cs" }, references: new[] { "lib.dll" })] - [KeptAssembly ("libfwd.dll")] [KeptAssembly ("lib.dll")] - [KeptMemberInAssembly ("libfwd.dll", typeof (UsedNonRequiredExportedTypeIsKeptWhenRooted_Used), "field", ExpectationAssemblyName = "lib.dll")] - [KeptMemberInAssembly ("libfwd.dll", typeof (UsedNonRequiredExportedTypeIsKeptWhenRooted_Used), "Method()", ExpectationAssemblyName = "lib.dll")] + [KeptAssembly ("libfwd.dll")] + [KeptMemberInAssembly ("lib.dll", typeof (UsedNonRequiredExportedTypeIsKeptWhenRooted_Used), "field", ExpectationAssemblyName = "libfwd.dll")] + [KeptMemberInAssembly ("lib.dll", typeof (UsedNonRequiredExportedTypeIsKeptWhenRooted_Used), "Method()", ExpectationAssemblyName = "libfwd.dll")] public class UsedNonRequiredExportedTypeIsKeptWhenRooted { public static void Main () diff --git a/test/Mono.Linker.Tests.Cases/References/AssemblyOnlyUsedByUsingWithCsc.cs b/test/Mono.Linker.Tests.Cases/References/AssemblyOnlyUsedByUsingWithCsc.cs index 48b87d074..7be38642a 100644 --- a/test/Mono.Linker.Tests.Cases/References/AssemblyOnlyUsedByUsingWithCsc.cs +++ b/test/Mono.Linker.Tests.Cases/References/AssemblyOnlyUsedByUsingWithCsc.cs @@ -1,4 +1,5 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; using Mono.Linker.Tests.Cases.Expectations.Metadata; using Mono.Linker.Tests.Cases.References.Dependencies; @@ -20,11 +21,7 @@ namespace Mono.Linker.Tests.Cases.References // We library should be gone. The `using` statement leaves no traces in the IL so nothing in `library` will be marked [RemovedAssembly ("library.dll")] -#if NETCOREAPP - [KeptReferencesInAssembly ("copied.dll", new[] { "System.Private.CoreLib" })] -#else - [KeptReferencesInAssembly ("copied.dll", new[] { "mscorlib" })] -#endif + [KeptReferencesInAssembly ("copied.dll", new[] { PlatformAssemblies.CoreLib, "library" })] public class AssemblyOnlyUsedByUsingWithCsc { public static void Main () diff --git a/test/Mono.Linker.Tests.Cases/References/AssemblyOnlyUsedByUsingWithCscWithKeepFacades.cs b/test/Mono.Linker.Tests.Cases/References/AssemblyOnlyUsedByUsingWithCscWithKeepFacades.cs index f039dac8f..2fcbb6a6c 100644 --- a/test/Mono.Linker.Tests.Cases/References/AssemblyOnlyUsedByUsingWithCscWithKeepFacades.cs +++ b/test/Mono.Linker.Tests.Cases/References/AssemblyOnlyUsedByUsingWithCscWithKeepFacades.cs @@ -1,4 +1,5 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; using Mono.Linker.Tests.Cases.Expectations.Metadata; using Mono.Linker.Tests.Cases.References.Dependencies; @@ -22,11 +23,7 @@ namespace Mono.Linker.Tests.Cases.References // We library should be gone. The `using` statement leaves no traces in the IL so nothing in `library` will be marked [RemovedAssembly ("library.dll")] -#if NETCOREAPP - [KeptReferencesInAssembly ("copied.dll", new[] { "System.Private.CoreLib" })] -#else - [KeptReferencesInAssembly ("copied.dll", new[] { "mscorlib" })] -#endif + [KeptReferencesInAssembly ("copied.dll", new[] { PlatformAssemblies.CoreLib, "library" })] public class AssemblyOnlyUsedByUsingWithCscWithKeepFacades { public static void Main () diff --git a/test/Mono.Linker.Tests.Cases/Symbols/ReferenceWithEmbeddedPdbCopyAction.cs b/test/Mono.Linker.Tests.Cases/Symbols/ReferenceWithEmbeddedPdbCopyAction.cs index 15ee534aa..3be34790d 100644 --- a/test/Mono.Linker.Tests.Cases/Symbols/ReferenceWithEmbeddedPdbCopyAction.cs +++ b/test/Mono.Linker.Tests.Cases/Symbols/ReferenceWithEmbeddedPdbCopyAction.cs @@ -8,7 +8,8 @@ namespace Mono.Linker.Tests.Cases.Symbols [SetupLinkerLinkSymbols ("false")] [SetupLinkerAction ("copy", "LibraryWithEmbeddedPdbSymbols")] - [RemovedSymbols ("LibraryWithEmbeddedPdbSymbols.dll")] + // Copy assemblies cannot be modified. + [KeptSymbols ("LibraryWithEmbeddedPdbSymbols.dll")] // Copying with symbol linking off is a little more complex for embedded pdbs. // Do a little extra asserting here to make sure the assembly wasn't accidentally linked diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/AttributeArgumentForwarded.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/AttributeArgumentForwarded.cs index 4c119d473..6e740e717 100644 --- a/test/Mono.Linker.Tests.Cases/TypeForwarding/AttributeArgumentForwarded.cs +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/AttributeArgumentForwarded.cs @@ -5,10 +5,6 @@ using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; namespace Mono.Linker.Tests.Cases.TypeForwarding { - // On .NET FW the built in compiler will drop the reference to the forwarder assembly when compiling the test assembly. - // Use roslyn to give consistent behavior across platforms - [SetupCSharpCompilerToUse ("csc")] - // Actions: // link - This assembly, Forwarder.dll and Implementation.dll [SetupLinkerDefaultAction ("link")] diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/AttributeArgumentForwardedWithCopyAction.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/AttributeArgumentForwardedWithCopyAction.cs index d67b53fab..0c5d01131 100644 --- a/test/Mono.Linker.Tests.Cases/TypeForwarding/AttributeArgumentForwardedWithCopyAction.cs +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/AttributeArgumentForwardedWithCopyAction.cs @@ -23,7 +23,7 @@ namespace Mono.Linker.Tests.Cases.TypeForwarding [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] [SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] - [RemovedAssembly ("Forwarder.dll")] + [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibrary))] [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary))] static class AttributeArgumentForwardedWithCopyAction { diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/AttributesScopeUpdated.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/AttributesScopeUpdated.cs index 56e06ac6f..1e01403be 100644 --- a/test/Mono.Linker.Tests.Cases/TypeForwarding/AttributesScopeUpdated.cs +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/AttributesScopeUpdated.cs @@ -19,7 +19,7 @@ namespace Mono.Linker.Tests.Cases.TypeForwarding [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] [SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] - [RemovedAssembly ("Forwarder.dll")] + [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibrary))] [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary))] static class AttributesScopeUpdated { diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ForwarderLibrary.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ForwarderLibrary.cs index 441a971eb..57aee6ea8 100644 --- a/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ForwarderLibrary.cs +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ForwarderLibrary.cs @@ -2,3 +2,6 @@ [assembly: System.Runtime.CompilerServices.TypeForwardedTo (typeof (Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ImplementationLibrary))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo (typeof (Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ImplementationStruct))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ImplementationLibraryAttribute))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ImplementationLibraryImp))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ImplementationLibraryInterface))] diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ImplementationLibrary.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ImplementationLibrary.cs index 86a37fdfc..211e2d14f 100644 --- a/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ImplementationLibrary.cs +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ImplementationLibrary.cs @@ -6,14 +6,38 @@ using Mono.Linker.Tests.Cases.Expectations.Metadata; namespace Mono.Linker.Tests.Cases.TypeForwarding.Dependencies { + public interface ImplementationLibraryInterface + { + public int GetDefaultImplementation () + { + return 42; + } + } + + public class ImplementationLibraryImp : ImplementationLibraryInterface + { + } + public class ImplementationLibrary { + public class ImplementationLibraryNestedType + { + public static int PropertyOnNestedType { get; set; } + } + + public static int someField = 42; + public string GetSomeValue () { return "Hello"; } } + [AttributeUsage (AttributeTargets.All)] + public class ImplementationLibraryAttribute : Attribute + { + } + public struct ImplementationStruct { public int Field; diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ReferenceImplementationLibrary.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ReferenceImplementationLibrary.cs index 8796effb1..4ec420372 100644 --- a/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ReferenceImplementationLibrary.cs +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ReferenceImplementationLibrary.cs @@ -9,13 +9,37 @@ namespace Mono.Linker.Tests.Cases.TypeForwarding.Dependencies } #if INCLUDE_REFERENCE_IMPL + public interface ImplementationLibraryInterface + { + public int GetDefaultImplementation () + { + return 42; + } + } + + public class ImplementationLibraryImp : ImplementationLibraryInterface + { + } + public class ImplementationLibrary { + public class ImplementationLibraryNestedType + { + public static int PropertyOnNestedType { get; set; } + } + + public static int someField = 0; + public string GetSomeValue () { return null; } } + [AttributeUsage (AttributeTargets.All)] + public class ImplementationLibraryAttribute : Attribute + { + } + public struct ImplementationStruct { public int Field; diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/SecurityAttributeScope.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/SecurityAttributeScope.cs index 17744e7bf..47bc346a2 100644 --- a/test/Mono.Linker.Tests.Cases/TypeForwarding/SecurityAttributeScope.cs +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/SecurityAttributeScope.cs @@ -16,13 +16,14 @@ namespace Mono.Linker.Tests.Cases.TypeForwarding [SetupLinkerArgument ("--strip-security", "false")] [Define ("IL_ASSEMBLY_AVAILABLE")] [KeepTypeForwarderOnlyAssemblies ("false")] - [SetupLinkerAction ("copy", "Library.dll")] + [SetupLinkerAction ("copy", "Library")] [SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/SecurityAttributeForwarderLibrary.cs" })] [SetupCompileBefore ("Library.dll", new[] { "Dependencies/LibraryWithSecurityAttributes.il" }, new[] { "Forwarder.dll" })] // Sanity checks to verify the test was setup correctly [KeptTypeInAssembly ("Library.dll", "LibraryWithSecurityAttributes")] - [RemovedAssembly ("Forwarder.dll")] + // There's a reference to `Forwarder` in the copy assembly `Library`. + [KeptAssembly ("Forwarder.dll")] public class SecurityAttributeScope { public static void Main () diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UnusedForwarderWithAssemblyCopyIsKept.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UnusedForwarderWithAssemblyCopyIsKept.cs new file mode 100644 index 000000000..a6a78eeab --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UnusedForwarderWithAssemblyCopyIsKept.cs @@ -0,0 +1,28 @@ +// 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 Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + [SetupLinkerDefaultAction ("link")] + [SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + + [SetupLinkerAction ("copy", "Forwarder")] + + [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibrary))] + [RemovedAssembly ("Implementation.dll")] + + public class UnusedForwarderWithAssemblyCopyIsKept + { + static void Main () + { + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedAndUnusedForwarderWithAssemblyCopy.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedAndUnusedForwarderWithAssemblyCopy.cs index 63f9e70c5..fdd75a7f8 100644 --- a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedAndUnusedForwarderWithAssemblyCopy.cs +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedAndUnusedForwarderWithAssemblyCopy.cs @@ -19,12 +19,14 @@ namespace Mono.Linker.Tests.Cases.TypeForwarding [SetupCompileAfter ("Unused.dll", new[] { "Dependencies/AnotherLibrary.cs" })] [SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary_2.cs" }, references: new[] { "Implementation.dll", "Unused.dll" })] - [KeptAssembly ("Forwarder.dll")] [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary), "GetSomeValue()")] + // The whole assembly is kept as is, since it is marked with the `copy` action. [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibrary))] + [KeptMemberInAssembly ("Forwarder.dll", "Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.AnotherLibrary`1")] + [KeptReferencesInAssembly ("Forwarder.dll", new[] { "System.Private.CoreLib", "Implementation", "Unused" })] + // Even though `Forwarder` references this assembly, none of its members are marked (none is used) and, since `Unused` + // has `link` action, it is removed. [RemovedAssembly ("Unused.dll")] - [RemovedForwarder ("Forwarder.dll", "Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.AnotherLibrary`1")] - [RemovedAssemblyReference ("Forwarder.dll", "Unused")] class UsedAndUnusedForwarderWithAssemblyCopy { static void Main () diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByPreserveDependency.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByPreserveDependency.cs new file mode 100644 index 000000000..4abfabc66 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByPreserveDependency.cs @@ -0,0 +1,33 @@ +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + // Actions: + // copy - This assembly + // link - Forwarder.dll and Implementation.dll + [SetupLinkerAction ("copy", "test")] + [SetupCompileBefore ("FakeSystemAssembly.dll", new[] { "../PreserveDependencies/Dependencies/PreserveDependencyAttribute.cs" })] + [SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + + [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibrary))] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary))] + + [Kept] + [KeptMember (".ctor()")] + public class UsedForwarderInCopyAssemblyKeptByPreserveDependency + { + [Kept] + [KeptAttributeAttribute (typeof (PreserveDependencyAttribute))] + [PreserveDependency ("*", "Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ImplementationLibrary", "Forwarder")] + public static void Main () + { + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedCustomAttribute.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedCustomAttribute.cs new file mode 100644 index 000000000..5c1230cd6 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedCustomAttribute.cs @@ -0,0 +1,32 @@ +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + // Actions: + // copy - This assembly + // link - Forwarder.dll and Implementation.dll + [SetupLinkerAction ("copy", "test")] + [SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + + [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibraryAttribute))] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibraryAttribute))] + + [Kept] + [KeptMember (".ctor()")] + public class UsedForwarderInCopyAssemblyKeptByUsedCustomAttribute + { + [Kept] + [KeptAttributeAttribute (typeof (ImplementationLibraryAttribute))] + [ImplementationLibrary] + public static void Main () + { + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedField.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedField.cs new file mode 100644 index 000000000..961cf64f8 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedField.cs @@ -0,0 +1,31 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + // Actions: + // copy - This assembly + // link - Forwarder.dll and Implementation.dll + [SetupLinkerAction ("copy", "test")] + [SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + + [KeptAssembly ("Forwarder.dll")] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary))] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary), "someField")] + [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibrary))] + + [Kept] + [KeptMember (".ctor()")] + public class UsedForwarderInCopyAssemblyKeptByUsedField + { + public static void Main () + { + int field = ImplementationLibrary.someField; + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedInterface.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedInterface.cs new file mode 100644 index 000000000..8b87e16a2 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedInterface.cs @@ -0,0 +1,32 @@ +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + // Actions: + // copy - This assembly + // link - Forwarder.dll and Implementation.dll + [SetupLinkerAction ("copy", "test")] + [SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + + [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibraryImp))] + [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibraryInterface))] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibraryImp))] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibraryInterface))] + + [Kept] + [KeptMember (".ctor()")] + public class UsedForwarderInCopyAssemblyKeptByUsedInterface + { + public static void Main () + { + ImplementationLibraryInterface myInterface = new ImplementationLibraryImp (); + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedMethod.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedMethod.cs new file mode 100644 index 000000000..7f574762a --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedMethod.cs @@ -0,0 +1,31 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + // Actions: + // copy - This assembly + // link - Forwarder.dll and Implementation.dll + [SetupLinkerAction ("copy", "test")] + [SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + + [KeptAssembly ("Forwarder.dll")] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary))] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary), "GetSomeValue()")] + [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibrary))] + + [Kept] + [KeptMember (".ctor()")] + public class UsedForwarderInCopyAssemblyKeptByUsedMethod + { + public static void Main () + { + new ImplementationLibrary ().GetSomeValue (); + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedNestedType.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedNestedType.cs new file mode 100644 index 000000000..56e952b25 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedNestedType.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + // Actions: + // copy - This assembly + // link - Forwarder.dll and Implementation.dll + [SetupLinkerAction ("copy", "test")] + [SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + + [KeptAssembly ("Forwarder.dll")] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary.ImplementationLibraryNestedType))] + [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibrary.ImplementationLibraryNestedType))] + + [Kept] + [KeptMember (".ctor()")] + public class UsedForwarderInCopyAssemblyKeptByUsedNestedType + { + public static void Main () + { + new ImplementationLibrary.ImplementationLibraryNestedType (); + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedProperty.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedProperty.cs new file mode 100644 index 000000000..e92f5e716 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedProperty.cs @@ -0,0 +1,32 @@ +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + // Actions: + // copy - This assembly + // link - Forwarder.dll and Implementation.dll + [SetupLinkerAction ("copy", "test")] + [SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + + [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibrary))] + [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibrary.ImplementationLibraryNestedType))] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary))] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary.ImplementationLibraryNestedType))] + + [Kept] + [KeptMember (".ctor()")] + public class UsedForwarderInCopyAssemblyKeptByUsedProperty + { + public static void Main () + { + var accessPropertyOnNestedType = ImplementationLibrary.ImplementationLibraryNestedType.PropertyOnNestedType; + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedTypeAsGenericArg.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedTypeAsGenericArg.cs new file mode 100644 index 000000000..1693a506e --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedTypeAsGenericArg.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + // Actions: + // copy - This assembly + // link - Forwarder.dll and Implementation.dll + [SetupLinkerAction ("copy", "test")] + [SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + + [KeptAssembly ("Forwarder.dll")] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary))] + [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibrary))] + + [Kept] + [KeptMember (".ctor()")] + public class UsedForwarderInCopyAssemblyKeptByUsedTypeAsGenericArg + { + public static void Main () + { + _ = new List<ImplementationLibrary> (); + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderIsDynamicallyAccessedWithAssemblyCopyUsed.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderIsDynamicallyAccessedWithAssemblyCopyUsed.cs new file mode 100644 index 000000000..963d22da9 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderIsDynamicallyAccessedWithAssemblyCopyUsed.cs @@ -0,0 +1,38 @@ +// 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.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + + [SetupLinkerAction ("copyused", "Forwarder")] + [KeepTypeForwarderOnlyAssemblies ("false")] + + [SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + + [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibrary))] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary))] + class UsedForwarderIsDynamicallyAccessedWithAssemblyCopyUsed + { + static void Main () + { + PointToTypeInFacade ("Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ImplementationLibrary, Forwarder"); + } + + [Kept] + static void PointToTypeInFacade ( + [KeptAttributeAttribute (typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string typeName) + { + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderWithAssemblyCopyUsedAndFacadesKept.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderWithAssemblyCopyUsedAndFacadesKept.cs index 9c7b0c769..7698bc70c 100644 --- a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderWithAssemblyCopyUsedAndFacadesKept.cs +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderWithAssemblyCopyUsedAndFacadesKept.cs @@ -20,6 +20,7 @@ namespace Mono.Linker.Tests.Cases.TypeForwarding [KeptMemberInAssembly ("Forwarder.dll", typeof (ImplementationLibrary))] [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary), "GetSomeValue()")] + [RemovedAssemblyReference ("test", "Forwarder")] class UsedForwarderWithAssemblyCopyUsedAndFacadesKept { static void Main () diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderInCopyAssemblyIsDynamicallyAccessed.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderInCopyAssemblyIsDynamicallyAccessed.cs new file mode 100644 index 000000000..4ba936ae6 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderInCopyAssemblyIsDynamicallyAccessed.cs @@ -0,0 +1,41 @@ +// 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.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + [SetupCompileBefore ("SecondForwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + [SetupCompileBefore ("FirstForwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "SecondForwarder.dll" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("SecondForwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + [SetupLinkerAction ("copy", "FirstForwarder")] + + [KeptMemberInAssembly ("FirstForwarder.dll", typeof (ImplementationLibrary))] + // Dynamically accessing a type forwarder will cause the linker to mark the scope + // of type pointed to as well as the resolved type. + [KeptMemberInAssembly ("SecondForwarder.dll", typeof (ImplementationLibrary))] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary), "GetSomeValue()")] + class UsedTransitiveForwarderInCopyAssemblyIsDynamicallyAccessed + { + static void Main () + { + // [copy] [link] [link] + // FirstForwarder -> SecondForwarder -> Implementation + PointToTypeInFacade ("Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ImplementationLibrary, FirstForwarder"); + } + + [Kept] + static void PointToTypeInFacade ( + [KeptAttributeAttribute (typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string typeName) + { + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderInCopyUsedAssemblyIsDynamicallyAccessed.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderInCopyUsedAssemblyIsDynamicallyAccessed.cs new file mode 100644 index 000000000..9f6b74c0d --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderInCopyUsedAssemblyIsDynamicallyAccessed.cs @@ -0,0 +1,43 @@ +// 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.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + [KeepTypeForwarderOnlyAssemblies ("false")] + + [SetupCompileBefore ("SecondForwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + [SetupCompileBefore ("FirstForwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "SecondForwarder.dll" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("SecondForwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + [SetupLinkerAction ("copyused", "FirstForwarder")] + + [KeptMemberInAssembly ("FirstForwarder.dll", typeof (ImplementationLibrary))] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary), "GetSomeValue()")] + [RemovedAssemblyReference ("FirstForwarder.dll", "SecondForwarder.dll")] + [RemovedForwarder ("FirstForwarder.dll", nameof (ImplementationStruct))] + [RemovedAssembly ("SecondForwarder.dll")] + class UsedTransitiveForwarderInCopyUsedAssemblyIsDynamicallyAccessed + { + static void Main () + { + // [copyused] [link] [link] + // FirstForwarder -> SecondForwarder -> Implementation + PointToTypeInFacade ("Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ImplementationLibrary, FirstForwarder"); + } + + [Kept] + static void PointToTypeInFacade ( + [KeptAttributeAttribute (typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string typeName) + { + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderIsDynamicallyAccessed.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderIsDynamicallyAccessed.cs new file mode 100644 index 000000000..d51014d0e --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderIsDynamicallyAccessed.cs @@ -0,0 +1,41 @@ +// 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.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + [KeepTypeForwarderOnlyAssemblies ("false")] + + [SetupCompileBefore ("SecondForwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + [SetupCompileBefore ("FirstForwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "SecondForwarder.dll" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("SecondForwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + + [KeptMemberInAssembly ("FirstForwarder.dll", typeof (ImplementationLibrary), "GetSomeValue()")] + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary), "GetSomeValue()")] + [RemovedForwarder ("FirstForwarder.dll", nameof (ImplementationStruct))] + [RemovedAssembly ("SecondForwarder.dll")] + class UsedTransitiveForwarderIsDynamicallyAccessed + { + static void Main () + { + // [link] [link] [link] + // FirstForwarder -> SecondForwarder -> Implementation + PointToTypeInFacade ("Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ImplementationLibrary, FirstForwarder"); + } + + [Kept] + static void PointToTypeInFacade ( + [KeptAttributeAttribute (typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string typeName) + { + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderIsResolvedAndFacadeRemoved.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderIsResolvedAndFacadeRemoved.cs new file mode 100644 index 000000000..1c8458eba --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderIsResolvedAndFacadeRemoved.cs @@ -0,0 +1,31 @@ +// 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.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + [KeepTypeForwarderOnlyAssemblies ("false")] + + [SetupCompileBefore ("SecondForwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + [SetupCompileBefore ("FirstForwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "SecondForwarder.dll" })] + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("SecondForwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary), "GetSomeValue()")] + [RemovedMemberInAssembly ("Implementation.dll", nameof (ImplementationStruct))] + [RemovedAssembly ("FirstForwarder.dll")] + [RemovedAssembly ("SecondForwarder.dll")] + class UsedTransitiveForwarderIsResolvedAndFacadeRemoved + { + static void Main () + { + var instance = new ImplementationLibrary (); + instance.GetSomeValue (); + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderIsResolvedAndFacadeRemovedInCopyAssembly.cs b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderIsResolvedAndFacadeRemovedInCopyAssembly.cs new file mode 100644 index 000000000..3472be667 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderIsResolvedAndFacadeRemovedInCopyAssembly.cs @@ -0,0 +1,30 @@ +// 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.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + [SetupCompileBefore ("SecondForwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + [SetupCompileBefore ("FirstForwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "SecondForwarder.dll" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter ("SecondForwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + [SetupLinkerAction ("copy", "Implementation")] + + [KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary), "GetSomeValue()")] + [RemovedMemberInAssembly ("Implementation.dll", nameof (ImplementationStruct))] + class UsedTransitiveForwarderIsResolvedAndFacadeRemovedInCopyAssembly + { + static void Main () + { + var instance = new ImplementationLibrary (); + instance.GetSomeValue (); + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests/TestCasesRunner/ExpectationsProvider.cs b/test/Mono.Linker.Tests/TestCasesRunner/ExpectationsProvider.cs index 0e87979f4..41bff0e3b 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/ExpectationsProvider.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/ExpectationsProvider.cs @@ -1,5 +1,6 @@ using Mono.Cecil; using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; namespace Mono.Linker.Tests.TestCasesRunner { @@ -8,7 +9,10 @@ namespace Mono.Linker.Tests.TestCasesRunner public static bool IsAssemblyAssertion (CustomAttribute attr) { - return attr.AttributeType.Name == nameof (KeptAssemblyAttribute) || attr.AttributeType.Name == nameof (RemovedAssemblyAttribute); + return attr.AttributeType.Name == nameof (KeptAssemblyAttribute) || + attr.AttributeType.Name == nameof (RemovedAssemblyAttribute) || + attr.AttributeType.Name == nameof (SetupLinkerActionAttribute) || + attr.AttributeType.Name == nameof (SetupLinkerTrimModeAttribute); } public static bool IsSymbolAssertion (CustomAttribute attr) diff --git a/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs index f0e2fd4cb..66ee602a5 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs @@ -6,6 +6,7 @@ using System.Text.RegularExpressions; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; using Mono.Linker.Tests.Extensions; using NUnit.Framework; @@ -108,6 +109,8 @@ namespace Mono.Linker.Tests.TestCasesRunner void PerformOutputAssemblyChecks (AssemblyDefinition original, NPath outputDirectory) { var assembliesToCheck = original.MainModule.Types.SelectMany (t => t.CustomAttributes).Where (attr => ExpectationsProvider.IsAssemblyAssertion (attr)); + var actionAssemblies = new HashSet<string> (); + bool trimModeIsCopy = false; foreach (var assemblyAttr in assembliesToCheck) { var name = (string) assemblyAttr.ConstructorArguments.First ().Value; @@ -117,9 +120,29 @@ namespace Mono.Linker.Tests.TestCasesRunner Assert.IsFalse (expectedPath.FileExists (), $"Expected the assembly {name} to not exist in {outputDirectory}, but it did"); else if (assemblyAttr.AttributeType.Name == nameof (KeptAssemblyAttribute)) Assert.IsTrue (expectedPath.FileExists (), $"Expected the assembly {name} to exist in {outputDirectory}, but it did not"); - else + else if (assemblyAttr.AttributeType.Name == nameof (SetupLinkerActionAttribute)) { + string assemblyName = (string) assemblyAttr.ConstructorArguments[1].Value; + if ((string) assemblyAttr.ConstructorArguments[0].Value == "copy") { + VerifyCopyAssemblyIsKeptUnmodified (outputDirectory, assemblyName + (assemblyName == "test" ? ".exe" : ".dll")); + } + + actionAssemblies.Add (assemblyName); + } else if (assemblyAttr.AttributeType.Name == nameof (SetupLinkerTrimModeAttribute)) { + // We delay checking that everything was copied after processing all assemblies + // with a specific action, since assembly action wins over trim mode. + if ((string) assemblyAttr.ConstructorArguments[0].Value == "copy") + trimModeIsCopy = true; + } else throw new NotImplementedException ($"Unknown assembly assertion of type {assemblyAttr.AttributeType}"); } + + if (trimModeIsCopy) { + foreach (string assemblyName in Directory.GetFiles (Directory.GetParent (outputDirectory).ToString (), "input")) { + var fileInfo = new FileInfo (assemblyName); + if (fileInfo.Extension == ".dll" && !actionAssemblies.Contains (assemblyName)) + VerifyCopyAssemblyIsKeptUnmodified (outputDirectory, assemblyName + (assemblyName == "test" ? ".exe" : ".dll")); + } + } } void PerformOutputSymbolChecks (AssemblyDefinition original, NPath outputDirectory) @@ -212,12 +235,19 @@ namespace Mono.Linker.Tests.TestCasesRunner } var expectedTypeName = checkAttrInAssembly.ConstructorArguments[1].Value.ToString (); - var linkedType = linkedAssembly.MainModule.GetType (expectedTypeName); + TypeDefinition linkedType = linkedAssembly.MainModule.GetType (expectedTypeName); if (linkedType == null && linkedAssembly.MainModule.HasExportedTypes) { - linkedType = linkedAssembly.MainModule.ExportedTypes - .FirstOrDefault (exported => exported.FullName == expectedTypeName) - ?.Resolve (); + ExportedType exportedType = linkedAssembly.MainModule.ExportedTypes + .FirstOrDefault (exported => exported.FullName == expectedTypeName); + + // Note that copied assemblies could have dangling references. + if (exportedType != null && original.EntryPoint.DeclaringType.CustomAttributes.FirstOrDefault ( + ca => ca.AttributeType.Name == nameof (RemovedAssemblyAttribute) + && ca.ConstructorArguments[0].Value.ToString () == exportedType.Scope.Name + ".dll") != null) + continue; + + linkedType = exportedType?.Resolve (); } switch (attributeTypeName) { @@ -374,6 +404,17 @@ namespace Mono.Linker.Tests.TestCasesRunner Assert.Fail ($"Invalid test assertion. No member named `{memberName}` exists on the original type `{originalType}`"); } + void VerifyCopyAssemblyIsKeptUnmodified (NPath outputDirectory, string assemblyName) + { + string inputAssemblyPath = Path.Combine (Directory.GetParent (outputDirectory).ToString (), "input", assemblyName); + string outputAssemblyPath = Path.Combine (outputDirectory, assemblyName); + Assert.IsTrue (File.ReadAllBytes (inputAssemblyPath).SequenceEqual (File.ReadAllBytes (outputAssemblyPath)), + $"Expected assemblies\n" + + $"\t{inputAssemblyPath}\n" + + $"\t{outputAssemblyPath}\n" + + $"binaries to be equal, since the input assembly has copy action."); + } + void VerifyCustomAttributeKept (ICustomAttributeProvider provider, string expectedAttributeTypeName) { var match = provider.CustomAttributes.FirstOrDefault (attr => attr.AttributeType.FullName == expectedAttributeTypeName); @@ -567,7 +608,11 @@ namespace Mono.Linker.Tests.TestCasesRunner void VerifyKeptReferencesInAssembly (CustomAttribute inAssemblyAttribute) { var assembly = ResolveLinkedAssembly (inAssemblyAttribute.ConstructorArguments[0].Value.ToString ()); - var expectedReferenceNames = ((CustomAttributeArgument[]) inAssemblyAttribute.ConstructorArguments[1].Value).Select (attr => (string) attr.Value); + var expectedReferenceNames = ((CustomAttributeArgument[]) inAssemblyAttribute.ConstructorArguments[1].Value).Select (attr => (string) attr.Value).ToList (); + for (int i = 0; i < expectedReferenceNames.Count (); i++) + if (expectedReferenceNames[i].EndsWith (".dll")) + expectedReferenceNames[i] = expectedReferenceNames[i].Substring (0, expectedReferenceNames[i].LastIndexOf (".")); + Assert.That (assembly.MainModule.AssemblyReferences.Select (asm => asm.Name), Is.EquivalentTo (expectedReferenceNames)); } |