Skip to content

Commit

Permalink
[dotnet] [bidi] Use JsonSerializerContext to be AOT friendly (#15162)
Browse files Browse the repository at this point in the history
  • Loading branch information
nvborisenko authored Jan 28, 2025
1 parent 0bf5e9e commit 8d0f6af
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 13 deletions.
2 changes: 2 additions & 0 deletions dotnet/src/webdriver/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ csharp_library(
framework("nuget", "Microsoft.Bcl.AsyncInterfaces"),
framework("nuget", "System.Threading.Tasks.Extensions"),
framework("nuget", "System.Memory"),
framework("nuget", "System.Runtime.CompilerServices.Unsafe"),
framework("nuget", "System.Text.Encodings.Web"),
framework("nuget", "System.Text.Json"),
],
Expand Down Expand Up @@ -120,6 +121,7 @@ csharp_library(
framework("nuget", "Microsoft.Bcl.AsyncInterfaces"),
framework("nuget", "System.Threading.Tasks.Extensions"),
framework("nuget", "System.Memory"),
framework("nuget", "System.Runtime.CompilerServices.Unsafe"),
framework("nuget", "System.Text.Encodings.Web"),
framework("nuget", "System.Text.Json"),
],
Expand Down
15 changes: 9 additions & 6 deletions dotnet/src/webdriver/BiDi/Communication/Broker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
// under the License.
// </copyright>

using OpenQA.Selenium.BiDi.Communication.Json;
using OpenQA.Selenium.BiDi.Communication.Json.Converters;
using OpenQA.Selenium.BiDi.Communication.Transport;
using OpenQA.Selenium.Internal.Logging;
Expand Down Expand Up @@ -53,14 +54,14 @@ public class Broker : IAsyncDisposable
private Task? _eventEmitterTask;
private CancellationTokenSource? _receiveMessagesCancellationTokenSource;

private readonly JsonSerializerOptions _jsonSerializerOptions;
private readonly BiDiJsonSerializerContext _jsonSerializerContext;

internal Broker(BiDi bidi, ITransport transport)
{
_bidi = bidi;
_transport = transport;

_jsonSerializerOptions = new JsonSerializerOptions
var jsonSerializerOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Expand Down Expand Up @@ -99,6 +100,8 @@ internal Broker(BiDi bidi, ITransport transport)
new Json.Converters.Enumerable.GetRealmsResultConverter(),
}
};

_jsonSerializerContext = new BiDiJsonSerializerContext(jsonSerializerOptions);
}

public async Task ConnectAsync(CancellationToken cancellationToken)
Expand All @@ -114,7 +117,7 @@ private async Task ReceiveMessagesAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var message = await _transport.ReceiveAsJsonAsync<Message>(_jsonSerializerOptions, cancellationToken);
var message = await _transport.ReceiveAsJsonAsync<Message>(_jsonSerializerContext, cancellationToken);

switch (message)
{
Expand Down Expand Up @@ -145,7 +148,7 @@ private async Task ProcessEventsAwaiterAsync()
{
foreach (var handler in eventHandlers.ToArray()) // copy handlers avoiding modified collection while iterating
{
var args = (EventArgs)result.Params.Deserialize(handler.EventArgsType, _jsonSerializerOptions)!;
var args = (EventArgs)result.Params.Deserialize(handler.EventArgsType, _jsonSerializerContext)!;

args.BiDi = _bidi;

Expand Down Expand Up @@ -177,7 +180,7 @@ public async Task<TResult> ExecuteCommandAsync<TResult>(Command command, Command
{
var result = await ExecuteCommandCoreAsync(command, options).ConfigureAwait(false);

return (TResult)((JsonElement)result).Deserialize(typeof(TResult), _jsonSerializerOptions)!;
return (TResult)((JsonElement)result).Deserialize(typeof(TResult), _jsonSerializerContext)!;
}

public async Task ExecuteCommandAsync(Command command, CommandOptions? options)
Expand All @@ -199,7 +202,7 @@ private async Task<object> ExecuteCommandCoreAsync(Command command, CommandOptio

_pendingCommands[command.Id] = tcs;

await _transport.SendAsJsonAsync(command, _jsonSerializerOptions, cts.Token).ConfigureAwait(false);
await _transport.SendAsJsonAsync(command, _jsonSerializerContext, cts.Token).ConfigureAwait(false);

return await tcs.Task.ConfigureAwait(false);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// <copyright file="BiDiJsonSerializerContext.cs" company="Selenium Committers">
// 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.
// </copyright>

using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace OpenQA.Selenium.BiDi.Communication.Json;

#region https://github.com/dotnet/runtime/issues/72604
[JsonSerializable(typeof(MessageSuccess))]
[JsonSerializable(typeof(MessageError))]
[JsonSerializable(typeof(MessageEvent))]

[JsonSerializable(typeof(Modules.Script.EvaluateResult.Success))]
[JsonSerializable(typeof(Modules.Script.EvaluateResult.Exception))]

[JsonSerializable(typeof(Modules.Script.RemoteValue.Number), TypeInfoPropertyName = "Script_RemoteValue_Number")]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Boolean), TypeInfoPropertyName = "Script_RemoteValue_Boolean")]
[JsonSerializable(typeof(Modules.Script.RemoteValue.String), TypeInfoPropertyName = "Script_RemoteValue_String")]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Null), TypeInfoPropertyName = "Script_RemoteValue_Null")]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Undefined), TypeInfoPropertyName = "Script_RemoteValue_Undefined")]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Symbol))]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Array), TypeInfoPropertyName = "Script_RemoteValue_Array")]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Object), TypeInfoPropertyName = "Script_RemoteValue_Object")]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Function))]
[JsonSerializable(typeof(Modules.Script.RemoteValue.RegExp), TypeInfoPropertyName = "Script_RemoteValue_RegExp")]
[JsonSerializable(typeof(Modules.Script.RemoteValue.RegExp.RegExpValue), TypeInfoPropertyName = "Script_RemoteValue_RegExp_RegExpValue")]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Date), TypeInfoPropertyName = "Script_RemoteValue_Date")]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Map), TypeInfoPropertyName = "Script_RemoteValue_Map")]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Set), TypeInfoPropertyName = "Script_RemoteValue_Set")]
[JsonSerializable(typeof(Modules.Script.RemoteValue.WeakMap))]
[JsonSerializable(typeof(Modules.Script.RemoteValue.WeakSet))]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Generator))]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Error))]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Proxy))]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Promise))]
[JsonSerializable(typeof(Modules.Script.RemoteValue.TypedArray))]
[JsonSerializable(typeof(Modules.Script.RemoteValue.ArrayBuffer))]
[JsonSerializable(typeof(Modules.Script.RemoteValue.NodeList))]
[JsonSerializable(typeof(Modules.Script.RemoteValue.HtmlCollection))]
[JsonSerializable(typeof(Modules.Script.RemoteValue.Node))]
[JsonSerializable(typeof(Modules.Script.RemoteValue.WindowProxy))]

[JsonSerializable(typeof(Modules.Script.RealmInfo.Window))]
[JsonSerializable(typeof(Modules.Script.RealmInfo.DedicatedWorker))]
[JsonSerializable(typeof(Modules.Script.RealmInfo.SharedWorker))]
[JsonSerializable(typeof(Modules.Script.RealmInfo.ServiceWorker))]
[JsonSerializable(typeof(Modules.Script.RealmInfo.Worker))]
[JsonSerializable(typeof(Modules.Script.RealmInfo.PaintWorklet))]
[JsonSerializable(typeof(Modules.Script.RealmInfo.AudioWorklet))]
[JsonSerializable(typeof(Modules.Script.RealmInfo.Worklet))]

[JsonSerializable(typeof(Modules.Log.Entry.Console))]
[JsonSerializable(typeof(Modules.Log.Entry.Javascript))]
#endregion

[JsonSerializable(typeof(Command))]
[JsonSerializable(typeof(Message))]

[JsonSerializable(typeof(Modules.Session.StatusResult))]
[JsonSerializable(typeof(Modules.Session.NewResult))]

[JsonSerializable(typeof(Modules.Browser.CloseCommand), TypeInfoPropertyName = "Browser_CloseCommand")]
[JsonSerializable(typeof(Modules.Browser.GetUserContextsResult))]
[JsonSerializable(typeof(IReadOnlyList<Modules.Browser.UserContextInfo>))]

[JsonSerializable(typeof(Modules.BrowsingContext.CloseCommand), TypeInfoPropertyName = "BrowsingContext_CloseCommand")]
[JsonSerializable(typeof(Modules.BrowsingContext.CreateResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.BrowsingContextInfo))]
[JsonSerializable(typeof(Modules.BrowsingContext.NavigateResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.NavigationInfo))]
[JsonSerializable(typeof(Modules.BrowsingContext.TraverseHistoryResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.LocateNodesResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.CaptureScreenshotResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.GetTreeResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.PrintResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.UserPromptOpenedEventArgs))]
[JsonSerializable(typeof(Modules.BrowsingContext.UserPromptClosedEventArgs))]
[JsonSerializable(typeof(Modules.BrowsingContext.Origin), TypeInfoPropertyName = "BrowsingContext_Origin")]

[JsonSerializable(typeof(Modules.Network.BytesValue.String), TypeInfoPropertyName = "Network_BytesValue_String")]
[JsonSerializable(typeof(Modules.Network.UrlPattern.String), TypeInfoPropertyName = "Network_UrlPattern_String")]
[JsonSerializable(typeof(Modules.Network.ContinueWithAuthParameters.Default), TypeInfoPropertyName = "Network_ContinueWithAuthParameters_Default")]
[JsonSerializable(typeof(Modules.Network.AddInterceptResult))]
[JsonSerializable(typeof(Modules.Network.BeforeRequestSentEventArgs))]
[JsonSerializable(typeof(Modules.Network.ResponseStartedEventArgs))]
[JsonSerializable(typeof(Modules.Network.ResponseCompletedEventArgs))]
[JsonSerializable(typeof(Modules.Network.FetchErrorEventArgs))]
[JsonSerializable(typeof(Modules.Network.AuthRequiredEventArgs))]

[JsonSerializable(typeof(Modules.Script.Channel), TypeInfoPropertyName = "Script_Channel")]
[JsonSerializable(typeof(Modules.Script.LocalValue.String), TypeInfoPropertyName = "Script_LocalValue_String")]
[JsonSerializable(typeof(Modules.Script.Target.Realm), TypeInfoPropertyName = "Script_Target_Realm")]
[JsonSerializable(typeof(Modules.Script.Target.Context), TypeInfoPropertyName = "Script_Target_Context")]
[JsonSerializable(typeof(Modules.Script.AddPreloadScriptResult))]
[JsonSerializable(typeof(Modules.Script.EvaluateResult))]
[JsonSerializable(typeof(Modules.Script.GetRealmsResult))]
[JsonSerializable(typeof(Modules.Script.MessageEventArgs))]
[JsonSerializable(typeof(Modules.Script.RealmDestroyedEventArgs))]
[JsonSerializable(typeof(IReadOnlyList<Modules.Script.RealmInfo>))]

[JsonSerializable(typeof(Modules.Log.Entry))]

[JsonSerializable(typeof(Modules.Storage.GetCookiesResult))]
[JsonSerializable(typeof(Modules.Storage.DeleteCookiesResult))]
[JsonSerializable(typeof(Modules.Storage.SetCookieResult))]

[JsonSerializable(typeof(Modules.Input.PerformActionsCommand))]
[JsonSerializable(typeof(Modules.Input.Pointer.Down), TypeInfoPropertyName = "Input_Pointer_Down")]
[JsonSerializable(typeof(Modules.Input.Pointer.Up), TypeInfoPropertyName = "Input_Pointer_Up")]
[JsonSerializable(typeof(Modules.Input.Pointer.Move), TypeInfoPropertyName = "Input_Pointer_Move")]
[JsonSerializable(typeof(Modules.Input.Key.Down), TypeInfoPropertyName = "Input_Key_Down")]
[JsonSerializable(typeof(Modules.Input.Key.Up), TypeInfoPropertyName = "Input_Key_Up")]
[JsonSerializable(typeof(IEnumerable<Modules.Input.IPointerSourceAction>))]
[JsonSerializable(typeof(IEnumerable<Modules.Input.IKeySourceAction>))]
[JsonSerializable(typeof(IEnumerable<Modules.Input.INoneSourceAction>))]
[JsonSerializable(typeof(IEnumerable<Modules.Input.IWheelSourceAction>))]

internal partial class BiDiJsonSerializerContext : JsonSerializerContext;
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
// under the License.
// </copyright>

using System.Text.Json;
using System.Threading.Tasks;
using System.Threading;
using System;
using System.Text.Json.Serialization;

#nullable enable

Expand All @@ -30,7 +30,7 @@ interface ITransport : IDisposable
{
Task ConnectAsync(CancellationToken cancellationToken);

Task<T> ReceiveAsJsonAsync<T>(JsonSerializerOptions jsonSerializerOptions, CancellationToken cancellationToken);
Task<T> ReceiveAsJsonAsync<T>(JsonSerializerContext jsonSerializerContext, CancellationToken cancellationToken);

Task SendAsJsonAsync(Command command, JsonSerializerOptions jsonSerializerOptions, CancellationToken cancellationToken);
Task SendAsJsonAsync(Command command, JsonSerializerContext jsonSerializerContext, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
using System.Text.Json;
using System.Text;
using OpenQA.Selenium.Internal.Logging;
using System.Text.Json.Serialization;

#nullable enable

Expand All @@ -44,7 +45,7 @@ public async Task ConnectAsync(CancellationToken cancellationToken)
await _webSocket.ConnectAsync(_uri, cancellationToken).ConfigureAwait(false);
}

public async Task<T> ReceiveAsJsonAsync<T>(JsonSerializerOptions jsonSerializerOptions, CancellationToken cancellationToken)
public async Task<T> ReceiveAsJsonAsync<T>(JsonSerializerContext jsonSerializerContext, CancellationToken cancellationToken)
{
using var ms = new MemoryStream();

Expand All @@ -65,14 +66,14 @@ public async Task<T> ReceiveAsJsonAsync<T>(JsonSerializerOptions jsonSerializerO
_logger.Trace($"BiDi RCV << {Encoding.UTF8.GetString(ms.ToArray())}");
}

var res = await JsonSerializer.DeserializeAsync(ms, typeof(T), jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
var res = await JsonSerializer.DeserializeAsync(ms, typeof(T), jsonSerializerContext, cancellationToken).ConfigureAwait(false);

return (T)res!;
}

public async Task SendAsJsonAsync(Command command, JsonSerializerOptions jsonSerializerOptions, CancellationToken cancellationToken)
public async Task SendAsJsonAsync(Command command, JsonSerializerContext jsonSerializerContext, CancellationToken cancellationToken)
{
var buffer = JsonSerializer.SerializeToUtf8Bytes(command, typeof(Command), jsonSerializerOptions);
var buffer = JsonSerializer.SerializeToUtf8Bytes(command, typeof(Command), jsonSerializerContext);

await _socketSendSemaphoreSlim.WaitAsync(cancellationToken);

Expand Down

0 comments on commit 8d0f6af

Please sign in to comment.