Skip to content

Commit b142f52

Browse files
committed
[dotnet] Fully annotate Command for AOT support
1 parent 014d1c3 commit b142f52

File tree

2 files changed

+53
-19
lines changed

2 files changed

+53
-19
lines changed

dotnet/src/webdriver/Command.cs

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using OpenQA.Selenium.Internal;
2121
using System;
2222
using System.Collections.Generic;
23+
using System.Diagnostics.CodeAnalysis;
2324
using System.Text.Json;
2425
using System.Text.Json.Serialization;
2526
using System.Text.Json.Serialization.Metadata;
@@ -31,24 +32,33 @@ namespace OpenQA.Selenium
3132
/// </summary>
3233
public class Command
3334
{
34-
private readonly static JsonSerializerOptions s_jsonSerializerOptions = new()
35+
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = $"All trimming-unsafe access points to {nameof(JsonSerializerOptions)} are annotated as such")]
36+
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = $"All AOT-unsafe access points to {nameof(JsonSerializerOptions)} are annotated as such")]
37+
private static class JsonOptionsHolder
3538
{
36-
TypeInfoResolverChain =
39+
public readonly static JsonSerializerOptions JsonSerializerOptions = new()
3740
{
38-
CommandJsonSerializerContext.Default,
39-
new DefaultJsonTypeInfoResolver()
40-
},
41-
Converters = { new ResponseValueJsonConverter() }
42-
};
41+
TypeInfoResolverChain =
42+
{
43+
CommandJsonSerializerContext.Default,
44+
new DefaultJsonTypeInfoResolver()
45+
},
46+
Converters = { new ResponseValueJsonConverter() }
47+
};
48+
}
49+
50+
private readonly Dictionary<string, object?> _parameters;
4351

4452
/// <summary>
4553
/// Initializes a new instance of the <see cref="Command"/> class using a command name and a JSON-encoded string for the parameters.
4654
/// </summary>
4755
/// <param name="name">Name of the command</param>
4856
/// <param name="jsonParameters">Parameters for the command as a JSON-encoded string.</param>
4957
public Command(string name, string jsonParameters)
50-
: this(null, name, ConvertParametersFromJson(jsonParameters))
5158
{
59+
this.SessionId = null;
60+
this._parameters = ConvertParametersFromJson(jsonParameters) ?? new Dictionary<string, object?>();
61+
this.Name = name ?? throw new ArgumentNullException(nameof(name));
5262
}
5363

5464
/// <summary>
@@ -58,10 +68,12 @@ public Command(string name, string jsonParameters)
5868
/// <param name="name">Name of the command</param>
5969
/// <param name="parameters">Parameters for that command</param>
6070
/// <exception cref="ArgumentNullException">If <paramref name="name"/> is <see langword="null"/>.</exception>
71+
[RequiresUnreferencedCode("Adding untyped parameter values for JSON serialization has best-effort AOT support. Ensure only Selenium types and well-known .NET types are added, or use the overload that takes pre-serialized string jsonParameters for guaranteed AOT compatibility.")]
72+
[RequiresDynamicCode("Adding untyped parameter values for JSON serialization has best-effort AOT support. Ensure only Selenium types and well-known .NET types are added, or use the overload that takes pre-serialized string jsonParameters for guaranteed AOT compatibility.")]
6173
public Command(SessionId? sessionId, string name, Dictionary<string, object?>? parameters)
6274
{
6375
this.SessionId = sessionId;
64-
this.Parameters = parameters ?? new Dictionary<string, object?>();
76+
this._parameters = parameters ?? new Dictionary<string, object?>();
6577
this.Name = name ?? throw new ArgumentNullException(nameof(name));
6678
}
6779

@@ -81,18 +93,25 @@ public Command(SessionId? sessionId, string name, Dictionary<string, object?>? p
8193
/// Gets the parameters of the command
8294
/// </summary>
8395
[JsonPropertyName("parameters")]
84-
public Dictionary<string, object?> Parameters { get; }
96+
public Dictionary<string, object?> Parameters
97+
{
98+
[RequiresUnreferencedCode("Adding untyped parameter values for JSON serialization has best-effort AOT support. Ensure only Selenium types and well-known .NET types are added.")]
99+
[RequiresDynamicCode("Adding untyped parameter values for JSON serialization has best-effort AOT support. Ensure only Selenium types and well-known .NET types are added.")]
100+
get => _parameters;
101+
}
85102

86103
/// <summary>
87104
/// Gets the parameters of the command as a JSON-encoded string.
88105
/// </summary>
89106
public string ParametersAsJsonString
90107
{
108+
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = $"All trimming-unsafe access points to {nameof(_parameters)} are annotated as such")]
109+
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = $"All AOT-unsafe access points to {nameof(_parameters)} are annotated as such")]
91110
get
92111
{
93-
if (this.Parameters != null && this.Parameters.Count > 0)
112+
if (HasParameters())
94113
{
95-
return JsonSerializer.Serialize(this.Parameters, s_jsonSerializerOptions);
114+
return JsonSerializer.Serialize(this._parameters, JsonOptionsHolder.JsonSerializerOptions);
96115
}
97116
else
98117
{
@@ -101,6 +120,25 @@ public string ParametersAsJsonString
101120
}
102121
}
103122

123+
internal bool HasParameters()
124+
{
125+
return this._parameters != null && this._parameters.Count > 0;
126+
}
127+
128+
internal bool TryGetValueAndRemoveIfNotNull(string key, [NotNullWhen(true)] out object? value)
129+
{
130+
if (this._parameters.TryGetValue(key, out value))
131+
{
132+
if (value is not null)
133+
{
134+
this._parameters.Remove(key);
135+
return true;
136+
}
137+
}
138+
139+
return false;
140+
}
141+
104142
/// <summary>
105143
/// Returns a string of the Command object
106144
/// </summary>

dotnet/src/webdriver/HttpCommandInfo.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,17 +118,13 @@ private static string GetCommandPropertyValue(string propertyName, Command comma
118118
propertyValue = commandToExecute.SessionId.ToString();
119119
}
120120
}
121-
else if (commandToExecute.Parameters != null && commandToExecute.Parameters.Count > 0)
121+
else if (commandToExecute.HasParameters())
122122
{
123123
// Extract the URL parameter, and remove it from the parameters dictionary
124124
// so it doesn't get transmitted as a JSON parameter.
125-
if (commandToExecute.Parameters.TryGetValue(propertyName, out var propertyValueObject))
125+
if (commandToExecute.TryGetValueAndRemoveIfNotNull(propertyName, out var propertyValueObject))
126126
{
127-
if (propertyValueObject != null)
128-
{
129-
propertyValue = propertyValueObject.ToString()!;
130-
commandToExecute.Parameters.Remove(propertyName);
131-
}
127+
propertyValue = propertyValueObject.ToString()!;
132128
}
133129
}
134130

0 commit comments

Comments
 (0)