Skip to content

Commit 8262020

Browse files
authored
Add option to keep/remove COM interfaces (#101087)
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.
1 parent 1e42214 commit 8262020

File tree

6 files changed

+68
-3
lines changed

6 files changed

+68
-3
lines changed

src/tools/illink/src/linker/CompatibilitySuppressions.xml

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
33
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
44
<Suppression>
@@ -1497,6 +1497,18 @@
14971497
<DiagnosticId>CP0002</DiagnosticId>
14981498
<Target>M:Mono.Linker.Steps.SubStepsDispatcher.Process(Mono.Linker.LinkContext)</Target>
14991499
</Suppression>
1500+
<Suppression>
1501+
<DiagnosticId>CP0002</DiagnosticId>
1502+
<Target>M:Mono.Linker.LinkContext.get_KeepComInterfaces</Target>
1503+
<Left>ref/net9.0/illink.dll</Left>
1504+
<Right>lib/net9.0/illink.dll</Right>
1505+
</Suppression>
1506+
<Suppression>
1507+
<DiagnosticId>CP0002</DiagnosticId>
1508+
<Target>M:Mono.Linker.LinkContext.set_KeepComInterfaces(System.Boolean)</Target>
1509+
<Left>ref/net9.0/illink.dll</Left>
1510+
<Right>lib/net9.0/illink.dll</Right>
1511+
</Suppression>
15001512
<Suppression>
15011513
<DiagnosticId>CP0008</DiagnosticId>
15021514
<Target>T:Mono.Linker.LinkContext</Target>
@@ -1537,4 +1549,4 @@
15371549
<DiagnosticId>CP0017</DiagnosticId>
15381550
<Target>M:Mono.Linker.LinkContext.Resolve(Mono.Cecil.AssemblyNameReference)$0</Target>
15391551
</Suppression>
1540-
</Suppressions>
1552+
</Suppressions>

src/tools/illink/src/linker/Linker.Steps/MarkStep.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2438,7 +2438,7 @@ protected virtual bool ShouldMarkInterfaceImplementationList (TypeDefinition typ
24382438

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

24442444
return IsFullyPreserved (type);

src/tools/illink/src/linker/Linker/Driver.cs

+8
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,12 @@ protected int SetupContext (ILogger? customLogger = null)
362362
context.SetCustomData (values[0], values[1]);
363363
continue;
364364

365+
case "--keep-com-interfaces":
366+
if (!GetBoolParam (token, l => context.KeepComInterfaces = l))
367+
return -1;
368+
369+
continue;
370+
365371
case "--keep-compilers-resources":
366372
if (!GetBoolParam (token, l => keepCompilersResources = l))
367373
return -1;
@@ -1284,6 +1290,7 @@ protected virtual LinkContext GetDefaultContext (Pipeline pipeline, ILogger? log
12841290
return new LinkContext (pipeline, logger ?? new ConsoleLogger (), "output") {
12851291
TrimAction = AssemblyAction.Link,
12861292
DefaultAction = AssemblyAction.Link,
1293+
KeepComInterfaces = true,
12871294
};
12881295
}
12891296

@@ -1369,6 +1376,7 @@ static void Usage ()
13691376
Console.WriteLine (" sealer: Any method or type which does not have override is marked as sealed");
13701377
Console.WriteLine (" --explicit-reflection Adds to members never used through reflection DisablePrivateReflection attribute. Defaults to false");
13711378
Console.WriteLine (" --feature FEATURE VALUE Apply any optimizations defined when this feature setting is a constant known at link time");
1379+
Console.WriteLine (" --keep-com-interfaces Keep COM interfaces implemented by kept types. Defaults to true");
13721380
Console.WriteLine (" --keep-compilers-resources Keep assembly resources used for F# compilation resources. Defaults to false");
13731381
Console.WriteLine (" --keep-dep-attributes Keep attributes used for manual dependency tracking. Defaults to false");
13741382
Console.WriteLine (" --keep-metadata NAME Keep metadata which would otherwise be removed if not used");

src/tools/illink/src/linker/Linker/LinkContext.cs

+2
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ public Pipeline Pipeline {
108108

109109
public bool LinkSymbols { get; set; }
110110

111+
public bool KeepComInterfaces { get; set; }
112+
111113
public bool KeepMembersForDebugger { get; set; } = true;
112114

113115
public bool IgnoreUnresolved { get; set; } = true;

src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/UnusedComInterfaceIsKept.cs

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public static void Main ()
2121
[Guid ("D7BB1889-3AB7-4681-A115-60CA9158FECA")]
2222
interface IBar
2323
{
24+
// Trimming may remove members from COM interfaces
25+
// even when keeping the COM-related attributes.
26+
// https://github.com/dotnet/runtime/issues/101128
2427
void Bar ();
2528
}
2629

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System.Runtime.InteropServices;
2+
using Mono.Linker.Tests.Cases.Expectations.Assertions;
3+
using Mono.Linker.Tests.Cases.Expectations.Metadata;
4+
5+
namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType
6+
{
7+
/// <summary>
8+
/// With --keep-com-interfaces false, we apply the unused interface rules also to com interfaces.
9+
/// </summary>
10+
[SetupLinkerArgument ("--keep-com-interfaces", "false")]
11+
public class UnusedComInterfaceIsRemoved
12+
{
13+
public static void Main ()
14+
{
15+
var i = new A ();
16+
i.Foo ();
17+
}
18+
19+
[ComImport]
20+
[Guid ("D7BB1889-3AB7-4681-A115-60CA9158FECA")]
21+
interface IBar
22+
{
23+
void Bar ();
24+
}
25+
26+
[Kept]
27+
[KeptMember (".ctor()")]
28+
class A : IBar
29+
{
30+
[Kept]
31+
public void Foo ()
32+
{
33+
}
34+
35+
public void Bar ()
36+
{
37+
}
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)