Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMateo Torres-Ruiz <mateoatr@users.noreply.github.com>2021-04-01 14:56:37 +0300
committerGitHub <noreply@github.com>2021-04-01 14:56:37 +0300
commit1d96189abb1b2cfe00dff817c63b84b25534746a (patch)
tree37bb5a8f110a36727054c3f6dc4acc6aa1dccaac
parentf4147aa53a425420f725c9cb5dbd36552df99372 (diff)
Change behavior of copy action to not rewrite assemblies (#1869)
Co-authored-by: Marek Safar <marek.safar@gmail.com>
-rw-r--r--src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs6
-rw-r--r--src/linker/Linker.Steps/DescriptorMarker.cs3
-rw-r--r--src/linker/Linker.Steps/LinkAttributesParser.cs4
-rw-r--r--src/linker/Linker.Steps/MarkStep.cs81
-rw-r--r--src/linker/Linker.Steps/OutputStep.cs26
-rw-r--r--src/linker/Linker.Steps/SweepStep.cs25
-rw-r--r--src/linker/Linker/MarkingHelpers.cs29
-rw-r--r--src/linker/Linker/ModuleDefinitionExtensions.cs15
-rw-r--r--src/linker/Linker/TypeNameResolver.cs13
-rw-r--r--test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveAnExportedType.cs2
-rw-r--r--test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveExportedTypesUsingRegex.cs2
-rw-r--r--test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKept.cs18
-rw-r--r--test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKept.xml2
-rw-r--r--test/Mono.Linker.Tests.Cases/LinkXml/UsedNonRequiredExportedTypeIsKeptWhenRooted.cs14
-rw-r--r--test/Mono.Linker.Tests.Cases/References/AssemblyOnlyUsedByUsingWithCsc.cs7
-rw-r--r--test/Mono.Linker.Tests.Cases/References/AssemblyOnlyUsedByUsingWithCscWithKeepFacades.cs7
-rw-r--r--test/Mono.Linker.Tests.Cases/Symbols/ReferenceWithEmbeddedPdbCopyAction.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/AttributeArgumentForwarded.cs4
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/AttributeArgumentForwardedWithCopyAction.cs2
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/AttributesScopeUpdated.cs2
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ForwarderLibrary.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ImplementationLibrary.cs24
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ReferenceImplementationLibrary.cs24
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/SecurityAttributeScope.cs5
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UnusedForwarderWithAssemblyCopyIsKept.cs28
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedAndUnusedForwarderWithAssemblyCopy.cs8
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByPreserveDependency.cs33
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedCustomAttribute.cs32
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedField.cs31
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedInterface.cs32
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedMethod.cs31
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedNestedType.cs31
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedProperty.cs32
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderInCopyAssemblyKeptByUsedTypeAsGenericArg.cs31
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderIsDynamicallyAccessedWithAssemblyCopyUsed.cs38
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderWithAssemblyCopyUsedAndFacadesKept.cs1
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderInCopyAssemblyIsDynamicallyAccessed.cs41
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderInCopyUsedAssemblyIsDynamicallyAccessed.cs43
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderIsDynamicallyAccessed.cs41
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderIsResolvedAndFacadeRemoved.cs31
-rw-r--r--test/Mono.Linker.Tests.Cases/TypeForwarding/UsedTransitiveForwarderIsResolvedAndFacadeRemovedInCopyAssembly.cs30
-rw-r--r--test/Mono.Linker.Tests/TestCasesRunner/ExpectationsProvider.cs6
-rw-r--r--test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs57
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));
}