From 6504cdb355b8bfbd0f1ae316ffe87ecddedc5dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Cant=C3=BA?= <16040868+Jozkee@users.noreply.github.com> Date: Mon, 11 Sep 2023 12:53:49 -0500 Subject: [PATCH] Enable rule CA1869 - Cache and reuse 'JsonSerializerOptions' (#90895) * Enable rule CA1869 * Fix ocurrences in src/tasks projects * Fix more occurrences on installer and mono\wasm --- eng/CodeAnalysis.src.globalconfig | 3 +++ eng/CodeAnalysis.test.globalconfig | 3 +++ .../ComHost/ClsidMap.cs | 4 +++- src/mono/wasm/host/CommonConfiguration.cs | 14 +++++++------ src/mono/wasm/host/RunArgumentsJson.cs | 12 ++++++----- src/mono/wasm/host/RunConfiguration.cs | 7 +------ src/tasks/Common/FileCache.cs | 5 +++-- .../RuntimeConfigParser.cs | 21 ++++++++++--------- src/tasks/WasmAppBuilder/WasmAppBuilder.cs | 16 +++++++------- .../InstallWorkloadFromArtifacts.cs | 11 +++++----- 10 files changed, 54 insertions(+), 42 deletions(-) diff --git a/eng/CodeAnalysis.src.globalconfig b/eng/CodeAnalysis.src.globalconfig index 12304e2799a89..26c9d2e8cf74e 100644 --- a/eng/CodeAnalysis.src.globalconfig +++ b/eng/CodeAnalysis.src.globalconfig @@ -486,6 +486,9 @@ dotnet_diagnostic.CA1864.severity = warning # CA1868: Unnecessary call to 'Contains' for sets dotnet_diagnostic.CA1868.severity = warning +# CA1869: Cache and reuse 'JsonSerializerOptions' instances +dotnet_diagnostic.CA1869.severity = warning + # CA2000: Dispose objects before losing scope dotnet_diagnostic.CA2000.severity = none diff --git a/eng/CodeAnalysis.test.globalconfig b/eng/CodeAnalysis.test.globalconfig index f0963e2cfe433..0a50a68d677b0 100644 --- a/eng/CodeAnalysis.test.globalconfig +++ b/eng/CodeAnalysis.test.globalconfig @@ -483,6 +483,9 @@ dotnet_diagnostic.CA1864.severity = none # CA1868: Unnecessary call to 'Contains' for sets dotnet_diagnostic.CA1868.severity = none +# CA1869: Cache and reuse 'JsonSerializerOptions' instances +dotnet_diagnostic.CA1869.severity = none + # CA2000: Dispose objects before losing scope dotnet_diagnostic.CA2000.severity = none diff --git a/src/installer/managed/Microsoft.NET.HostModel/ComHost/ClsidMap.cs b/src/installer/managed/Microsoft.NET.HostModel/ComHost/ClsidMap.cs index a8cbb02294ea2..4d7dcd09a00a6 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/ComHost/ClsidMap.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/ComHost/ClsidMap.cs @@ -18,6 +18,8 @@ namespace Microsoft.NET.HostModel.ComHost { public static class ClsidMap { + private static readonly JsonSerializerOptions s_jsonOptions = new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; + private struct ClsidEntry { [JsonPropertyName("type")] @@ -65,7 +67,7 @@ public static void Create(MetadataReader metadataReader, string clsidMapPath) using (StreamWriter writer = File.CreateText(clsidMapPath)) { - writer.Write(JsonSerializer.Serialize(clsidMap, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull })); + writer.Write(JsonSerializer.Serialize(clsidMap, s_jsonOptions)); } } diff --git a/src/mono/wasm/host/CommonConfiguration.cs b/src/mono/wasm/host/CommonConfiguration.cs index 9e97bc4511930..e83337a9bc868 100644 --- a/src/mono/wasm/host/CommonConfiguration.cs +++ b/src/mono/wasm/host/CommonConfiguration.cs @@ -26,7 +26,14 @@ internal sealed class CommonConfiguration public string? RuntimeConfigPath { get; private set; } private string? hostArg; + private static readonly JsonSerializerOptions s_jsonOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web) + { + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip, + PropertyNameCaseInsensitive = true + }; + public static JsonSerializerOptions JsonOptions => s_jsonOptions; public static CommonConfiguration FromCommandLineArguments(string[] args) => new CommonConfiguration(args); private CommonConfiguration(string[] args) @@ -62,12 +69,7 @@ private CommonConfiguration(string[] args) RuntimeConfig? rconfig = JsonSerializer.Deserialize( File.ReadAllText(RuntimeConfigPath), - new JsonSerializerOptions(JsonSerializerDefaults.Web) - { - AllowTrailingCommas = true, - ReadCommentHandling = JsonCommentHandling.Skip, - PropertyNameCaseInsensitive = true - }); + JsonOptions); if (rconfig == null) throw new CommandLineException($"Failed to deserialize {RuntimeConfigPath}"); diff --git a/src/mono/wasm/host/RunArgumentsJson.cs b/src/mono/wasm/host/RunArgumentsJson.cs index f4573c1c7ff5a..a8ab0cdf13ef3 100644 --- a/src/mono/wasm/host/RunArgumentsJson.cs +++ b/src/mono/wasm/host/RunArgumentsJson.cs @@ -18,17 +18,19 @@ internal sealed record RunArgumentsJson( bool debugging = false ) { + private static readonly JsonSerializerOptions s_jsonOptions = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + // using an explicit property because the deserializer doesn't like // extension data in the record constructor [property: JsonExtensionData] public Dictionary? Extra { get; set; } public void Save(string file) { - string json = JsonSerializer.Serialize(this, new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase - }); + string json = JsonSerializer.Serialize(this, s_jsonOptions); File.WriteAllText(file, json); } } diff --git a/src/mono/wasm/host/RunConfiguration.cs b/src/mono/wasm/host/RunConfiguration.cs index caf5b183f58bc..732c40cc32aac 100644 --- a/src/mono/wasm/host/RunConfiguration.cs +++ b/src/mono/wasm/host/RunConfiguration.cs @@ -27,12 +27,7 @@ public RunConfiguration(string runtimeConfigPath, string? hostArg) RuntimeConfig? rconfig = JsonSerializer.Deserialize( File.ReadAllText(runtimeConfigPath), - new JsonSerializerOptions(JsonSerializerDefaults.Web) - { - AllowTrailingCommas = true, - ReadCommentHandling = JsonCommentHandling.Skip, - PropertyNameCaseInsensitive = true - }); + CommonConfiguration.JsonOptions); if (rconfig == null) throw new Exception($"Failed to deserialize {runtimeConfigPath}"); diff --git a/src/tasks/Common/FileCache.cs b/src/tasks/Common/FileCache.cs index c362719b3be59..39ecd5e70ab2d 100644 --- a/src/tasks/Common/FileCache.cs +++ b/src/tasks/Common/FileCache.cs @@ -16,6 +16,7 @@ internal sealed class FileCache { private CompilerCache? _newCache; private CompilerCache? _oldCache; + private static readonly JsonSerializerOptions s_jsonOptions = new JsonSerializerOptions { WriteIndented = true }; public bool Enabled { get; } public TaskLoggingHelper Log { get; } @@ -34,7 +35,7 @@ public FileCache(string? cacheFilePath, TaskLoggingHelper log) { _oldCache = (CompilerCache?)JsonSerializer.Deserialize(File.ReadAllText(cacheFilePath), typeof(CompilerCache), - new JsonSerializerOptions()); + s_jsonOptions); } _oldCache ??= new(); @@ -84,7 +85,7 @@ public bool Save(string? cacheFilePath) if (!Enabled || string.IsNullOrEmpty(cacheFilePath)) return false; - var json = JsonSerializer.Serialize (_newCache, new JsonSerializerOptions { WriteIndented = true }); + var json = JsonSerializer.Serialize (_newCache, s_jsonOptions); File.WriteAllText(cacheFilePath!, json); return true; } diff --git a/src/tasks/MonoTargetsTasks/RuntimeConfigParser/RuntimeConfigParser.cs b/src/tasks/MonoTargetsTasks/RuntimeConfigParser/RuntimeConfigParser.cs index 612df3cc9f0fe..3430fb09f72b2 100644 --- a/src/tasks/MonoTargetsTasks/RuntimeConfigParser/RuntimeConfigParser.cs +++ b/src/tasks/MonoTargetsTasks/RuntimeConfigParser/RuntimeConfigParser.cs @@ -30,6 +30,16 @@ public class RuntimeConfigParserTask : Task /// public ITaskItem[] RuntimeConfigReservedProperties { get; set; } = Array.Empty(); + private static readonly JsonSerializerOptions s_jsonOptions = new JsonSerializerOptions + { + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip, + Converters = + { + new StringConverter() + } + }; + public override bool Execute() { if (string.IsNullOrEmpty(RuntimeConfigFile)) @@ -72,17 +82,8 @@ private bool TryConvertInputToDictionary(string inputFilePath, [NotNullWhen(true { result = null; - var options = new JsonSerializerOptions { - AllowTrailingCommas = true, - ReadCommentHandling = JsonCommentHandling.Skip, - Converters = - { - new StringConverter() - } - }; - var jsonString = File.ReadAllText(inputFilePath); - var parsedJson = JsonSerializer.Deserialize(jsonString, options); + var parsedJson = JsonSerializer.Deserialize(jsonString, s_jsonOptions); if (parsedJson == null) { diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index 869feb1277011..5c7cfbf4dd2ee 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -29,6 +29,14 @@ public class WasmAppBuilder : WasmAppBuilderBaseTask public string? RuntimeAssetsLocation { get; set; } public bool CacheBootResources { get; set; } + private static readonly JsonSerializerOptions s_jsonOptions = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + WriteIndented = true + }; + + // // Extra json elements to add to _framework/blazor.boot.json // @@ -368,13 +376,7 @@ protected override bool ExecuteInternal() { helper.ComputeResourcesHash(bootConfig); - var jsonOptions = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = true - }; - var json = JsonSerializer.Serialize(bootConfig, jsonOptions); + var json = JsonSerializer.Serialize(bootConfig, s_jsonOptions); sw.Write(json); } diff --git a/src/tasks/WorkloadBuildTasks/InstallWorkloadFromArtifacts.cs b/src/tasks/WorkloadBuildTasks/InstallWorkloadFromArtifacts.cs index 8586a4db4fe3a..81a8d450de751 100644 --- a/src/tasks/WorkloadBuildTasks/InstallWorkloadFromArtifacts.cs +++ b/src/tasks/WorkloadBuildTasks/InstallWorkloadFromArtifacts.cs @@ -49,6 +49,11 @@ public partial class InstallWorkloadFromArtifacts : Task private string AllManifestsStampPath => Path.Combine(SdkWithNoWorkloadInstalledPath, ".all-manifests.stamp"); private string _tempDir = string.Empty; private string _nugetCachePath = string.Empty; + private static readonly JsonSerializerOptions s_jsonOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web) + { + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip + }; [GeneratedRegex(@"^\d+\.\d+\.\d+(-[A-z]*\.*\d*)?")] private static partial Regex bandVersionRegex(); @@ -330,11 +335,7 @@ private bool InstallWorkloadManifest(ITaskItem workloadId, string name, string v { manifest = JsonSerializer.Deserialize( File.ReadAllBytes(jsonPath), - new JsonSerializerOptions(JsonSerializerDefaults.Web) - { - AllowTrailingCommas = true, - ReadCommentHandling = JsonCommentHandling.Skip - }); + s_jsonOptions); if (manifest == null) {