Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a library mode for type hierarchy marking #2114

Merged
merged 6 commits into from
Jul 13, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ public DynamicallyAccessedMembersTypeHierarchy (LinkContext context, MarkStep ma

Debug.Assert (!apply || annotation != DynamicallyAccessedMemberTypes.None);

// If OptimizeTypeHierarchyAnnotations is disabled, we will apply the annotations without seeing object.GetType()
// Unfortunately, we cannot apply the annotation to type derived from EventSource - Revisit after https://github.com/dotnet/runtime/issues/54859
apply |= (annotation != DynamicallyAccessedMemberTypes.None) && !_context.IsOptimizationEnabled (CodeOptimizations.OptimizeTypeHierarchyAnnotations, type)
&& !_context.IsOptimizationEnabled (CodeOptimizations.RemoveEventSourceSpecialHandling, type) && !BCL.EventTracingForWindows.IsEventSourceImplementation (type, _context);

// Store the results in the cache
// Don't store empty annotations for non-interface types - we can use the presence of the row
// in the cache as indication of it instead.
Expand Down
3 changes: 3 additions & 0 deletions src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ public void ApplyDynamicallyAccessedMembersToType (ref ReflectionPatternContext
Debug.Assert (annotation != DynamicallyAccessedMemberTypes.None);

reflectionPatternContext.AnalyzingPattern ();
// Handle cases where a type has no members but annotations are to be applied to derived type members
reflectionPatternContext.RecordHandledPattern ();

MarkTypeForDynamicallyAccessedMembers (ref reflectionPatternContext, type, annotation);
}

Expand Down
7 changes: 5 additions & 2 deletions src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1776,7 +1776,8 @@ protected internal virtual TypeDefinition MarkType (TypeReference reference, Dep
MarkSerializable (type);

// This marks static fields of KeyWords/OpCodes/Tasks subclasses of an EventSource type.
if (BCL.EventTracingForWindows.IsEventSourceImplementation (type, _context)) {
// The special handling of EventSource is still needed in .NET6 in library mode
if ((_context.GetTargetRuntimeVersion () < TargetRuntimeVersion.NET6 || !_context.IsOptimizationEnabled (CodeOptimizations.RemoveEventSourceSpecialHandling, type)) && BCL.EventTracingForWindows.IsEventSourceImplementation (type, _context)) {
MarkEventSourceProviders (type);
}

Expand Down Expand Up @@ -1911,7 +1912,8 @@ void MarkTypeSpecialCustomAttributes (TypeDefinition type)
case "DebuggerTypeProxyAttribute" when attrType.Namespace == "System.Diagnostics":
MarkTypeWithDebuggerTypeProxyAttribute (type, attribute);
break;
case "EventDataAttribute" when attrType.Namespace == "System.Diagnostics.Tracing":
// The special handling of EventSource is still needed in .NET6 in library mode
case "EventDataAttribute" when attrType.Namespace == "System.Diagnostics.Tracing" && (_context.GetTargetRuntimeVersion () < TargetRuntimeVersion.NET6 || !_context.IsOptimizationEnabled (CodeOptimizations.RemoveEventSourceSpecialHandling, type)):
if (MarkMethodsIf (type.Methods, MethodDefinitionExtensions.IsPublicInstancePropertyMethod, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, type)))
Tracer.AddDirectDependency (attribute, new DependencyInfo (DependencyKind.CustomAttribute, type), marked: false);
break;
Expand Down Expand Up @@ -2377,6 +2379,7 @@ protected virtual bool AlwaysMarkTypeAsInstantiated (TypeDefinition td)

void MarkEventSourceProviders (TypeDefinition td)
{
Debug.Assert (_context.GetTargetRuntimeVersion () < TargetRuntimeVersion.NET6 || !_context.IsOptimizationEnabled (CodeOptimizations.RemoveEventSourceSpecialHandling, td));
foreach (var nestedType in td.NestedTypes) {
if (BCL.EventTracingForWindows.IsProviderName (nestedType.Name))
MarkStaticFields (nestedType, new DependencyInfo (DependencyKind.EventSourceProviderField, td));
Expand Down
4 changes: 3 additions & 1 deletion src/linker/Linker.Steps/RootAssemblyInputStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ protected override void Process ()
CodeOptimizations.RemoveDescriptors |
CodeOptimizations.RemoveLinkAttributes |
CodeOptimizations.RemoveSubstitutions |
CodeOptimizations.RemoveDynamicDependencyAttribute, assembly.Name.Name);
CodeOptimizations.RemoveDynamicDependencyAttribute |
CodeOptimizations.OptimizeTypeHierarchyAnnotations |
CodeOptimizations.RemoveEventSourceSpecialHandling, assembly.Name.Name);

// No metadata trimming
Context.MetadataTrimming = MetadataTrimming.None;
Expand Down
16 changes: 15 additions & 1 deletion src/linker/Linker/LinkContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,9 @@ public LinkContext (Pipeline pipeline, ILogger logger)
CodeOptimizations.RemoveDescriptors |
CodeOptimizations.RemoveLinkAttributes |
CodeOptimizations.RemoveSubstitutions |
CodeOptimizations.RemoveDynamicDependencyAttribute;
CodeOptimizations.RemoveDynamicDependencyAttribute |
CodeOptimizations.OptimizeTypeHierarchyAnnotations |
CodeOptimizations.RemoveEventSourceSpecialHandling;

Optimizations = new CodeOptimizationsSettings (defaultOptimizations);
}
Expand Down Expand Up @@ -986,5 +988,17 @@ public enum CodeOptimizations
RemoveSubstitutions = 1 << 21,
RemoveLinkAttributes = 1 << 22,
RemoveDynamicDependencyAttribute = 1 << 23,

/// <summary>
/// Option to apply annotations to type heirarchy
/// Enable type heirarchy apply in library mode to annotate derived types eagerly
/// Otherwise, type annotation will only be applied with calls to object.GetType()
/// </summary>
OptimizeTypeHierarchyAnnotations = 1 << 24,

/// <summary>
/// Pption to not special case EventSource
/// </summary>
RemoveEventSourceSpecialHandling = 1 << 25,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ public class CustomEventSource
{
public static void Main ()
{
var b = MyCompanyEventSource.Log.IsEnabled ();
// This call will trigger Object.GetType() Reflection pattern that will preserve all
EventSource.GenerateManifest (typeof (MyCompanyEventSource), null);
}
}

Expand All @@ -21,29 +22,41 @@ public static void Main ()
[EventSource (Name = "MyCompany")]
class MyCompanyEventSource : EventSource
{
[KeptMember (".ctor()")]
[Kept]
public class Keywords
{
[Kept]
public const EventKeywords Page = (EventKeywords) 1;

[Kept]
public int Unused;
}

[KeptMember (".ctor()")]
[Kept]
public class Tasks
{
[Kept]
public const EventTask Page = (EventTask) 1;

[Kept]
public int Unused;
}

[KeptMember (".ctor()")]
[Kept]
class NotMatching
{
}

[Kept]
public static MyCompanyEventSource Log = new MyCompanyEventSource ();

[Kept]
int private_member;

[Kept]
void PrivateMethod () { }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
using System.Diagnostics.Tracing;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;

namespace Mono.Linker.Tests.Cases.BCLFeatures.ETW
{
[SetupLinkerArgument ("-a", "test.exe", "library")]
[KeptMember (".ctor()")]
public class CustomLibraryEventSource
{
public static void Main ()
{
// Reference to a derived EventSource but does not trigger Object.GetType()
var b = CustomEventSourceInLibraryMode.Log.IsEnabled ();
}
}

[Kept]
[KeptBaseType (typeof (EventSource))]
[KeptAttributeAttribute (typeof (EventSourceAttribute))]
[KeptMember (".ctor()")]
[KeptMember (".cctor()")]

[EventSource (Name = "MyLibraryCompany")]
class CustomEventSourceInLibraryMode : EventSource
{
// In library mode, we special case nested types
[Kept]
public class Keywords
{
[Kept]
public const EventKeywords Page = (EventKeywords) 1;

public int Unused;
}

[Kept]
public class Tasks
{
[Kept]
public const EventTask Page = (EventTask) 1;

public int Unused;
}

class NotMatching
{
}

[Kept]
public static CustomEventSourceInLibraryMode Log = new CustomEventSourceInLibraryMode ();

int private_member;

void PrivateMethod () { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@ private int PrivateProperty {
[Kept]
public static class PublicNestedType
{
// PublicNestedType should be kept but linker won't mark anything besides the declaration
[Kept]
public static int _nestedPublicField;
[Kept]
Expand Down Expand Up @@ -261,7 +260,6 @@ private int PrivateProperty {
[Kept]
public static class PublicNestedType
{
// PublicNestedType should be kept but linker won't mark anything besides the declaration
[Kept]
public static int _nestedPublicField;
[Kept]
Expand Down Expand Up @@ -318,7 +316,6 @@ private int PrivateProperty {
[Kept]
public static class PublicNestedType
{
// PublicNestedType should be kept but linker won't mark anything besides the declaration
[Kept]
public static int _nestedPublicField;
[Kept]
Expand Down Expand Up @@ -375,7 +372,6 @@ private int PrivateProperty {
[Kept]
public static class PublicNestedType
{
// PublicNestedType should be kept but linker won't mark anything besides the declaration
[Kept]
public static int _nestedPublicField;
[Kept]
Expand Down Expand Up @@ -432,7 +428,6 @@ private int PrivateProperty {
[Kept]
public static class PublicNestedType
{
// PublicNestedType should be kept but linker won't mark anything besides the declaration
[Kept]
public static int _nestedPublicField;
[Kept]
Expand Down
158 changes: 158 additions & 0 deletions test/Mono.Linker.Tests.Cases/Reflection/ObjectGetTypeLibraryMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// 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;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Helpers;
using Mono.Linker.Tests.Cases.Expectations.Metadata;

namespace Mono.Linker.Tests.Cases.Reflection
{
[SetupLinkerArgument ("-a", "test.exe", "library")]
[ExpectedNoWarnings]
[KeptMember (".ctor()")]
public class ObjectGetTypeLibraryMode
{
public static void Main ()
{
BasicAnnotationWithNoDerivedClasses.Test ();
BasicNoAnnotationWithNoDerivedClasses.Test ();
}

[Kept]
class BasicAnnotationWithNoDerivedClasses
{
[Kept]
[KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))]
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)]
public interface IBasicAnnotatedInterface
{
}

[Kept]
[KeptMember (".ctor()")]
[KeptInterface (typeof (IBasicAnnotatedInterface))]
class ClassImplementingAnnotatedInterface : IBasicAnnotatedInterface
{
[Kept]
public void UsedMethod () { }
[Kept] // The type is not sealed, so trimmer will apply the annotation from the interface
public void UnusedMethod () { }
}

[Kept]
static void TestInterface ()
{
var classImplementingInterface = new ClassImplementingAnnotatedInterface ();
}

[Kept]
[KeptMember (".ctor()")]
[KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))]
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)]
class BasicAnnotatedClass
{
[Kept]
public void UsedMethod () { }
[Kept] // The type is not sealed, so trimmer will apply the annotation from the interface
public void UnusedMethod () { }
}

[Kept]
static void TestClass ()
{
var instance = new BasicAnnotatedClass ();
}

[Kept]
[KeptMember (".ctor()")]
[KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))]
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)]
struct BasicAnnotatedStruct
{
[Kept]
public void UsedMethod () { }
[Kept]
public void UnusedMethod () { }
}

[Kept]
static void TestStruct ()
{
var instance = new BasicAnnotatedStruct ();
}

[Kept]
public static void Test ()
{
TestInterface ();
TestClass ();
TestStruct ();
}
}

[Kept]
class BasicNoAnnotationWithNoDerivedClasses
{
[Kept]
public interface IBasicNoAnnotatedInterface
{
}

[Kept]
[KeptMember (".ctor()")]
[KeptInterface (typeof (IBasicNoAnnotatedInterface))]
class ClassImplementingNoAnnotatedInterface : IBasicNoAnnotatedInterface
{
public void UsedMethod () { }
public void UnusedMethod () { }
}

[Kept]
static void TestInterface ()
{
var classImplementingInterface = new ClassImplementingNoAnnotatedInterface ();
}

[Kept]
[KeptMember (".ctor()")]
class BasicNoAnnotatedClass
{
public void UsedMethod () { }
public void UnusedMethod () { }
}

[Kept]
static void TestClass ()
{
var instance = new BasicNoAnnotatedClass ();
}

[Kept]
[KeptMember (".ctor()")]
struct BasicNoAnnotatedStruct
{
public void UsedMethod () { }
public void UnusedMethod () { }
}

[Kept]
static void TestStruct ()
{
var instance = new BasicNoAnnotatedStruct ();
}

[Kept]
public static void Test ()
{
TestInterface ();
TestClass ();
TestStruct ();
}
}
}
}