1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-09 09:34:49 +09:00

Fix missing assemblyref for typeref only kept for debug info in illink (#87575)

Fixes https://github.com/dotnet/runtime/issues/86462 by walking
debug info to discover typerefs in the assembly that are only
preserved due to debug info. In this case the assembly has the
'save' action, so the reference to the typeref in the debug info
is not swept. This means that the presence of the typeref in the
final output depends on whether we are linking the PDB. Walking
the typeref will preserve the assemblyref that it refers to,
fixing the issue.

The fix also needs to walk up the parent import scopes,
discovered by reproducing the issue in our test infra.

The behavior needs to depend on whether we are linking debug
symbols, otherwise we will keep the assemblyref (but not the
typeref that uses it) when PDBs are present, and the output
should not depend on the presence of
PDBs. `AssemblyOnlyUsedByUsingSaveAction` tests this case - see
comments in there for more detail.

This includes some unrelated test infra changes (supporting
multiple additional compiler arguments) which were useful for
iterating on this, even though they aren't necessary in the final
version of the testcases.
This commit is contained in:
Sven Boemer 2023-06-16 16:33:31 -07:00 committed by GitHub
parent b84612bbf9
commit af70c369a7
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 241 additions and 82 deletions

View file

@ -1452,7 +1452,7 @@ namespace Mono.Linker.Steps
readonly MarkingHelpers markingHelpers;
TypeReferenceMarker (AssemblyDefinition assembly, MarkingHelpers markingHelpers)
: base (assembly)
: base (assembly, walkSymbols: false)
{
this.markingHelpers = markingHelpers;
}

View file

@ -58,7 +58,7 @@ namespace Mono.Linker.Steps
foreach (var assembly in assemblies)
RemoveUnmarkedAssembly (assembly);
// Look for references (included to previously unresolved assemblies) marked for deletion
// Look for references (including to previously unresolved assemblies) marked for deletion
foreach (var assembly in assemblies)
UpdateAssemblyReferencesToRemovedAssemblies (assembly);
@ -69,7 +69,7 @@ namespace Mono.Linker.Steps
case AssemblyAction.CopyUsed:
case AssemblyAction.Link:
case AssemblyAction.Save:
bool changed = AssemblyReferencesCorrector.SweepAssemblyReferences (assembly);
bool changed = SweepAssemblyReferences (assembly);
if (changed && action == AssemblyAction.CopyUsed)
Annotations.SetAction (assembly, AssemblyAction.Save);
break;
@ -174,7 +174,7 @@ namespace Mono.Linker.Steps
AssemblyAction assemblyAction = AssemblyAction.Copy;
if (SweepTypeForwarders (assembly)) {
// Need to sweep references, in case sweeping type forwarders removed any
AssemblyReferencesCorrector.SweepAssemblyReferences (assembly);
SweepAssemblyReferences (assembly);
assemblyAction = AssemblyAction.Save;
}
@ -194,7 +194,7 @@ namespace Mono.Linker.Steps
case AssemblyAction.Save:
if (SweepTypeForwarders (assembly)) {
// Need to sweep references, in case sweeping type forwarders removed any
AssemblyReferencesCorrector.SweepAssemblyReferences (assembly);
SweepAssemblyReferences (assembly);
}
break;
}
@ -242,7 +242,7 @@ namespace Mono.Linker.Steps
}
if (SweepTypeForwarders (assembly) || updateScopes)
AssemblyReferencesCorrector.SweepAssemblyReferences (assembly);
SweepAssemblyReferences (assembly);
}
bool IsMarkedAssembly (AssemblyDefinition assembly)
@ -568,32 +568,32 @@ namespace Mono.Linker.Steps
{
}
bool SweepAssemblyReferences (AssemblyDefinition assembly)
{
//
// We used to run over list returned by GetTypeReferences but
// that returns typeref(s) of original assembly and we don't track
// which types are needed for which assembly which left us
// with dangling assembly references
//
assembly.MainModule.AssemblyReferences.Clear ();
var arc = new AssemblyReferencesCorrector (assembly, walkSymbols: Context.LinkSymbols);
arc.Process ();
return arc.ChangedAnyScopes;
}
sealed class AssemblyReferencesCorrector : TypeReferenceWalker
{
readonly DefaultMetadataImporter importer;
bool changedAnyScopes;
public bool ChangedAnyScopes { get; private set; }
AssemblyReferencesCorrector (AssemblyDefinition assembly) : base (assembly)
public AssemblyReferencesCorrector (AssemblyDefinition assembly, bool walkSymbols) : base (assembly, walkSymbols)
{
this.importer = new DefaultMetadataImporter (assembly.MainModule);
changedAnyScopes = false;
}
public static bool SweepAssemblyReferences (AssemblyDefinition assembly)
{
//
// We used to run over list returned by GetTypeReferences but
// that returns typeref(s) of original assembly and we don't track
// which types are needed for which assembly which left us
// with dangling assembly references
//
assembly.MainModule.AssemblyReferences.Clear ();
var arc = new AssemblyReferencesCorrector (assembly);
arc.Process ();
return arc.changedAnyScopes;
ChangedAnyScopes = false;
}
protected override void ProcessTypeReference (TypeReference type)
@ -626,7 +626,7 @@ namespace Mono.Linker.Steps
return;
type.Scope = tr.Scope;
changedAnyScopes = true;
ChangedAnyScopes = true;
}
protected override void ProcessExportedType (ExportedType exportedType)
@ -647,7 +647,7 @@ namespace Mono.Linker.Steps
return;
exportedType.Scope = tr.Scope;
changedAnyScopes = true;
ChangedAnyScopes = true;
}
}
}

View file

@ -15,15 +15,18 @@ namespace Mono.Linker
protected HashSet<TypeReference> Visited { get; } = new HashSet<TypeReference> ();
public TypeReferenceWalker (AssemblyDefinition assembly)
readonly bool walkSymbols;
public TypeReferenceWalker (AssemblyDefinition assembly, bool walkSymbols)
{
this.assembly = assembly;
this.walkSymbols = walkSymbols;
}
// Traverse the assembly and mark the scopes of discovered type references (but not exported types).
// This includes scopes referenced by Cecil TypeReference objects that don't represent rows in the typeref table,
// such as references to built-in types, or attribute arguments which encode type references as strings.
protected virtual void Process ()
public virtual void Process ()
{
if (Visited.Count > 0)
throw new InvalidOperationException ();
@ -103,6 +106,9 @@ namespace Mono.Linker
if (m.HasBody)
WalkTypeScope (m.Body);
if (walkSymbols && m.DebugInformation?.Scope?.Import is ImportDebugInformation import)
WalkDebugInfoImportScope (import);
}
}
@ -315,6 +321,17 @@ namespace Mono.Linker
}
}
void WalkDebugInfoImportScope (ImportDebugInformation import)
{
if (import.HasTargets) {
foreach (var target in import.Targets)
WalkScopeOfTypeReference (target.Type);
}
if (import.Parent is ImportDebugInformation parent)
WalkDebugInfoImportScope (parent);
}
void WalkScopeOfTypeReference (TypeReference type)
{
if (type == null)

View file

@ -11,7 +11,7 @@ namespace Mono.Linker.Tests.Cases.Expectations.Metadata
[AttributeUsage (AttributeTargets.Class, AllowMultiple = true)]
public class SetupCompileBeforeAttribute : BaseMetadataAttribute
{
public SetupCompileBeforeAttribute (string outputName, string[] sourceFiles, string[] references = null, string[] defines = null, object[] resources = null, string additionalArguments = null, string compilerToUse = null, bool addAsReference = true, bool removeFromLinkerInput = false, string outputSubFolder = null)
public SetupCompileBeforeAttribute (string outputName, string[] sourceFiles, string[] references = null, string[] defines = null, object[] resources = null, string[] additionalArguments = null, string compilerToUse = null, bool addAsReference = true, bool removeFromLinkerInput = false, string outputSubFolder = null)
{
ArgumentNullException.ThrowIfNull (sourceFiles);

View file

@ -16,6 +16,7 @@
<Compile Remove="LinkXml\Dependencies\CanPreserveAnExportedType_Forwarder.cs" />
<Compile Remove="LinkXml\Dependencies\UsedNonRequiredExportedTypeIsKept_fwd.cs" />
<Compile Remove="LinkXml\Dependencies\UsedNonRequiredExportedTypeIsKeptWhenRooted_fwd.cs" />
<Compile Remove="References\Dependencies\CustomMarkHandlerSaveAssembly.cs" />
<Compile Remove="TypeForwarding\Dependencies\AnotherLibraryForwarder.cs" />
<Compile Remove="TypeForwarding\Dependencies\AssemblyReferenceIsRemovedWhenUnused_Library.cs" />
<Compile Remove="TypeForwarding\Dependencies\ForwarderLibrary.cs" />

View file

@ -0,0 +1,42 @@
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;
namespace Mono.Linker.Tests.Cases.References
{
/// <summary>
/// We can't detect the using usage in the assembly. As a result, nothing in `library` is going to be marked and that assembly will be deleted.
/// With the assembly action of `save`, we remove unused assembly references from the assembly and rewrite it.
/// When cecil writes the assembly, the unused typeref is not written out.
/// </summary>
// Add a custom step which sets the assembly action of the test to "save"
[SetupCompileBefore ("SetSaveAction.dll", new[] { "Dependencies/CustomMarkHandlerSaveAssembly.cs" },
new[] { "illink.dll", "Mono.Cecil.dll", "netstandard.dll" })]
[SetupLinkerArgument ("--custom-step", "CustomMarkHandlerSaveAssembly,SetSaveAction.dll")]
// When csc is used, `saved.dll` will have a reference to `library.dll`
[SetupCompileBefore ("library.dll", new[] { "Dependencies/AssemblyOnlyUsedByUsing_Lib.cs" })]
[SetupCompileBefore ("saved.dll", new[] { "Dependencies/AssemblyOnlyUsedByUsing_UnusedUsing.cs" },
// Even though this testcase doesn't link symbols, tell the compiler to produce symbols to confirm that the behavior
// isn't affected by the presence of symbols when not passing '-b'.
new[] { "library.dll" }, additionalArguments: new string[] { "/debug:portable" }, compilerToUse: "csc")]
// Here to assert that the test is setup correctly to preserve unused code in the saved assembly. This is an important aspect of the bug
[KeptMemberInAssembly ("saved.dll", typeof (AssemblyOnlyUsedByUsing_UnusedUsing), "Unused()")]
// The library should be gone. The `using` statement leaves no traces in the IL so nothing in `library` will be marked
[RemovedAssembly ("library.dll")]
// The `save` action results in the reference to System.Runtime being resolved into a reference directly to System.Private.CoreLib.
// The reference to `library` is removed.
[KeptReferencesInAssembly ("saved.dll", new[] { "System.Private.CoreLib" })]
public class AssemblyOnlyUsedByUsingSaveAction
{
public static void Main ()
{
// Use something to keep the reference at compile time
AssemblyOnlyUsedByUsing_UnusedUsing.UsedToKeepReference ();
}
}
}

View file

@ -0,0 +1,45 @@
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;
namespace Mono.Linker.Tests.Cases.References
{
/// <summary>
/// We can't detect the using usage in the assembly. As a result, nothing in `library` is going to be marked and that assembly will be deleted.
/// With the assembly action of `save`, we remove unused assembly references from the assembly and rewrite it, preserving all methods.
/// When debug symbols are present for the `save` assembly, we do not trim debug symbols, so cecil keeps all type references in the
/// save assembly that are referenced by the debug info. This includes the unused typeref, so the assemblyref referenced by the typeref
/// must also be preserved.
/// </summary>
// Add a custom step which sets the assembly action of the test to "save"
[SetupCompileBefore ("SetSaveAction_Symbols.dll", new[] { "Dependencies/CustomMarkHandlerSaveAssembly.cs" },
new[] { "illink.dll", "Mono.Cecil.dll", "netstandard.dll" })]
[SetupLinkerArgument ("--custom-step", "CustomMarkHandlerSaveAssembly,SetSaveAction_Symbols.dll")]
// When csc is used, `saved.dll` will have a reference to `library.dll`
[SetupCompileBefore ("library.dll", new[] { "Dependencies/AssemblyOnlyUsedByUsing_Lib.cs" })]
[SetupCompileBefore ("saved.dll", new[] { "Dependencies/AssemblyOnlyUsedByUsing_UnusedUsing.cs" },
new[] { "library.dll" }, additionalArguments: new string[] { "/debug:portable" }, compilerToUse: "csc")]
// Here to assert that the test is setup correctly to preserve unused code in the saved assembly. This is an important aspect of the bug
[KeptMemberInAssembly ("saved.dll", typeof (AssemblyOnlyUsedByUsing_UnusedUsing), "Unused()")]
// The library should be gone. The `using` statement leaves no traces in the IL so nothing in `library` will be marked
[RemovedAssembly ("library.dll")]
// The `save` action results in the reference to System.Runtime being resolved into a reference directly to System.Private.CoreLib.
// The reference to `library` is kept, because it is referenced from a typeref that is referenced from the debug info.
[KeptReferencesInAssembly ("saved.dll", new[] { "System.Private.CoreLib", "library" })]
// Linking debug symbols is required for cecil not to remove the typeref from the assembly, because it is only referenced from the debug info.
[SetupLinkerLinkSymbols ("true")]
public class AssemblyOnlyUsedByUsingSaveActionWithSymbols
{
public static void Main ()
{
// Use something to keep the reference at compile time
AssemblyOnlyUsedByUsing_UnusedUsing.UsedToKeepReference ();
}
}
}

View file

@ -7,18 +7,18 @@ namespace Mono.Linker.Tests.Cases.References
{
/// <summary>
/// We can't detect the using usage in the assembly. As a result, nothing in `library` is going to be marked and that assembly will be deleted.
/// Because of that, `copied` needs to have it's reference to `library` removed even though we specified an assembly action of `copy`
/// However, because we specified an assembly action of `copy`, we do not rewrite `copied`, and it ends up with an unused reference to the removed `library`.
/// </summary>
[SetupLinkerAction ("copy", "copied")]
[SetupCompileBefore ("library.dll", new[] { "Dependencies/AssemblyOnlyUsedByUsing_Lib.cs" })]
// When csc is used, `copied.dll` will have a reference to `library.dll`
[SetupCompileBefore ("copied.dll", new[] { "Dependencies/AssemblyOnlyUsedByUsing_Copied.cs" }, new[] { "library.dll" }, compilerToUse: "csc")]
[SetupCompileBefore ("library.dll", new[] { "Dependencies/AssemblyOnlyUsedByUsing_Lib.cs" })]
[SetupCompileBefore ("copied.dll", new[] { "Dependencies/AssemblyOnlyUsedByUsing_UnusedUsing.cs" }, new[] { "library.dll" }, compilerToUse: "csc")]
// Here to assert that the test is setup correctly to copy the copied assembly. This is an important aspect of the bug
[KeptMemberInAssembly ("copied.dll", typeof (AssemblyOnlyUsedByUsing_Copied), "Unused()")]
[KeptMemberInAssembly ("copied.dll", typeof (AssemblyOnlyUsedByUsing_UnusedUsing), "Unused()")]
// We library should be gone. The `using` statement leaves no traces in the IL so nothing in `library` will be marked
// The library should be gone. The `using` statement leaves no traces in the IL so nothing in `library` will be marked
[RemovedAssembly ("library.dll")]
[KeptReferencesInAssembly ("copied.dll", new[] { "System.Runtime", "library" })]
public class AssemblyOnlyUsedByUsingWithCsc
@ -26,7 +26,7 @@ namespace Mono.Linker.Tests.Cases.References
public static void Main ()
{
// Use something to keep the reference at compile time
AssemblyOnlyUsedByUsing_Copied.UsedToKeepReference ();
AssemblyOnlyUsedByUsing_UnusedUsing.UsedToKeepReference ();
}
}
}
}

View file

@ -1,11 +1,10 @@
// This is what triggers the behavior difference between Roslyn and mcs. Roslyn will keep the reference
// to this assembly because of this whereas mcs will not
using ImportantForBug = Mono.Linker.Tests.Cases.References.Dependencies.AssemblyOnlyUsedByUsing_Lib;
namespace Mono.Linker.Tests.Cases.References.Dependencies
{
public class AssemblyOnlyUsedByUsing_Copied
public class AssemblyOnlyUsedByUsing_UnusedUsing
{
public static void UsedToKeepReference ()
{
@ -15,4 +14,4 @@ namespace Mono.Linker.Tests.Cases.References.Dependencies
{
}
}
}
}

View file

@ -0,0 +1,15 @@
using System;
using Mono.Cecil;
using Mono.Linker;
using Mono.Linker.Steps;
public class CustomMarkHandlerSaveAssembly : IMarkHandler
{
public void Initialize (LinkContext context, MarkContext markContext)
{
markContext.RegisterMarkAssemblyAction (assembly => {
if (assembly.Name.Name == "saved")
context.Annotations.SetAction (assembly, AssemblyAction.Save);
});
}
}

View file

@ -5,7 +5,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: "/debug:embedded", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: new[] { "/debug:embedded" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("false")]
[RemovedSymbols ("LibraryWithEmbeddedPdbSymbols.dll")]

View file

@ -5,7 +5,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: "/debug:embedded", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: new[] { "/debug:embedded" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("true")]
[KeptSymbols ("LibraryWithEmbeddedPdbSymbols.dll")]

View file

@ -4,7 +4,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: "/debug:embedded", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: new[] { "/debug:embedded" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("true")]
[SetupLinkerArgument ("--deterministic", "true")]

View file

@ -4,7 +4,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: "/debug:embedded", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: new[] { "/debug:embedded" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("true")]
[SetupLinkerArgument ("--deterministic", "true")]

View file

@ -4,7 +4,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: "/debug:embedded", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: new[] { "/debug:embedded" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("false")]
[SetupLinkerAction ("copy", "LibraryWithEmbeddedPdbSymbols")]

View file

@ -4,7 +4,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: "/debug:embedded", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: new[] { "/debug:embedded" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("true")]
[SetupLinkerAction ("copy", "LibraryWithEmbeddedPdbSymbols")]

View file

@ -4,7 +4,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: "/debug:embedded", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: new[] { "/debug:embedded" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("false")]
[RemovedAssembly ("LibraryWithEmbeddedPdbSymbols.dll")]

View file

@ -4,7 +4,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: "/debug:embedded", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: new[] { "/debug:embedded" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("true")]
[RemovedAssembly ("LibraryWithEmbeddedPdbSymbols.dll")]

View file

@ -5,7 +5,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: "/debug:portable", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: new[] { "/debug:portable" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("false")]
[RemovedSymbols ("LibraryWithPortablePdbSymbols.dll")]

View file

@ -5,7 +5,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: "/debug:portable", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: new[] { "/debug:portable" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("true")]
[KeptSymbols ("LibraryWithPortablePdbSymbols.dll")]

View file

@ -4,7 +4,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: "/debug:portable", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: new[] { "/debug:portable" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("true")]
[SetupLinkerArgument ("--deterministic", "true")]

View file

@ -4,7 +4,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: "/debug:portable", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: new[] { "/debug:portable" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("true")]
[SetupLinkerArgument ("--new-mvid", "true")]

View file

@ -4,7 +4,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: "/debug:portable", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: new[] { "/debug:portable" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("false")]
[SetupLinkerAction ("copy", "LibraryWithPortablePdbSymbols")]

View file

@ -4,7 +4,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: "/debug:portable", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: new[] { "/debug:portable" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("true")]
[SetupLinkerAction ("copy", "LibraryWithPortablePdbSymbols")]

View file

@ -4,7 +4,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: "/debug:portable", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: new[] { "/debug:portable" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("false")]
[RemovedAssembly ("LibraryWithPortablePdbSymbols.dll")]

View file

@ -4,7 +4,7 @@ using Mono.Linker.Tests.Cases.Symbols.Dependencies;
namespace Mono.Linker.Tests.Cases.Symbols
{
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: "/debug:portable", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: new[] { "/debug:portable" }, compilerToUse: "csc")]
[SetupLinkerLinkSymbols ("true")]
[RemovedAssembly ("LibraryWithPortablePdbSymbols.dll")]

View file

@ -13,9 +13,9 @@ namespace Mono.Linker.Tests.Cases.Symbols
[Reference ("Dependencies/LibraryWithPdb/LibraryWithPdb.dll")]
[ReferenceDependency ("Dependencies/LibraryWithPdb/LibraryWithPdb.pdb")]
[SetupCompileBefore ("LibraryWithCompilerDefaultSymbols.dll", new[] { "Dependencies/LibraryWithCompilerDefaultSymbols.cs" }, additionalArguments: "/debug:full")]
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: "/debug:portable", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: "/debug:embedded", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithCompilerDefaultSymbols.dll", new[] { "Dependencies/LibraryWithCompilerDefaultSymbols.cs" }, additionalArguments: new[] { "/debug:full" })]
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: new[] { "/debug:portable" }, compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: new[] { "/debug:embedded" }, compilerToUse: "csc")]
[SetupCompileArgument ("/debug:full")]
[SetupLinkerLinkSymbols ("false")]

View file

@ -10,9 +10,9 @@ namespace Mono.Linker.Tests.Cases.Symbols
[Reference ("Dependencies/LibraryWithPdb/LibraryWithPdb.dll")]
[ReferenceDependency ("Dependencies/LibraryWithPdb/LibraryWithPdb.pdb")]
[SetupCompileBefore ("LibraryWithCompilerDefaultSymbols.dll", new[] { "Dependencies/LibraryWithCompilerDefaultSymbols.cs" }, additionalArguments: "/debug:full")]
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: "/debug:portable", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: "/debug:embedded", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithCompilerDefaultSymbols.dll", new[] { "Dependencies/LibraryWithCompilerDefaultSymbols.cs" }, additionalArguments: new[] { "/debug:full" })]
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: new[] { "/debug:portable" }, compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: new[] { "/debug:embedded" }, compilerToUse: "csc")]
[SetupCompileArgument ("/debug:full")]
[SetupLinkerLinkSymbols ("true")]

View file

@ -16,9 +16,9 @@ namespace Mono.Linker.Tests.Cases.Symbols
[Reference ("Dependencies/LibraryWithPdb/LibraryWithPdb.dll")]
[ReferenceDependency ("Dependencies/LibraryWithPdb/LibraryWithPdb.pdb")]
[SetupCompileBefore ("LibraryWithCompilerDefaultSymbols.dll", new[] { "Dependencies/LibraryWithCompilerDefaultSymbols.cs" }, additionalArguments: "/debug:full")]
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: "/debug:portable", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: "/debug:embedded", compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithCompilerDefaultSymbols.dll", new[] { "Dependencies/LibraryWithCompilerDefaultSymbols.cs" }, additionalArguments: new[] { "/debug:full" })]
[SetupCompileBefore ("LibraryWithPortablePdbSymbols.dll", new[] { "Dependencies/LibraryWithPortablePdbSymbols.cs" }, additionalArguments: new[] { "/debug:portable" }, compilerToUse: "csc")]
[SetupCompileBefore ("LibraryWithEmbeddedPdbSymbols.dll", new[] { "Dependencies/LibraryWithEmbeddedPdbSymbols.cs" }, additionalArguments: new[] { "/debug:embedded" }, compilerToUse: "csc")]
[SetupCompileArgument ("/debug:full")]
[SetupLinkerLinkSymbols ("true")]

View file

@ -8,7 +8,7 @@ namespace Mono.Linker.Tests.Cases.UnreachableBlock
[SetupCompileArgument ("/optimize+")]
[SetupLinkerArgument ("--enable-opt", "ipconstprop")]
[SetupCompileBefore ("library.dll", new string[] { "Dependencies/ReferencedAssemblyWithUnreachableBlocks.cs" },
addAsReference: false, additionalArguments: "/optimize+", compilerToUse: "csc")]
addAsReference: false, additionalArguments: new[] { "/optimize+" }, compilerToUse: "csc")]
[RemovedMemberInAssembly ("library.dll", "Mono.Linker.Tests.Cases.UnreachableBlock.Dependencies.AssemblyWithUnreachableBlocks",
new string[] { "NeverReached()" })]
[ExpectedInstructionSequenceOnMemberInAssembly ("library.dll",

View file

@ -46,7 +46,7 @@ namespace Mono.Linker.Tests.TestCasesRunner
var allResults = _verifier.Verify (Resolve (assemblyName))
?? Enumerable.Empty<VerificationResult> ();
Results = allResults.Where (r => r.Code switch {
Results = allResults.Where (r => r.Code is not (
ILVerify.VerifierError.None
// Static interface methods cause this warning
or ILVerify.VerifierError.CallAbstract
@ -57,10 +57,7 @@ namespace Mono.Linker.Tests.TestCasesRunner
// ref returning a ref local causes this warning but is okay
or VerifierError.ReturnPtrToStack
// Span indexing with indexer (ex. span[^4]) causes this warning
or VerifierError.InitOnly
=> false,
_ => true
});
or VerifierError.InitOnly));
}
PEReader LoadAssembly (string assemblyName)

View file

@ -24,11 +24,11 @@ namespace Mono.Linker.Tests.TestCasesRunner
}
}
public virtual void Link (string[] args, LinkerCustomizations customizations, ILogger logger)
public virtual int Link (string[] args, LinkerCustomizations customizations, ILogger logger)
{
Driver.ProcessResponseFile (args, out var queue);
using (var driver = new TestDriver (queue, customizations)) {
driver.Run (logger);
return driver.Run (logger);
}
}
}

View file

@ -49,13 +49,42 @@ namespace Mono.Linker.Tests.TestCasesRunner
_linkedReaderParameters = linkedReaderParameters;
}
static void VerifyIL (NPath pathToAssembly)
static void VerifyIL (NPath pathToAssembly, AssemblyDefinition linked)
{
var verifier = new ILVerifier (pathToAssembly);
foreach (var result in verifier.Results) {
if (result.Code == ILVerify.VerifierError.None)
continue;
foreach (var result in verifier.Results)
Assert.Fail (ILVerifier.GetErrorMessage (result));
}
static void ValidateTypeRefsHaveValidAssemblyRefs (AssemblyDefinition linked)
{
foreach (var typeRef in linked.MainModule.GetTypeReferences ()) {
switch (typeRef.Scope) {
case null:
// There should be an ExportedType row for this typeref
var exportedType = linked.MainModule.ExportedTypes.SingleOrDefault (et => et.FullName == typeRef.FullName);
Assert.IsNotNull (exportedType, $"Type reference '{typeRef.FullName}' with null scope has no ExportedType row");
// The exported type's Implementation must be an index into the File/ExportedType/AssemblyRef table
switch (exportedType.Scope) {
case AssemblyNameReference:
// There should be an AssemblyRef row for this assembly
var assemblyRef = linked.MainModule.AssemblyReferences.Single (ar => ar.Name == exportedType.Scope.Name);
Assert.IsNotNull (assemblyRef, $"Exported type '{exportedType.FullName}' has a reference to assembly '{exportedType.Scope.Name}' which is not a reference of '{linked.FullName}'");
break;
default:
throw new NotImplementedException ($"Unexpected scope type '{exportedType.Scope.GetType ()}' for exported type '{exportedType.FullName}'");
}
continue;
case AssemblyNameReference:
{
// There should be an AssemblyRef row for this assembly
var assemblyRef = linked.MainModule.AssemblyReferences.Single (ar => ar.Name == typeRef.Scope.Name);
Assert.IsNotNull (assemblyRef, $"Type reference '{typeRef.FullName}' has a reference to assembly '{typeRef.Scope.Name}' which is not a reference of '{linked.FullName}'");
continue;
}
default:
throw new NotImplementedException ($"Unexpected scope type '{typeRef.Scope.GetType ()}' for type reference '{typeRef.FullName}'");
}
}
}
@ -85,8 +114,10 @@ namespace Mono.Linker.Tests.TestCasesRunner
Assert.IsTrue (linkResult.OutputAssemblyPath.FileExists (), $"The linked output assembly was not found. Expected at {linkResult.OutputAssemblyPath}");
var linked = ResolveLinkedAssembly (linkResult.OutputAssemblyPath.FileNameWithoutExtension);
if (ShouldValidateIL (original))
VerifyIL (linkResult.OutputAssemblyPath);
if (ShouldValidateIL (original)) {
VerifyIL (linkResult.OutputAssemblyPath, linked);
ValidateTypeRefsHaveValidAssemblyRefs (linked);
}
InitialChecking (linkResult, original, linked);
@ -99,6 +130,7 @@ namespace Mono.Linker.Tests.TestCasesRunner
}
VerifyLinkingOfOtherAssemblies (original);
VerifyILOfOtherAssemblies (linkResult);
AdditionalChecking (linkResult, original);
} finally {
_originalsResolver.Dispose ();
@ -116,6 +148,17 @@ namespace Mono.Linker.Tests.TestCasesRunner
}
}
void VerifyILOfOtherAssemblies (LinkedTestCaseResult linkResult)
{
foreach (var linkedAssemblyPath in linkResult.Sandbox.OutputDirectory.Files ("*.dll")) {
if (linkedAssemblyPath == linkResult.OutputAssemblyPath)
continue;
var linked = ResolveLinkedAssembly (linkedAssemblyPath.FileNameWithoutExtension);
ValidateTypeRefsHaveValidAssemblyRefs (linked);
}
}
protected virtual AssemblyChecker CreateAssemblyChecker (AssemblyDefinition original, AssemblyDefinition linked, LinkedTestCaseResult linkedTestCase)
{
return new AssemblyChecker (original, linked, linkedTestCase);

View file

@ -12,7 +12,7 @@ namespace Mono.Linker.Tests.TestCasesRunner
public string[] Defines;
public string[] References;
public SourceAndDestinationPair[] Resources;
public string AdditionalArguments;
public string[] AdditionalArguments;
public string CompilerToUse;
public bool AddAsReference;
public bool RemoveFromLinkerInput;

View file

@ -227,7 +227,7 @@ namespace Mono.Linker.Tests.TestCasesRunner
References = ((CustomAttributeArgument[]) ctorArguments[2].Value)?.Select (arg => arg.Value.ToString ()).ToArray (),
Defines = ((CustomAttributeArgument[]) ctorArguments[3].Value)?.Select (arg => arg.Value.ToString ()).ToArray (),
Resources = ResourcesForAttributeArgument (ctorArguments[4]),
AdditionalArguments = (string) ctorArguments[5].Value,
AdditionalArguments = ((CustomAttributeArgument[]) ctorArguments[5].Value)?.Select (arg => arg.Value.ToString ()).ToArray (),
CompilerToUse = (string) ctorArguments[6].Value,
AddAsReference = ctorArguments.Count >= 8 ? (bool) ctorArguments[7].Value : true,
RemoveFromLinkerInput = ctorArguments.Count >= 9 ? (bool) ctorArguments[8].Value : false,

View file

@ -94,7 +94,7 @@ namespace Mono.Linker.Tests.TestCasesRunner
{
var allDefines = defines.Concat (setupCompileInfo.Defines ?? Array.Empty<string> ()).ToArray ();
var allReferences = references.Concat (setupCompileInfo.References?.Select (p => MakeSupportingAssemblyReferencePathAbsolute (outputDirectory, p)) ?? Array.Empty<NPath> ()).ToArray ();
string[] additionalArguments = string.IsNullOrEmpty (setupCompileInfo.AdditionalArguments) ? null : new[] { setupCompileInfo.AdditionalArguments };
string[] additionalArguments = setupCompileInfo.AdditionalArguments;
return new CompilerOptions {
OutputPath = outputDirectory.Combine (setupCompileInfo.OutputName),
SourceFiles = sourceFiles,