From 923f23f26ac8565bd33aed680b6be14d01a2522e Mon Sep 17 00:00:00 2001 From: Michael Render Date: Wed, 23 Oct 2024 01:51:13 -0400 Subject: [PATCH 01/11] [dotnet] Add trimming attributes, address some trim warnings --- .../src/webdriver/DevTools/DevToolsDomains.cs | 26 +- .../DevTools/Json/JsonEnumMemberConverter.cs | 13 +- .../webdriver/Internal/TrimmingAttributes.cs | 420 ++++++++++++++++++ dotnet/src/webdriver/WebDriver.csproj | 5 + 4 files changed, 452 insertions(+), 12 deletions(-) create mode 100644 dotnet/src/webdriver/Internal/TrimmingAttributes.cs diff --git a/dotnet/src/webdriver/DevTools/DevToolsDomains.cs b/dotnet/src/webdriver/DevTools/DevToolsDomains.cs index f29e5eecaaf09..12e87aff5303f 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsDomains.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsDomains.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace OpenQA.Selenium.DevTools @@ -33,14 +34,22 @@ public abstract class DevToolsDomains // This is the list of known supported DevTools version implementation. // When new versions are implemented for support, new types must be // added to this dictionary. - private static readonly Dictionary SupportedDevToolsVersions = new Dictionary() + private static readonly Dictionary SupportedDevToolsVersions = new Dictionary() { - { 127, typeof(V127.V127Domains) }, - { 129, typeof(V129.V129Domains) }, - { 128, typeof(V128.V128Domains) }, - { 85, typeof(V85.V85Domains) } + { 127, new DomainType(typeof(V127.V127Domains)) }, + { 129, new DomainType(typeof(V129.V129Domains)) }, + { 128, new DomainType(typeof(V128.V128Domains)) }, + { 85, new DomainType(typeof(V85.V85Domains)) } }; + /// Workaround for trimming. + struct DomainType + { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + public Type Type; + + public DomainType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type t) => Type = t; + } /// /// Gets the version-specific domains for the DevTools session. This value must be cast to a version specific type to be at all useful. /// @@ -102,12 +111,13 @@ public static DevToolsDomains InitializeDomains(int protocolVersion, DevToolsSes return domains; } + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] private static Type MatchDomainsVersion(int desiredVersion, int versionRange) { // Return fast on an exact match - if (SupportedDevToolsVersions.ContainsKey(desiredVersion)) + if (SupportedDevToolsVersions.TryGetValue(desiredVersion, out DomainType type)) { - return SupportedDevToolsVersions[desiredVersion]; + return type.Type; } // Get the list of supported versions and sort descending @@ -121,7 +131,7 @@ private static Type MatchDomainsVersion(int desiredVersion, int versionRange) // (that is, closest without going over). if (desiredVersion >= supportedVersion && desiredVersion - supportedVersion < versionRange) { - return SupportedDevToolsVersions[supportedVersion]; + return SupportedDevToolsVersions[supportedVersion].Type; } } diff --git a/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs b/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs index 2ec7f4ae55723..8a5b1f42c511b 100644 --- a/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs +++ b/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.Serialization; using System.Text.Json; @@ -7,7 +8,8 @@ namespace OpenQA.Selenium.DevTools.Json { - internal class JsonEnumMemberConverter : JsonConverter where TEnum : Enum + internal class JsonEnumMemberConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum> + : JsonConverter where TEnum : struct, Enum { private readonly Dictionary _enumToString = new Dictionary(); private readonly Dictionary _stringToEnum = new Dictionary(); @@ -15,11 +17,14 @@ internal class JsonEnumMemberConverter : JsonConverter where TEnum public JsonEnumMemberConverter() { var type = typeof(TEnum); - var values = Enum.GetValues(type); - +#if NET5_0_OR_GREATER + TEnum[] values = Enum.GetValues(); +#else + Array values = Enum.GetValues(type); +#endif foreach (var value in values) { - var enumMember = type.GetMember(value.ToString())[0]; + var enumMember = type.GetField(value.ToString()); var attr = enumMember.GetCustomAttributes(typeof(EnumMemberAttribute), false) .Cast() .FirstOrDefault(); diff --git a/dotnet/src/webdriver/Internal/TrimmingAttributes.cs b/dotnet/src/webdriver/Internal/TrimmingAttributes.cs new file mode 100644 index 0000000000000..7d20d0f6e3ca2 --- /dev/null +++ b/dotnet/src/webdriver/Internal/TrimmingAttributes.cs @@ -0,0 +1,420 @@ +#nullable enable + +// As per guidance in https://devblogs.microsoft.com/dotnet/creating-aot-compatible-libraries/#targetframeworks + +namespace System.Diagnostics.CodeAnalysis +{ +#if !NET7_0_OR_GREATER + /// + /// Indicates that the specified method requires the ability to generate new code at runtime, + /// for example through . + /// + /// + /// This allows tools to understand which methods are unsafe to call when compiling ahead of time. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] + internal sealed class RequiresDynamicCodeAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified message. + /// + /// + /// A message that contains information about the usage of dynamic code. + /// + public RequiresDynamicCodeAttribute(string message) + { + Message = message; + } + + /// + /// Gets a message that contains information about the usage of dynamic code. + /// + public string Message { get; } + + /// + /// Gets or sets an optional URL that contains more information about the method, + /// why it requires dynamic code, and what options a consumer has to deal with it. + /// + public string? Url { get; set; } + } +#endif + +#if !NET5_0_OR_GREATER + /// + /// Indicates that the specified method requires dynamic access to code that is not referenced + /// statically, for example through . + /// + /// + /// This allows tools to understand which methods are unsafe to call when removing unreferenced + /// code from an application. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] + internal sealed class RequiresUnreferencedCodeAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified message. + /// + /// + /// A message that contains information about the usage of unreferenced code. + /// + public RequiresUnreferencedCodeAttribute(string message) + { + Message = message; + } + + /// + /// Gets a message that contains information about the usage of unreferenced code. + /// + public string Message { get; } + + /// + /// Gets or sets an optional URL that contains more information about the method, + /// why it requires unreferenced code, and what options a consumer has to deal with it. + /// + public string? Url { get; set; } + } + + /// + /// Suppresses reporting of a specific rule violation, allowing multiple suppressions on a + /// single code artifact. + /// + /// + /// is different than + /// in that it doesn't have a + /// . So it is always preserved in the compiled assembly. + /// + [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] + internal sealed class UnconditionalSuppressMessageAttribute : Attribute + { + /// + /// Initializes a new instance of the + /// class, specifying the category of the tool and the identifier for an analysis rule. + /// + /// The category for the attribute. + /// The identifier of the analysis rule the attribute applies to. + public UnconditionalSuppressMessageAttribute(string category, string checkId) + { + Category = category; + CheckId = checkId; + } + + /// + /// Gets the category identifying the classification of the attribute. + /// + /// + /// The property describes the tool or tool analysis category + /// for which a message suppression attribute applies. + /// + public string Category { get; } + + /// + /// Gets the identifier of the analysis tool rule to be suppressed. + /// + /// + /// Concatenated together, the and + /// properties form a unique check identifier. + /// + public string CheckId { get; } + + /// + /// Gets or sets the scope of the code that is relevant for the attribute. + /// + /// + /// The Scope property is an optional argument that specifies the metadata scope for which + /// the attribute is relevant. + /// + public string? Scope { get; set; } + + /// + /// Gets or sets a fully qualified path that represents the target of the attribute. + /// + /// + /// The property is an optional argument identifying the analysis target + /// of the attribute. An example value is "System.IO.Stream.ctor():System.Void". + /// Because it is fully qualified, it can be long, particularly for targets such as parameters. + /// The analysis tool user interface should be capable of automatically formatting the parameter. + /// + public string? Target { get; set; } + + /// + /// Gets or sets an optional argument expanding on exclusion criteria. + /// + /// + /// The property is an optional argument that specifies additional + /// exclusion where the literal metadata target is not sufficiently precise. For example, + /// the cannot be applied within a method, + /// and it may be desirable to suppress a violation against a statement in the method that will + /// give a rule violation, but not against all statements in the method. + /// + public string? MessageId { get; set; } + + /// + /// Gets or sets the justification for suppressing the code analysis message. + /// + public string? Justification { get; set; } + } + + /// + /// States a dependency that one member has on another. + /// + /// + /// This can be used to inform tooling of a dependency that is otherwise not evident purely from + /// metadata and IL, for example a member relied on via reflection. + /// + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method, + AllowMultiple = true, Inherited = false)] + internal sealed class DynamicDependencyAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on the same type as the consumer. + /// + /// The signature of the member depended on. + public DynamicDependencyAttribute(string memberSignature) + { + MemberSignature = memberSignature; + } + + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on a . + /// + /// The signature of the member depended on. + /// The containing . + public DynamicDependencyAttribute(string memberSignature, Type type) + { + MemberSignature = memberSignature; + Type = type; + } + + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on a type in an assembly. + /// + /// The signature of the member depended on. + /// The full name of the type containing the specified member. + /// The assembly name of the type containing the specified member. + public DynamicDependencyAttribute(string memberSignature, string typeName, string assemblyName) + { + MemberSignature = memberSignature; + TypeName = typeName; + AssemblyName = assemblyName; + } + + /// + /// Initializes a new instance of the class + /// with the specified types of members on a . + /// + /// The types of members depended on. + /// The containing the specified members. + public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, Type type) + { + MemberTypes = memberTypes; + Type = type; + } + + /// + /// Initializes a new instance of the class + /// with the specified types of members on a type in an assembly. + /// + /// The types of members depended on. + /// The full name of the type containing the specified members. + /// The assembly name of the type containing the specified members. + public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, string typeName, string assemblyName) + { + MemberTypes = memberTypes; + TypeName = typeName; + AssemblyName = assemblyName; + } + + /// + /// Gets the signature of the member depended on. + /// + /// + /// Either must be a valid string or + /// must not equal , but not both. + /// + public string? MemberSignature { get; } + + /// + /// Gets the which specifies the type + /// of members depended on. + /// + /// + /// Either must be a valid string or + /// must not equal , but not both. + /// + public DynamicallyAccessedMemberTypes MemberTypes { get; } + + /// + /// Gets the containing the specified member. + /// + /// + /// If neither nor are specified, + /// the type of the consumer is assumed. + /// + public Type? Type { get; } + + /// + /// Gets the full name of the type containing the specified member. + /// + /// + /// If neither nor are specified, + /// the type of the consumer is assumed. + /// + public string? TypeName { get; } + + /// + /// Gets the assembly name of the specified type. + /// + /// + /// is only valid when is specified. + /// + public string? AssemblyName { get; } + + /// + /// Gets or sets the condition in which the dependency is applicable, e.g. "DEBUG". + /// + public string? Condition { get; set; } + } + + /// + /// Indicates that certain members on a specified are accessed dynamically, + /// for example through . + /// + /// + /// This allows tools to understand which members are being accessed during the execution + /// of a program. + /// + /// This attribute is valid on members whose type is or . + /// + /// When this attribute is applied to a location of type , the assumption is + /// that the string represents a fully qualified type name. + /// + /// When this attribute is applied to a class, interface, or struct, the members specified + /// can be accessed dynamically on instances returned from calling + /// on instances of that class, interface, or struct. + /// + /// If the attribute is applied to a method it's treated as a special case and it implies + /// the attribute should be applied to the "this" parameter of the method. As such the attribute + /// should only be used on instance methods of types assignable to System.Type (or string, but no methods + /// will use it there). + /// + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter | + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, + Inherited = false)] + internal sealed class DynamicallyAccessedMembersAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified member types. + /// + /// The types of members dynamically accessed. + public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes) + { + MemberTypes = memberTypes; + } + + /// + /// Gets the which specifies the type + /// of members dynamically accessed. + /// + public DynamicallyAccessedMemberTypes MemberTypes { get; } + } + + /// + /// Specifies the types of members that are dynamically accessed. + /// + /// This enumeration has a attribute that allows a + /// bitwise combination of its member values. + /// + [Flags] + internal enum DynamicallyAccessedMemberTypes + { + /// + /// Specifies no members. + /// + None = 0, + + /// + /// Specifies the default, parameterless public constructor. + /// + PublicParameterlessConstructor = 0x0001, + + /// + /// Specifies all public constructors. + /// + PublicConstructors = 0x0002 | PublicParameterlessConstructor, + + /// + /// Specifies all non-public constructors. + /// + NonPublicConstructors = 0x0004, + + /// + /// Specifies all public methods. + /// + PublicMethods = 0x0008, + + /// + /// Specifies all non-public methods. + /// + NonPublicMethods = 0x0010, + + /// + /// Specifies all public fields. + /// + PublicFields = 0x0020, + + /// + /// Specifies all non-public fields. + /// + NonPublicFields = 0x0040, + + /// + /// Specifies all public nested types. + /// + PublicNestedTypes = 0x0080, + + /// + /// Specifies all non-public nested types. + /// + NonPublicNestedTypes = 0x0100, + + /// + /// Specifies all public properties. + /// + PublicProperties = 0x0200, + + /// + /// Specifies all non-public properties. + /// + NonPublicProperties = 0x0400, + + /// + /// Specifies all public events. + /// + PublicEvents = 0x0800, + + /// + /// Specifies all non-public events. + /// + NonPublicEvents = 0x1000, + + /// + /// Specifies all interfaces implemented by the type. + /// + Interfaces = 0x2000, + + /// + /// Specifies all members. + /// + All = ~None + } +#endif +} diff --git a/dotnet/src/webdriver/WebDriver.csproj b/dotnet/src/webdriver/WebDriver.csproj index e9acf442d70c1..01fca78221941 100644 --- a/dotnet/src/webdriver/WebDriver.csproj +++ b/dotnet/src/webdriver/WebDriver.csproj @@ -42,6 +42,11 @@ true + + + From 84714f328942fdcc0ba380628155815554b78719 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Wed, 23 Oct 2024 11:03:09 -0400 Subject: [PATCH 02/11] Make DomainType struct readonly --- dotnet/src/webdriver/DevTools/DevToolsDomains.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotnet/src/webdriver/DevTools/DevToolsDomains.cs b/dotnet/src/webdriver/DevTools/DevToolsDomains.cs index 12e87aff5303f..e01d3ca07f9ff 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsDomains.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsDomains.cs @@ -43,10 +43,10 @@ public abstract class DevToolsDomains }; /// Workaround for trimming. - struct DomainType + private readonly struct DomainType { [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] - public Type Type; + public readonly Type Type; public DomainType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type t) => Type = t; } From 59baae44cc62093a7a8aa6fe6fe8562ab68652e5 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 17 Nov 2024 15:58:00 -0500 Subject: [PATCH 03/11] center preprocessor directives on .net 8+ --- dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs | 2 +- dotnet/src/webdriver/Internal/TrimmingAttributes.cs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs b/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs index 99d35bc001851..44d2c435b51cf 100644 --- a/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs +++ b/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs @@ -36,7 +36,7 @@ internal class JsonEnumMemberConverter<[DynamicallyAccessedMembers(DynamicallyAc public JsonEnumMemberConverter() { var type = typeof(TEnum); -#if NET5_0_OR_GREATER +#if NET8_0_OR_GREATER TEnum[] values = Enum.GetValues(); #else Array values = Enum.GetValues(type); diff --git a/dotnet/src/webdriver/Internal/TrimmingAttributes.cs b/dotnet/src/webdriver/Internal/TrimmingAttributes.cs index 7d20d0f6e3ca2..db74656bca993 100644 --- a/dotnet/src/webdriver/Internal/TrimmingAttributes.cs +++ b/dotnet/src/webdriver/Internal/TrimmingAttributes.cs @@ -4,7 +4,7 @@ namespace System.Diagnostics.CodeAnalysis { -#if !NET7_0_OR_GREATER +#if !NET8_0_OR_GREATER /// /// Indicates that the specified method requires the ability to generate new code at runtime, /// for example through . @@ -38,9 +38,7 @@ public RequiresDynamicCodeAttribute(string message) /// public string? Url { get; set; } } -#endif -#if !NET5_0_OR_GREATER /// /// Indicates that the specified method requires dynamic access to code that is not referenced /// statically, for example through . From b4564f989e79590305e0051b2123d0fc92452585 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 17 Nov 2024 16:30:20 -0500 Subject: [PATCH 04/11] Add file header to `TrimmingAttributes.cs` --- .../webdriver/Internal/TrimmingAttributes.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dotnet/src/webdriver/Internal/TrimmingAttributes.cs b/dotnet/src/webdriver/Internal/TrimmingAttributes.cs index db74656bca993..a7b73918c0903 100644 --- a/dotnet/src/webdriver/Internal/TrimmingAttributes.cs +++ b/dotnet/src/webdriver/Internal/TrimmingAttributes.cs @@ -1,3 +1,22 @@ +// +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + #nullable enable // As per guidance in https://devblogs.microsoft.com/dotnet/creating-aot-compatible-libraries/#targetframeworks From a5c2e090db03330656fcafa4335d615e269226e4 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 17 Nov 2024 16:44:20 -0500 Subject: [PATCH 05/11] Tweak #if location --- dotnet/src/webdriver/Internal/TrimmingAttributes.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dotnet/src/webdriver/Internal/TrimmingAttributes.cs b/dotnet/src/webdriver/Internal/TrimmingAttributes.cs index a7b73918c0903..ed2cca3111998 100644 --- a/dotnet/src/webdriver/Internal/TrimmingAttributes.cs +++ b/dotnet/src/webdriver/Internal/TrimmingAttributes.cs @@ -21,9 +21,10 @@ // As per guidance in https://devblogs.microsoft.com/dotnet/creating-aot-compatible-libraries/#targetframeworks +#if !NET8_0_OR_GREATER + namespace System.Diagnostics.CodeAnalysis { -#if !NET8_0_OR_GREATER /// /// Indicates that the specified method requires the ability to generate new code at runtime, /// for example through . @@ -433,5 +434,6 @@ internal enum DynamicallyAccessedMemberTypes /// All = ~None } -#endif } + +#endif From 26e327f9a5d606a92ca13cedec114449b3e02332 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Mon, 18 Nov 2024 14:01:46 -0500 Subject: [PATCH 06/11] Hide `Assembly.CodeBase` from .NET 8 compilation --- dotnet/src/webdriver/Internal/FileUtilities.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dotnet/src/webdriver/Internal/FileUtilities.cs b/dotnet/src/webdriver/Internal/FileUtilities.cs index 0d83a6927aeca..443471db366ba 100644 --- a/dotnet/src/webdriver/Internal/FileUtilities.cs +++ b/dotnet/src/webdriver/Internal/FileUtilities.cs @@ -186,12 +186,14 @@ public static string GetCurrentDirectory() string currentDirectory = location; +#if !NET8_0_OR_GREATER // If we're shadow copying, get the directory from the codebase instead if (AppDomain.CurrentDomain.ShadowCopyFiles) { Uri uri = new Uri(executingAssembly.CodeBase); currentDirectory = Path.GetDirectoryName(uri.LocalPath); } +#endif return currentDirectory; } From 225a030ea3c042cd7d8113595400a12cf9e97dcd Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 8 Dec 2024 00:39:09 -0500 Subject: [PATCH 07/11] Remove changes from `DevToolsDomains` --- .../src/webdriver/DevTools/DevToolsDomains.cs | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/dotnet/src/webdriver/DevTools/DevToolsDomains.cs b/dotnet/src/webdriver/DevTools/DevToolsDomains.cs index 01b846897c386..8b9945364c465 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsDomains.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsDomains.cs @@ -36,22 +36,14 @@ public abstract class DevToolsDomains // This is the list of known supported DevTools version implementation. // When new versions are implemented for support, new types must be // added to this dictionary. - private static readonly Dictionary SupportedDevToolsVersions = new Dictionary() + private static readonly Dictionary SupportedDevToolsVersions = new Dictionary() { - { 130, new DomainType(typeof(V130.V130Domains)) }, - { 129, new DomainType(typeof(V129.V129Domains)) }, - { 128, new DomainType(typeof(V128.V128Domains)) }, - { 85, new DomainType(typeof(V85.V85Domains)) } + { 130, typeof(V130.V130Domains) }, + { 129, typeof(V129.V129Domains) }, + { 128, typeof(V128.V128Domains) }, + { 85, typeof(V85.V85Domains) } }; - /// Workaround for trimming. - private readonly struct DomainType - { - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] - public readonly Type Type; - - public DomainType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type t) => Type = t; - } /// /// Gets the version-specific domains for the DevTools session. This value must be cast to a version specific type to be at all useful. /// @@ -117,9 +109,9 @@ public static DevToolsDomains InitializeDomains(int protocolVersion, DevToolsSes private static Type MatchDomainsVersion(int desiredVersion, int versionRange) { // Return fast on an exact match - if (SupportedDevToolsVersions.TryGetValue(desiredVersion, out DomainType type)) + if (SupportedDevToolsVersions.ContainsKey(desiredVersion)) { - return type.Type; + return SupportedDevToolsVersions[desiredVersion]; } // Get the list of supported versions and sort descending @@ -133,7 +125,7 @@ private static Type MatchDomainsVersion(int desiredVersion, int versionRange) // (that is, closest without going over). if (desiredVersion >= supportedVersion && desiredVersion - supportedVersion < versionRange) { - return SupportedDevToolsVersions[supportedVersion].Type; + return SupportedDevToolsVersions[supportedVersion]; } } From 1440e2261037878a1252ea6a5865b253d365afb7 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 8 Dec 2024 00:39:36 -0500 Subject: [PATCH 08/11] remove remaining changes from `DevToolsDomains` --- dotnet/src/webdriver/DevTools/DevToolsDomains.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/dotnet/src/webdriver/DevTools/DevToolsDomains.cs b/dotnet/src/webdriver/DevTools/DevToolsDomains.cs index 8b9945364c465..53631416e161c 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsDomains.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsDomains.cs @@ -19,7 +19,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace OpenQA.Selenium.DevTools @@ -105,7 +104,6 @@ public static DevToolsDomains InitializeDomains(int protocolVersion, DevToolsSes return domains; } - [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] private static Type MatchDomainsVersion(int desiredVersion, int versionRange) { // Return fast on an exact match From f1fce52459efc06da11b493f38b1bd65f21eb15d Mon Sep 17 00:00:00 2001 From: Michael Render Date: Fri, 31 Jan 2025 17:02:57 -0500 Subject: [PATCH 09/11] Annotate CDP as AOT-unsafe --- dotnet/src/webdriver/DevTools/DevToolsSession.cs | 7 +++++++ dotnet/src/webdriver/DevTools/IDevToolsSession.cs | 5 +++++ .../src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/DevTools/DevToolsSession.cs b/dotnet/src/webdriver/DevTools/DevToolsSession.cs index a11f4757776a3..0713e8eb11fdf 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsSession.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsSession.cs @@ -20,6 +20,7 @@ using OpenQA.Selenium.Internal.Logging; using System; using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Net.Http; using System.Text.Json; @@ -148,6 +149,8 @@ public T GetVersionSpecificDomains() where T : DevToolsSessionDomains /// The execution timeout of the command in milliseconds. /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. + [RequiresUnreferencedCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] + [RequiresDynamicCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] public async Task> SendCommand(TCommand command, CancellationToken cancellationToken = default(CancellationToken), int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) where TCommand : ICommand { @@ -181,6 +184,8 @@ public T GetVersionSpecificDomains() where T : DevToolsSessionDomains /// The execution timeout of the command in milliseconds. /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. + [RequiresUnreferencedCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] + [RequiresDynamicCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] public async Task> SendCommand(TCommand command, string sessionId, CancellationToken cancellationToken = default(CancellationToken), int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) where TCommand : ICommand { @@ -214,6 +219,8 @@ public T GetVersionSpecificDomains() where T : DevToolsSessionDomains /// The execution timeout of the command in milliseconds. /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. + [RequiresUnreferencedCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] + [RequiresDynamicCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] public async Task SendCommand(TCommand command, CancellationToken cancellationToken = default(CancellationToken), int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) where TCommand : ICommand where TCommandResponse : ICommandResponse diff --git a/dotnet/src/webdriver/DevTools/IDevToolsSession.cs b/dotnet/src/webdriver/DevTools/IDevToolsSession.cs index 3837d38485523..c8121e899ce73 100644 --- a/dotnet/src/webdriver/DevTools/IDevToolsSession.cs +++ b/dotnet/src/webdriver/DevTools/IDevToolsSession.cs @@ -18,6 +18,7 @@ // using System; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Nodes; using System.Threading; @@ -59,6 +60,8 @@ public interface IDevToolsSession : IDisposable /// The execution timeout of the command in milliseconds. /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. + [RequiresUnreferencedCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] + [RequiresDynamicCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] Task> SendCommand(TCommand command, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived) where TCommand : ICommand; @@ -72,6 +75,8 @@ Task> SendCommand(TCommand command, Cancell /// The execution timeout of the command in milliseconds. /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. + [RequiresUnreferencedCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] + [RequiresDynamicCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] Task SendCommand(TCommand command, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived) where TCommand : ICommand where TCommandResponse : ICommandResponse; diff --git a/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs b/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs index b91197c062fd5..c3d10f1fac641 100644 --- a/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs +++ b/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs @@ -27,7 +27,7 @@ namespace OpenQA.Selenium.DevTools.Json { - internal sealed class JsonEnumMemberConverter : JsonConverter + internal sealed class JsonEnumMemberConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum> : JsonConverter where TEnum : struct, Enum { private readonly Dictionary _enumToString = new Dictionary(); From 10929628667906ed00fe252c44ab5581c0860d9e Mon Sep 17 00:00:00 2001 From: Michael Render Date: Fri, 31 Jan 2025 18:06:30 -0500 Subject: [PATCH 10/11] Annotate CDP as fully AOT-unsafe --- dotnet/src/webdriver/Chromium/ChromiumDriver.cs | 7 +++++++ dotnet/src/webdriver/DevTools/DevToolsSession.cs | 8 ++------ dotnet/src/webdriver/DevTools/IDevTools.cs | 9 +++++++++ dotnet/src/webdriver/DevTools/IDevToolsSession.cs | 10 ++++++---- dotnet/src/webdriver/INetwork.cs | 5 +++++ dotnet/src/webdriver/JavaScriptEngine.cs | 3 +++ dotnet/src/webdriver/NetworkManager.cs | 7 +++++++ dotnet/src/webdriver/Remote/RemoteWebDriver.cs | 9 +++++++++ dotnet/src/webdriver/Response.cs | 4 ++++ 9 files changed, 52 insertions(+), 10 deletions(-) diff --git a/dotnet/src/webdriver/Chromium/ChromiumDriver.cs b/dotnet/src/webdriver/Chromium/ChromiumDriver.cs index 489109d34f2d4..e17063b7f6bad 100644 --- a/dotnet/src/webdriver/Chromium/ChromiumDriver.cs +++ b/dotnet/src/webdriver/Chromium/ChromiumDriver.cs @@ -22,6 +22,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading.Tasks; @@ -278,6 +279,8 @@ public object ExecuteCdpCommand(string commandName, Dictionary c /// Creates a session to communicate with a browser using the Chromium Developer Tools debugging protocol. /// /// The active session to use to communicate with the Chromium Developer Tools debugging protocol. + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] public DevToolsSession GetDevToolsSession() { return GetDevToolsSession(new DevToolsOptions() { ProtocolVersion = DevToolsSession.AutoDetectDevToolsProtocolVersion }); @@ -287,6 +290,8 @@ public DevToolsSession GetDevToolsSession() /// Creates a session to communicate with a browser using the Chromium Developer Tools debugging protocol. /// /// The active session to use to communicate with the Chromium Developer Tools debugging protocol. + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] public DevToolsSession GetDevToolsSession(DevToolsOptions options) { if (this.devToolsSession == null) @@ -329,6 +334,8 @@ public DevToolsSession GetDevToolsSession(DevToolsOptions options) /// The version of the Chromium Developer Tools protocol to use. Defaults to autodetect the protocol version. /// The active session to use to communicate with the Chromium Developer Tools debugging protocol. [Obsolete("Use GetDevToolsSession(DevToolsOptions options)")] + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] public DevToolsSession GetDevToolsSession(int devToolsProtocolVersion) { return GetDevToolsSession(new DevToolsOptions() { ProtocolVersion = devToolsProtocolVersion }); diff --git a/dotnet/src/webdriver/DevTools/DevToolsSession.cs b/dotnet/src/webdriver/DevTools/DevToolsSession.cs index 0713e8eb11fdf..e2910e0fe8534 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsSession.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsSession.cs @@ -34,6 +34,8 @@ namespace OpenQA.Selenium.DevTools /// Represents a WebSocket connection to a running DevTools instance that can be used to send /// commands and recieve events. /// + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] public class DevToolsSession : IDevToolsSession { /// @@ -149,8 +151,6 @@ public T GetVersionSpecificDomains() where T : DevToolsSessionDomains /// The execution timeout of the command in milliseconds. /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. - [RequiresUnreferencedCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] - [RequiresDynamicCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] public async Task> SendCommand(TCommand command, CancellationToken cancellationToken = default(CancellationToken), int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) where TCommand : ICommand { @@ -184,8 +184,6 @@ public T GetVersionSpecificDomains() where T : DevToolsSessionDomains /// The execution timeout of the command in milliseconds. /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. - [RequiresUnreferencedCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] - [RequiresDynamicCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] public async Task> SendCommand(TCommand command, string sessionId, CancellationToken cancellationToken = default(CancellationToken), int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) where TCommand : ICommand { @@ -219,8 +217,6 @@ public T GetVersionSpecificDomains() where T : DevToolsSessionDomains /// The execution timeout of the command in milliseconds. /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. - [RequiresUnreferencedCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] - [RequiresDynamicCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] public async Task SendCommand(TCommand command, CancellationToken cancellationToken = default(CancellationToken), int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) where TCommand : ICommand where TCommandResponse : ICommandResponse diff --git a/dotnet/src/webdriver/DevTools/IDevTools.cs b/dotnet/src/webdriver/DevTools/IDevTools.cs index b720b5d1e9222..8f90d18d0267d 100644 --- a/dotnet/src/webdriver/DevTools/IDevTools.cs +++ b/dotnet/src/webdriver/DevTools/IDevTools.cs @@ -18,6 +18,7 @@ // using System; +using System.Diagnostics.CodeAnalysis; namespace OpenQA.Selenium.DevTools { @@ -35,6 +36,8 @@ public interface IDevTools /// Creates a session to communicate with a browser using a Developer Tools debugging protocol. /// /// The active session to use to communicate with the Developer Tools debugging protocol. + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] DevToolsSession GetDevToolsSession(); /// @@ -42,6 +45,8 @@ public interface IDevTools /// /// The options for the DevToolsSession to use. /// The active session to use to communicate with the Developer Tools debugging protocol. + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] DevToolsSession GetDevToolsSession(DevToolsOptions options); /// @@ -50,11 +55,15 @@ public interface IDevTools /// The specific version of the Developer Tools debugging protocol to use. /// The active session to use to communicate with the Developer Tools debugging protocol. [Obsolete("Use GetDevToolsSession(DevToolsOptions options)")] + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] DevToolsSession GetDevToolsSession(int protocolVersion); /// /// Closes a DevTools session /// + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] void CloseDevToolsSession(); } } diff --git a/dotnet/src/webdriver/DevTools/IDevToolsSession.cs b/dotnet/src/webdriver/DevTools/IDevToolsSession.cs index c8121e899ce73..d30dca3b4fddb 100644 --- a/dotnet/src/webdriver/DevTools/IDevToolsSession.cs +++ b/dotnet/src/webdriver/DevTools/IDevToolsSession.cs @@ -60,8 +60,8 @@ public interface IDevToolsSession : IDisposable /// The execution timeout of the command in milliseconds. /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. - [RequiresUnreferencedCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] - [RequiresDynamicCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] Task> SendCommand(TCommand command, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived) where TCommand : ICommand; @@ -75,8 +75,8 @@ Task> SendCommand(TCommand command, Cancell /// The execution timeout of the command in milliseconds. /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. - [RequiresUnreferencedCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] - [RequiresDynamicCode("SendCommand is not compatible with trimming or AOT. Use the overload that takes JsonNode parameters instead")] + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] Task SendCommand(TCommand command, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived) where TCommand : ICommand where TCommandResponse : ICommandResponse; @@ -90,6 +90,8 @@ Task SendCommand(TCommand command, /// The execution timeout of the command in milliseconds. /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] Task SendCommand(string commandName, JsonNode @params, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived); } } diff --git a/dotnet/src/webdriver/INetwork.cs b/dotnet/src/webdriver/INetwork.cs index 34e6fa3a6b9f5..e01cb961d3c6e 100644 --- a/dotnet/src/webdriver/INetwork.cs +++ b/dotnet/src/webdriver/INetwork.cs @@ -18,6 +18,7 @@ // using System; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace OpenQA.Selenium @@ -77,12 +78,16 @@ public interface INetwork /// Asynchronously starts monitoring for network traffic. /// /// A task that represents the asynchronous operation. + [RequiresUnreferencedCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] + [RequiresDynamicCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] Task StartMonitoring(); /// /// Asynchronously stops monitoring for network traffic. /// /// A task that represents the asynchronous operation. + [RequiresUnreferencedCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] + [RequiresDynamicCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] Task StopMonitoring(); } } diff --git a/dotnet/src/webdriver/JavaScriptEngine.cs b/dotnet/src/webdriver/JavaScriptEngine.cs index 4bd4fe4676eb4..baba25b506b07 100644 --- a/dotnet/src/webdriver/JavaScriptEngine.cs +++ b/dotnet/src/webdriver/JavaScriptEngine.cs @@ -21,6 +21,7 @@ using OpenQA.Selenium.Internal; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; @@ -32,6 +33,8 @@ namespace OpenQA.Selenium /// /// Provides methods allowing the user to manage settings in the browser's JavaScript engine. /// + [RequiresUnreferencedCode("JavaScriptEngine is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] + [RequiresDynamicCode("JavaScriptEngine is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] public class JavaScriptEngine : IJavaScriptEngine { private readonly string MonitorBindingName = "__webdriver_attribute"; diff --git a/dotnet/src/webdriver/NetworkManager.cs b/dotnet/src/webdriver/NetworkManager.cs index b508c32140ca0..a395a16dc230d 100644 --- a/dotnet/src/webdriver/NetworkManager.cs +++ b/dotnet/src/webdriver/NetworkManager.cs @@ -20,6 +20,7 @@ using OpenQA.Selenium.DevTools; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace OpenQA.Selenium @@ -38,6 +39,8 @@ public class NetworkManager : INetwork /// Initializes a new instance of the class. /// /// The instance on which the network should be monitored. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Warnings are added to StartMonitoring and StopMonitoring")] + [UnconditionalSuppressMessage("Trimming", "IL3050", Justification = "Warnings are added to StartMonitoring and StopMonitoring")] public NetworkManager(IWebDriver driver) { // Use of Lazy means this exception won't be thrown until the user first @@ -69,6 +72,8 @@ public NetworkManager(IWebDriver driver) /// Asynchronously starts monitoring for network traffic. /// /// A task that represents the asynchronous operation. + [RequiresUnreferencedCode("NetworkManager is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] + [RequiresDynamicCode("NetworkManager is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] public async Task StartMonitoring() { this.session.Value.Domains.Network.RequestPaused += OnRequestPaused; @@ -83,6 +88,8 @@ public async Task StartMonitoring() /// Asynchronously stops monitoring for network traffic. /// /// A task that represents the asynchronous operation. + [RequiresUnreferencedCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] + [RequiresDynamicCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] public async Task StopMonitoring() { this.session.Value.Domains.Network.ResponsePaused -= OnResponsePaused; diff --git a/dotnet/src/webdriver/Remote/RemoteWebDriver.cs b/dotnet/src/webdriver/Remote/RemoteWebDriver.cs index 0950c84949ff9..8472e339fc205 100644 --- a/dotnet/src/webdriver/Remote/RemoteWebDriver.cs +++ b/dotnet/src/webdriver/Remote/RemoteWebDriver.cs @@ -26,6 +26,7 @@ using System.IO.Compression; using System.Linq; using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; namespace OpenQA.Selenium.Remote { @@ -426,6 +427,8 @@ public ReadOnlyCollection FindElementsByCssSelector(string cssSelec /// Creates a session to communicate with a browser using a Developer Tools debugging protocol. /// /// The active session to use to communicate with the Developer Tools debugging protocol. + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] public DevToolsSession GetDevToolsSession() { if (this.Capabilities.GetCapability(CapabilityType.BrowserName) is "firefox") @@ -443,6 +446,8 @@ public DevToolsSession GetDevToolsSession() /// Creates a session to communicate with a browser using a Developer Tools debugging protocol. /// /// The active session to use to communicate with the Developer Tools debugging protocol. + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] public DevToolsSession GetDevToolsSession(DevToolsOptions options) { if (options is null) @@ -498,6 +503,8 @@ public DevToolsSession GetDevToolsSession(DevToolsOptions options) /// The specific version of the Developer Tools debugging protocol to use. /// The active session to use to communicate with the Developer Tools debugging protocol. [Obsolete("Use GetDevToolsSession(DevToolsOptions options)")] + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] public DevToolsSession GetDevToolsSession(int protocolVersion) { return GetDevToolsSession(new DevToolsOptions() { ProtocolVersion = protocolVersion }); @@ -576,6 +583,8 @@ public void DeleteDownloadableFiles() /// /// Closes a DevTools session. /// + [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] + [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] public void CloseDevToolsSession() { if (this.devToolsSession != null) diff --git a/dotnet/src/webdriver/Response.cs b/dotnet/src/webdriver/Response.cs index c6ac73328b1d7..bf5445285bd4d 100644 --- a/dotnet/src/webdriver/Response.cs +++ b/dotnet/src/webdriver/Response.cs @@ -20,6 +20,7 @@ using OpenQA.Selenium.Internal; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; @@ -210,6 +211,9 @@ public static Response FromErrorJson(string value) /// Returns this object as a JSON-encoded string. /// /// A JSON-encoded string representing this object. + + [RequiresUnreferencedCode("Free-form JSON serialization.")] + [RequiresDynamicCode("Free-form JSON serialization.")] public string ToJson() { return JsonSerializer.Serialize(this); From 68339cd3e6ac4c1e9258590502a2274fdc37d342 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Fri, 28 Feb 2025 13:42:13 -0500 Subject: [PATCH 11/11] Improve AOT incompatibility message --- dotnet/src/webdriver/Chromium/ChromiumDriver.cs | 12 ++++++------ dotnet/src/webdriver/DevTools/DevToolsSession.cs | 6 ++++-- dotnet/src/webdriver/DevTools/IDevTools.cs | 16 ++++++++-------- .../src/webdriver/DevTools/IDevToolsSession.cs | 12 ++++++------ dotnet/src/webdriver/Remote/RemoteWebDriver.cs | 16 ++++++++-------- dotnet/src/webdriver/Response.cs | 4 ++-- 6 files changed, 34 insertions(+), 32 deletions(-) diff --git a/dotnet/src/webdriver/Chromium/ChromiumDriver.cs b/dotnet/src/webdriver/Chromium/ChromiumDriver.cs index 43c852f4281b1..d6fbebd953d84 100644 --- a/dotnet/src/webdriver/Chromium/ChromiumDriver.cs +++ b/dotnet/src/webdriver/Chromium/ChromiumDriver.cs @@ -299,8 +299,8 @@ public void SetPermission(string permissionName, string permissionValue) /// Creates a session to communicate with a browser using the Chromium Developer Tools debugging protocol. /// /// The active session to use to communicate with the Chromium Developer Tools debugging protocol. - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] public DevToolsSession GetDevToolsSession() { return GetDevToolsSession(new DevToolsOptions() { ProtocolVersion = DevToolsSession.AutoDetectDevToolsProtocolVersion }); @@ -310,8 +310,8 @@ public DevToolsSession GetDevToolsSession() /// Creates a session to communicate with a browser using the Chromium Developer Tools debugging protocol. /// /// The active session to use to communicate with the Chromium Developer Tools debugging protocol. - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] public DevToolsSession GetDevToolsSession(DevToolsOptions options) { if (this.devToolsSession == null) @@ -353,8 +353,8 @@ public DevToolsSession GetDevToolsSession(DevToolsOptions options) /// The version of the Chromium Developer Tools protocol to use. Defaults to autodetect the protocol version. /// The active session to use to communicate with the Chromium Developer Tools debugging protocol. [Obsolete("Use GetDevToolsSession(DevToolsOptions options)")] - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] public DevToolsSession GetDevToolsSession(int devToolsProtocolVersion) { return GetDevToolsSession(new DevToolsOptions() { ProtocolVersion = devToolsProtocolVersion }); diff --git a/dotnet/src/webdriver/DevTools/DevToolsSession.cs b/dotnet/src/webdriver/DevTools/DevToolsSession.cs index 4bd56cc9855ff..28d9b9def942f 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsSession.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsSession.cs @@ -36,10 +36,12 @@ namespace OpenQA.Selenium.DevTools /// Represents a WebSocket connection to a running DevTools instance that can be used to send /// commands and receive events. /// - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(CDP_AOTIncompatibilityMessage)] public class DevToolsSession : IDevToolsSession { + internal const string CDP_AOTIncompatibilityMessage = "CDP is not compatible with trimming or AOT."; + /// /// A value indicating that the version of the DevTools protocol in use /// by the browser should be automatically detected. diff --git a/dotnet/src/webdriver/DevTools/IDevTools.cs b/dotnet/src/webdriver/DevTools/IDevTools.cs index a7cccd31cf712..4c256730e1ff8 100644 --- a/dotnet/src/webdriver/DevTools/IDevTools.cs +++ b/dotnet/src/webdriver/DevTools/IDevTools.cs @@ -38,8 +38,8 @@ public interface IDevTools /// Creates a session to communicate with a browser using a Developer Tools debugging protocol. /// /// The active session to use to communicate with the Developer Tools debugging protocol. - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] DevToolsSession GetDevToolsSession(); /// @@ -48,8 +48,8 @@ public interface IDevTools /// The options for the DevToolsSession to use. /// The active session to use to communicate with the Developer Tools debugging protocol. /// If is . - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] DevToolsSession GetDevToolsSession(DevToolsOptions options); /// @@ -58,15 +58,15 @@ public interface IDevTools /// The specific version of the Developer Tools debugging protocol to use. /// The active session to use to communicate with the Developer Tools debugging protocol. [Obsolete("Use GetDevToolsSession(DevToolsOptions options)")] - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] DevToolsSession GetDevToolsSession(int protocolVersion); /// /// Closes a DevTools session /// - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] void CloseDevToolsSession(); } } diff --git a/dotnet/src/webdriver/DevTools/IDevToolsSession.cs b/dotnet/src/webdriver/DevTools/IDevToolsSession.cs index 80d3e4a207605..705ef72649e55 100644 --- a/dotnet/src/webdriver/DevTools/IDevToolsSession.cs +++ b/dotnet/src/webdriver/DevTools/IDevToolsSession.cs @@ -64,8 +64,8 @@ public interface IDevToolsSession : IDisposable /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. /// If is . - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] Task?> SendCommand(TCommand command, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived) where TCommand : ICommand; @@ -80,8 +80,8 @@ public interface IDevToolsSession : IDisposable /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. /// If is . - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] Task SendCommand(TCommand command, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived) where TCommand : ICommand where TCommandResponse : ICommandResponse; @@ -96,8 +96,8 @@ public interface IDevToolsSession : IDisposable /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. /// If is . - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] Task SendCommand(string commandName, JsonNode @params, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived); } } diff --git a/dotnet/src/webdriver/Remote/RemoteWebDriver.cs b/dotnet/src/webdriver/Remote/RemoteWebDriver.cs index e9d3434958c0d..4d46ac8df39f9 100644 --- a/dotnet/src/webdriver/Remote/RemoteWebDriver.cs +++ b/dotnet/src/webdriver/Remote/RemoteWebDriver.cs @@ -427,8 +427,8 @@ public ReadOnlyCollection FindElementsByCssSelector(string cssSelec /// Creates a session to communicate with a browser using a Developer Tools debugging protocol. /// /// The active session to use to communicate with the Developer Tools debugging protocol. - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] public DevToolsSession GetDevToolsSession() { if (this.Capabilities.GetCapability(CapabilityType.BrowserName) is "firefox") @@ -446,8 +446,8 @@ public DevToolsSession GetDevToolsSession() /// Creates a session to communicate with a browser using a Developer Tools debugging protocol. /// /// The active session to use to communicate with the Developer Tools debugging protocol. - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] public DevToolsSession GetDevToolsSession(DevToolsOptions options) { if (options is null) @@ -504,8 +504,8 @@ public DevToolsSession GetDevToolsSession(DevToolsOptions options) /// The specific version of the Developer Tools debugging protocol to use. /// The active session to use to communicate with the Developer Tools debugging protocol. [Obsolete("Use GetDevToolsSession(DevToolsOptions options)")] - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] public DevToolsSession GetDevToolsSession(int protocolVersion) { return GetDevToolsSession(new DevToolsOptions() { ProtocolVersion = protocolVersion }); @@ -594,8 +594,8 @@ public void DeleteDownloadableFiles() /// /// Closes a DevTools session. /// - [RequiresUnreferencedCode("CDP is not compatible with trimming or AOT.")] - [RequiresDynamicCode("CDP is not compatible with trimming or AOT.")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] public void CloseDevToolsSession() { if (this.devToolsSession != null) diff --git a/dotnet/src/webdriver/Response.cs b/dotnet/src/webdriver/Response.cs index 8aa6658bc2931..8ee70c76250b3 100644 --- a/dotnet/src/webdriver/Response.cs +++ b/dotnet/src/webdriver/Response.cs @@ -173,8 +173,8 @@ public static Response FromErrorJson(string value) /// /// A JSON-encoded string representing this object. - [RequiresUnreferencedCode("Free-form JSON serialization.")] - [RequiresDynamicCode("Free-form JSON serialization.")] + [RequiresUnreferencedCode("Untyped JSON serialization is not trim- or AOT- safe.")] + [RequiresDynamicCode("Untyped JSON serialization is not trim- or AOT- safe.")] public string ToJson() { return JsonSerializer.Serialize(this);