Skip to content

Commit 9a53261

Browse files
committed
Revert optimization
It wasn't clear that this led to a real perf benefit in optimized builds
1 parent 48e7961 commit 9a53261

File tree

3 files changed

+66
-54
lines changed

3 files changed

+66
-54
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Text.Json;
5+
using System.Text.Json.Serialization;
6+
using System.Text.Json.Serialization.Metadata;
7+
8+
namespace Microsoft.AspNetCore.Components;
9+
10+
// For custom converters that don't rely on serializing an object graph,
11+
// we can resolve the incoming type's JsonTypeInfo directly from the converter.
12+
// This skips extra work to collect metadata for the type that won't be used.
13+
internal sealed class JsonConverterFactoryTypeInfoResolver<T> : IJsonTypeInfoResolver
14+
{
15+
public static readonly JsonConverterFactoryTypeInfoResolver<T> Instance = new();
16+
17+
private JsonConverterFactoryTypeInfoResolver()
18+
{
19+
}
20+
21+
public JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options)
22+
{
23+
if (type != typeof(T))
24+
{
25+
return null;
26+
}
27+
28+
foreach (var converter in options.Converters)
29+
{
30+
if (converter is not JsonConverterFactory factory || !factory.CanConvert(type))
31+
{
32+
continue;
33+
}
34+
35+
if (factory.CreateConverter(type, options) is not { } converterToUse)
36+
{
37+
continue;
38+
}
39+
40+
return JsonMetadataServices.CreateValueInfo<T>(options, converterToUse);
41+
}
42+
43+
return null;
44+
}
45+
}

src/Components/Web/src/Microsoft.AspNetCore.Components.Web.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<Compile Include="$(ComponentsSharedSourceRoot)src\AttributeUtilities.cs" LinkBase="Forms" />
1717
<Compile Include="$(ComponentsSharedSourceRoot)src\ExpressionFormatting\**\*.cs" LinkBase="Forms\ExpressionFommatting" />
1818
<Compile Include="$(ComponentsSharedSourceRoot)src\DefaultAntiforgeryStateProvider.cs" LinkBase="Forms" />
19+
<Compile Include="$(ComponentsSharedSourceRoot)src\JsonSerialization\JsonConverterFactoryTypeInfoResolver.cs" LinkBase="JsonSerialization" />
1920
</ItemGroup>
2021

2122
<ItemGroup>

src/Components/Web/src/WebRenderer.cs

Lines changed: 20 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics.CodeAnalysis;
5-
using System.Globalization;
65
using System.Text.Json;
76
using System.Text.Json.Serialization;
8-
using System.Text.Json.Serialization.Metadata;
97
using Microsoft.AspNetCore.Components.Web;
108
using Microsoft.AspNetCore.Components.Web.Infrastructure;
119
using Microsoft.AspNetCore.Components.Web.Internal;
@@ -102,10 +100,19 @@ protected override void Dispose(bool disposing)
102100
base.Dispose(disposing);
103101
}
104102

103+
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
105104
private void AttachWebRendererInterop(IJSRuntime jsRuntime, JsonSerializerOptions jsonOptions, JSComponentInterop jsComponentInterop)
106105
{
107106
const string JSMethodIdentifier = "Blazor._internal.attachWebRendererInterop";
108107

108+
// These arguments should be kept in sync with WebRendererSerializerContext
109+
object[] args = [
110+
_rendererId,
111+
_interopMethodsReference,
112+
jsComponentInterop.Configuration.JSComponentParametersByIdentifier,
113+
jsComponentInterop.Configuration.JSComponentIdentifiersByInitializer,
114+
];
115+
109116
if (jsRuntime is IInternalWebJSInProcessRuntime inProcessRuntime)
110117
{
111118
// Fast path for WebAssembly: Rather than using the JSRuntime to serialize
@@ -117,59 +124,18 @@ private void AttachWebRendererInterop(IJSRuntime jsRuntime, JsonSerializerOption
117124
// a supported type in the JsonSerializerContext generates unnecessary code to produce
118125
// JsonTypeInfo for all the types referenced by both DotNetObjectReference<T> and its
119126
// generic type argument.
120-
var interopMethodsReferenceJsonTypeInfo = GetJsonTypeInfoFromJsonConverterFactories<DotNetObjectReference<WebRendererInteropMethods>>(
121-
jsonOptions.Converters,
122-
WebRendererSerializerContext.Default.Options);
123-
124-
var rendererIdJson = _rendererId.ToString(CultureInfo.InvariantCulture);
125-
var interopMethodsReferenceJson = JsonSerializer.Serialize(
126-
_interopMethodsReference,
127-
interopMethodsReferenceJsonTypeInfo);
128-
var jsComponentParametersByIdentifierJson = JsonSerializer.Serialize(
129-
jsComponentInterop.Configuration.JSComponentParametersByIdentifier,
130-
WebRendererSerializerContext.Default.DictionaryStringJSComponentParameterArray);
131-
var jsComponentIdentifiersByInitializerJson = JsonSerializer.Serialize(
132-
jsComponentInterop.Configuration.JSComponentIdentifiersByInitializer,
133-
WebRendererSerializerContext.Default.DictionaryStringListString);
134-
135-
var argsJson =
136-
$"[{rendererIdJson}, " +
137-
$"{interopMethodsReferenceJson}, " +
138-
$"{jsComponentParametersByIdentifierJson}, " +
139-
$"{jsComponentIdentifiersByInitializerJson}]";
127+
128+
var newJsonOptions = new JsonSerializerOptions(jsonOptions);
129+
newJsonOptions.TypeInfoResolverChain.Clear();
130+
newJsonOptions.TypeInfoResolverChain.Add(WebRendererSerializerContext.Default);
131+
newJsonOptions.TypeInfoResolverChain.Add(JsonConverterFactoryTypeInfoResolver<DotNetObjectReference<WebRendererInteropMethods>>.Instance);
132+
var argsJson = JsonSerializer.Serialize(args, newJsonOptions);
140133
inProcessRuntime.InvokeJS(JSMethodIdentifier, argsJson, JSCallResultType.JSVoidResult, 0);
141134
}
142135
else
143136
{
144-
jsRuntime.InvokeVoidAsync(
145-
JSMethodIdentifier,
146-
_rendererId,
147-
_interopMethodsReference,
148-
jsComponentInterop.Configuration.JSComponentParametersByIdentifier,
149-
jsComponentInterop.Configuration.JSComponentIdentifiersByInitializer).Preserve();
150-
}
151-
}
152-
153-
private static JsonTypeInfo<T> GetJsonTypeInfoFromJsonConverterFactories<T>(
154-
IList<JsonConverter> converters,
155-
JsonSerializerOptions optionsToUse)
156-
{
157-
foreach (var converter in converters)
158-
{
159-
if (converter is not JsonConverterFactory factory || !factory.CanConvert(typeof(T)))
160-
{
161-
continue;
162-
}
163-
164-
if (factory.CreateConverter(typeof(T), optionsToUse) is not { } converterToUse)
165-
{
166-
continue;
167-
}
168-
169-
return JsonMetadataServices.CreateValueInfo<T>(optionsToUse, converterToUse);
137+
jsRuntime.InvokeVoidAsync(JSMethodIdentifier, args).Preserve();
170138
}
171-
172-
throw new InvalidOperationException($"Could not create a JsonTypeInfo for type {typeof(T).FullName}");
173139
}
174140

175141
/// <summary>
@@ -215,10 +181,10 @@ public void RemoveRootComponent(int componentId)
215181
}
216182
}
217183

218-
[JsonSourceGenerationOptions(
219-
GenerationMode = JsonSourceGenerationMode.Serialization,
220-
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
221-
PropertyNameCaseInsensitive = true)]
184+
// This should be kept in sync with the argument types in the call to
185+
// 'Blazor._internal.attachWebRendererInterop'
186+
[JsonSerializable(typeof(object[]))]
187+
[JsonSerializable(typeof(int))]
222188
[JsonSerializable(typeof(Dictionary<string, JSComponentConfigurationStore.JSComponentParameter[]>))]
223189
[JsonSerializable(typeof(Dictionary<string, List<string>>))]
224190
internal sealed partial class WebRendererSerializerContext : JsonSerializerContext;

0 commit comments

Comments
 (0)