Skip to content

Commit

Permalink
Add option to keep/remove COM interfaces (#101087)
Browse files Browse the repository at this point in the history
Adds an illink option that can be used to remove COM
interfaces (the existing behavior is to preserve COM interfaces
on marked types). If a type implements a COM interface that is
also marked elsewhere, it will still be kept even with
`--keep-com-interfaces false`. This includes windows runtime
interfaces under the same option name.

This eliminates some trim warnings in winforms due to
trim-incompatible code in Control's implementations of COM
interfaces like IPersistPropertyBag. Experimentally, this still
produces a working app for the scenario we've been testing
(https://github.com/dotnet/winforms/tree/main/src/System.Windows.Forms/tests/IntegrationTests/TrimTest).
For now, this just introduces the option without setting it by
default anywhere (or exposing it in MSBuild) so that we can opt
in from the winforms scenario. We might consider setting it
wherever `BuiltInComInteropSupport` is false.
  • Loading branch information
sbomer committed Apr 16, 2024
1 parent 1e42214 commit 8262020
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 3 deletions.
16 changes: 14 additions & 2 deletions src/tools/illink/src/linker/CompatibilitySuppressions.xml
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
Expand Down Expand Up @@ -1497,6 +1497,18 @@
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Mono.Linker.Steps.SubStepsDispatcher.Process(Mono.Linker.LinkContext)</Target>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Mono.Linker.LinkContext.get_KeepComInterfaces</Target>
<Left>ref/net9.0/illink.dll</Left>
<Right>lib/net9.0/illink.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Mono.Linker.LinkContext.set_KeepComInterfaces(System.Boolean)</Target>
<Left>ref/net9.0/illink.dll</Left>
<Right>lib/net9.0/illink.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0008</DiagnosticId>
<Target>T:Mono.Linker.LinkContext</Target>
Expand Down Expand Up @@ -1537,4 +1549,4 @@
<DiagnosticId>CP0017</DiagnosticId>
<Target>M:Mono.Linker.LinkContext.Resolve(Mono.Cecil.AssemblyNameReference)$0</Target>
</Suppression>
</Suppressions>
</Suppressions>
2 changes: 1 addition & 1 deletion src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
Expand Up @@ -2438,7 +2438,7 @@ protected virtual bool ShouldMarkInterfaceImplementationList (TypeDefinition typ

// It's hard to know if a com or windows runtime interface will be needed from managed code alone,
// so as a precaution we will mark these interfaces once the type is instantiated
if (resolvedInterfaceType.IsImport || resolvedInterfaceType.IsWindowsRuntime)
if (Context.KeepComInterfaces && (resolvedInterfaceType.IsImport || resolvedInterfaceType.IsWindowsRuntime))
return true;

return IsFullyPreserved (type);
Expand Down
8 changes: 8 additions & 0 deletions src/tools/illink/src/linker/Linker/Driver.cs
Expand Up @@ -362,6 +362,12 @@ protected int SetupContext (ILogger? customLogger = null)
context.SetCustomData (values[0], values[1]);
continue;

case "--keep-com-interfaces":
if (!GetBoolParam (token, l => context.KeepComInterfaces = l))
return -1;

continue;

case "--keep-compilers-resources":
if (!GetBoolParam (token, l => keepCompilersResources = l))
return -1;
Expand Down Expand Up @@ -1284,6 +1290,7 @@ protected virtual LinkContext GetDefaultContext (Pipeline pipeline, ILogger? log
return new LinkContext (pipeline, logger ?? new ConsoleLogger (), "output") {
TrimAction = AssemblyAction.Link,
DefaultAction = AssemblyAction.Link,
KeepComInterfaces = true,
};
}

Expand Down Expand Up @@ -1369,6 +1376,7 @@ static void Usage ()
Console.WriteLine (" sealer: Any method or type which does not have override is marked as sealed");
Console.WriteLine (" --explicit-reflection Adds to members never used through reflection DisablePrivateReflection attribute. Defaults to false");
Console.WriteLine (" --feature FEATURE VALUE Apply any optimizations defined when this feature setting is a constant known at link time");
Console.WriteLine (" --keep-com-interfaces Keep COM interfaces implemented by kept types. Defaults to true");
Console.WriteLine (" --keep-compilers-resources Keep assembly resources used for F# compilation resources. Defaults to false");
Console.WriteLine (" --keep-dep-attributes Keep attributes used for manual dependency tracking. Defaults to false");
Console.WriteLine (" --keep-metadata NAME Keep metadata which would otherwise be removed if not used");
Expand Down
2 changes: 2 additions & 0 deletions src/tools/illink/src/linker/Linker/LinkContext.cs
Expand Up @@ -108,6 +108,8 @@ public class LinkContext : IMetadataResolver, ITryResolveMetadata, IDisposable

public bool LinkSymbols { get; set; }

public bool KeepComInterfaces { get; set; }

public bool KeepMembersForDebugger { get; set; } = true;

public bool IgnoreUnresolved { get; set; } = true;
Expand Down
Expand Up @@ -21,6 +21,9 @@ public static void Main ()
[Guid ("D7BB1889-3AB7-4681-A115-60CA9158FECA")]
interface IBar
{
// Trimming may remove members from COM interfaces
// even when keeping the COM-related attributes.
// https://github.com/dotnet/runtime/issues/101128
void Bar ();
}

Expand Down
@@ -0,0 +1,40 @@
using System.Runtime.InteropServices;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;

namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType
{
/// <summary>
/// With --keep-com-interfaces false, we apply the unused interface rules also to com interfaces.
/// </summary>
[SetupLinkerArgument ("--keep-com-interfaces", "false")]
public class UnusedComInterfaceIsRemoved
{
public static void Main ()
{
var i = new A ();
i.Foo ();
}

[ComImport]
[Guid ("D7BB1889-3AB7-4681-A115-60CA9158FECA")]
interface IBar
{
void Bar ();
}

[Kept]
[KeptMember (".ctor()")]
class A : IBar
{
[Kept]
public void Foo ()
{
}

public void Bar ()
{
}
}
}
}

0 comments on commit 8262020

Please sign in to comment.