From 1011edc50c0ecdaf80bc9b1bc9c802f86583170f Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Wed, 11 Aug 2021 15:36:46 -0500 Subject: [PATCH] Annotate DebuggerVisualizerAttribute to be linker safe (#57082) * Annotate DebuggerVisualizerAttribute to be linker safe Fix #49055 --- .../Diagnostics/DebuggerVisualizerAttribute.cs | 26 +++- src/libraries/System.Runtime/ref/System.Runtime.cs | 14 +- .../DebuggerVisualizerAttributeTests.cs | 158 +++++++++++++++++++++ .../System.Runtime.TrimmingTests.proj | 1 + 4 files changed, 187 insertions(+), 12 deletions(-) create mode 100644 src/libraries/System.Runtime/tests/TrimmingTests/DebuggerVisualizerAttributeTests.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/DebuggerVisualizerAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/DebuggerVisualizerAttribute.cs index 0ad6bd8..425156d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/DebuggerVisualizerAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/DebuggerVisualizerAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.Diagnostics { /// @@ -12,18 +14,23 @@ namespace System.Diagnostics { private Type? _target; - public DebuggerVisualizerAttribute(string visualizerTypeName) + public DebuggerVisualizerAttribute( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string visualizerTypeName) { VisualizerTypeName = visualizerTypeName; } - public DebuggerVisualizerAttribute(string visualizerTypeName, string? visualizerObjectSourceTypeName) + public DebuggerVisualizerAttribute( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string visualizerTypeName, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string? visualizerObjectSourceTypeName) { VisualizerTypeName = visualizerTypeName; VisualizerObjectSourceTypeName = visualizerObjectSourceTypeName; } - public DebuggerVisualizerAttribute(string visualizerTypeName, Type visualizerObjectSource) + public DebuggerVisualizerAttribute( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string visualizerTypeName, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type visualizerObjectSource) { if (visualizerObjectSource == null) { @@ -34,7 +41,8 @@ namespace System.Diagnostics VisualizerObjectSourceTypeName = visualizerObjectSource.AssemblyQualifiedName; } - public DebuggerVisualizerAttribute(Type visualizer) + public DebuggerVisualizerAttribute( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type visualizer) { if (visualizer == null) { @@ -44,7 +52,9 @@ namespace System.Diagnostics VisualizerTypeName = visualizer.AssemblyQualifiedName!; } - public DebuggerVisualizerAttribute(Type visualizer, Type visualizerObjectSource) + public DebuggerVisualizerAttribute( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type visualizer, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type visualizerObjectSource) { if (visualizer == null) { @@ -59,7 +69,9 @@ namespace System.Diagnostics VisualizerObjectSourceTypeName = visualizerObjectSource.AssemblyQualifiedName; } - public DebuggerVisualizerAttribute(Type visualizer, string? visualizerObjectSourceTypeName) + public DebuggerVisualizerAttribute( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type visualizer, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string? visualizerObjectSourceTypeName) { if (visualizer == null) { @@ -70,8 +82,10 @@ namespace System.Diagnostics VisualizerObjectSourceTypeName = visualizerObjectSourceTypeName; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] public string? VisualizerObjectSourceTypeName { get; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] public string VisualizerTypeName { get; } public string? Description { get; set; } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 3016a1e..fd7ba1e 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -8860,16 +8860,18 @@ namespace System.Diagnostics [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple=true)] public sealed partial class DebuggerVisualizerAttribute : System.Attribute { - public DebuggerVisualizerAttribute(string visualizerTypeName) { } - public DebuggerVisualizerAttribute(string visualizerTypeName, string? visualizerObjectSourceTypeName) { } - public DebuggerVisualizerAttribute(string visualizerTypeName, System.Type visualizerObjectSource) { } - public DebuggerVisualizerAttribute(System.Type visualizer) { } - public DebuggerVisualizerAttribute(System.Type visualizer, string? visualizerObjectSourceTypeName) { } - public DebuggerVisualizerAttribute(System.Type visualizer, System.Type visualizerObjectSource) { } + public DebuggerVisualizerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] string visualizerTypeName) { } + public DebuggerVisualizerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] string visualizerTypeName, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] string? visualizerObjectSourceTypeName) { } + public DebuggerVisualizerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] string visualizerTypeName, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type visualizerObjectSource) { } + public DebuggerVisualizerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type visualizer) { } + public DebuggerVisualizerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type visualizer, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] string? visualizerObjectSourceTypeName) { } + public DebuggerVisualizerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type visualizer, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type visualizerObjectSource) { } public string? Description { get { throw null; } set { } } public System.Type? Target { get { throw null; } set { } } public string? TargetTypeName { get { throw null; } set { } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] public string? VisualizerObjectSourceTypeName { get { throw null; } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] public string VisualizerTypeName { get { throw null; } } } [System.AttributeUsage(AttributeTargets.Class | System.AttributeTargets.Method | System.AttributeTargets.Constructor | System.AttributeTargets.Struct, Inherited = false)] diff --git a/src/libraries/System.Runtime/tests/TrimmingTests/DebuggerVisualizerAttributeTests.cs b/src/libraries/System.Runtime/tests/TrimmingTests/DebuggerVisualizerAttributeTests.cs new file mode 100644 index 0000000..dccaf03 --- /dev/null +++ b/src/libraries/System.Runtime/tests/TrimmingTests/DebuggerVisualizerAttributeTests.cs @@ -0,0 +1,158 @@ +using System; +using System.Diagnostics; + +/// +/// Tests that types used by DebuggerVisualizerAttribute are not trimmed out +/// when Debugger.IsSupported is true (the default). +/// +class Program +{ + static int Main(string[] args) + { + MyClass myClass = new MyClass() { Name = "trimmed" }; + MyClassWithVisualizerString myClassWithString = new MyClassWithVisualizerString() { Name = "trimmed" }; + + Type[] allTypes = typeof(MyClass).Assembly.GetTypes(); + bool foundDebuggerVisualizer = false; + bool foundDebuggerVisualizer2 = false; + bool foundDebuggerVisualizerObjectSource = false; + bool foundStringDebuggerVisualizer = false; + bool foundStringDebuggerVisualizer2 = false; + bool foundStringDebuggerVisualizerObjectSource = false; + for (int i = 0; i < allTypes.Length; i++) + { + Type currentType = allTypes[i]; + if (currentType.FullName == "Program+MyClass+DebuggerVisualizer" && + currentType.GetProperties().Length == 1 && + currentType.GetConstructors().Length == 1) + { + foundDebuggerVisualizer = true; + } + else if (currentType.FullName == "Program+MyClass+DebuggerVisualizer2" && + currentType.GetProperties().Length == 1 && + currentType.GetConstructors().Length == 1) + { + foundDebuggerVisualizer2 = true; + } + else if (currentType.FullName == "Program+MyClass+DebuggerVisualizerObjectSource" && + currentType.GetProperties().Length == 1 && + currentType.GetConstructors().Length == 1) + { + foundDebuggerVisualizerObjectSource = true; + } + else if (currentType.FullName == "Program+MyClassWithVisualizerStringVisualizer" && + currentType.GetProperties().Length == 1 && + currentType.GetConstructors().Length == 1) + { + foundStringDebuggerVisualizer = true; + } + else if (currentType.FullName == "Program+MyClassWithVisualizerStringVisualizer2" && + currentType.GetProperties().Length == 1 && + currentType.GetConstructors().Length == 1) + { + foundStringDebuggerVisualizer2 = true; + } + else if (currentType.FullName == "Program+MyClassWithVisualizerStringVisualizerObjectSource" && + currentType.GetProperties().Length == 1 && + currentType.GetConstructors().Length == 1) + { + foundStringDebuggerVisualizerObjectSource = true; + } + } + + if (!foundDebuggerVisualizer) return -1; + if (!foundDebuggerVisualizer2) return -2; + if (!foundDebuggerVisualizerObjectSource) return -3; + if (!foundStringDebuggerVisualizer) return -4; + if (!foundStringDebuggerVisualizer2) return -5; + if (!foundStringDebuggerVisualizerObjectSource) return -6; + + return 100; + } + + [DebuggerVisualizer(typeof(DebuggerVisualizer))] + [DebuggerVisualizer(typeof(DebuggerVisualizer2), typeof(DebuggerVisualizerObjectSource))] + public class MyClass + { + public string Name { get; set; } + + private class DebuggerVisualizer + { + private MyClass _instance; + + public DebuggerVisualizer(MyClass instance) + { + _instance = instance; + } + + public string DebuggerName => _instance.Name + " Visualizer"; + } + + private class DebuggerVisualizer2 + { + private MyClass _instance; + + public DebuggerVisualizer2(MyClass instance) + { + _instance = instance; + } + + public string DebuggerName => _instance.Name + " Visualizer"; + } + + private class DebuggerVisualizerObjectSource + { + private MyClass _instance; + + public DebuggerVisualizerObjectSource(MyClass instance) + { + _instance = instance; + } + + public string DebuggerName => _instance.Name + " Visualizer"; + } + } + + [DebuggerVisualizer("Program+MyClassWithVisualizerStringVisualizer")] + [DebuggerVisualizer("Program+MyClassWithVisualizerStringVisualizer2", "Program+MyClassWithVisualizerStringVisualizerObjectSource")] + public class MyClassWithVisualizerString + { + public string Name { get; set; } + } + + internal class MyClassWithVisualizerStringVisualizer + { + private MyClassWithVisualizerString _instance; + + public MyClassWithVisualizerStringVisualizer(MyClassWithVisualizerString instance) + { + _instance = instance; + } + + public string DebuggerName => _instance.Name + " StringVisualizer"; + } + + internal class MyClassWithVisualizerStringVisualizer2 + { + private MyClassWithVisualizerString _instance; + + public MyClassWithVisualizerStringVisualizer2(MyClassWithVisualizerString instance) + { + _instance = instance; + } + + public string DebuggerName => _instance.Name + " StringVisualizer"; + } + + internal class MyClassWithVisualizerStringVisualizerObjectSource + { + private MyClassWithVisualizerString _instance; + + public MyClassWithVisualizerStringVisualizerObjectSource(MyClassWithVisualizerString instance) + { + _instance = instance; + } + + public string DebuggerName => _instance.Name + " StringVisualizer"; + } +} diff --git a/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj b/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj index 844edd5..918079b 100644 --- a/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj +++ b/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj @@ -7,6 +7,7 @@ osx-x64;linux-x64;browser-wasm + -- 2.7.4