From 0099ffa86bc252262530f1245ba16890057875f7 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sun, 16 Apr 2023 18:05:21 -0700 Subject: [PATCH] Revert use of raw pointers in System.Text.Json (#78741)" (#84899) Revert use of raw pointers for .NET Standard support. This particular raw pointer use does not work with .NET UWP toolchain. Also, convert uses of Enum.HasFlags to simple bit tests for better .NET Framework and .NET Standard support. Enum.HasFlags performs poorly (boxes) on .NET Framework and .NET Standard targets. Fixes #84895 --- .../Collection/JsonCollectionConverter.cs | 8 ++-- .../Collection/JsonDictionaryConverter.cs | 4 +- .../Converters/Object/ObjectDefaultConverter.cs | 4 +- .../ObjectWithParameterizedConstructorConverter.cs | 4 +- .../Converters/Value/EnumConverter.cs | 51 ++++++++++++---------- .../JsonConverter.MetadataHandling.cs | 2 +- .../JsonSerializer.Read.HandleMetadata.cs | 4 +- 7 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs index d7153ae..e87b7f1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs @@ -161,7 +161,7 @@ namespace System.Text.Json.Serialization } // Dispatch to any polymorphic converters: should always be entered regardless of ObjectState progress - if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Type) && + if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Type) != 0 && state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntryStarted && ResolvePolymorphicConverter(jsonTypeInfo, ref state) is JsonConverter polymorphicConverter) { @@ -181,7 +181,7 @@ namespace System.Text.Json.Serialization CreateCollection(ref reader, ref state, options); - if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Id)) + if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Id) != 0) { Debug.Assert(state.ReferenceId != null); Debug.Assert(options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve); @@ -245,7 +245,7 @@ namespace System.Text.Json.Serialization state.Current.ObjectState = StackFrameObjectState.EndToken; // Array payload is nested inside a $values metadata property. - if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Values)) + if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Values) != 0) { if (!reader.Read()) { @@ -258,7 +258,7 @@ namespace System.Text.Json.Serialization if (state.Current.ObjectState < StackFrameObjectState.EndTokenValidation) { // Array payload is nested inside a $values metadata property. - if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Values)) + if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Values) != 0) { if (reader.TokenType != JsonTokenType.EndObject) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs index e1dbc74..d637543 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs @@ -185,7 +185,7 @@ namespace System.Text.Json.Serialization } // Dispatch to any polymorphic converters: should always be entered regardless of ObjectState progress - if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Type) && + if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Type) != 0 && state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntryStarted && ResolvePolymorphicConverter(jsonTypeInfo, ref state) is JsonConverter polymorphicConverter) { @@ -206,7 +206,7 @@ namespace System.Text.Json.Serialization CreateCollection(ref reader, ref state); - if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Id)) + if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Id) != 0) { Debug.Assert(state.ReferenceId != null); Debug.Assert(options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs index 2568f58..1a3ddf3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs @@ -102,7 +102,7 @@ namespace System.Text.Json.Serialization.Converters } // Dispatch to any polymorphic converters: should always be entered regardless of ObjectState progress - if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Type) && + if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Type) != 0 && state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntryStarted && ResolvePolymorphicConverter(jsonTypeInfo, ref state) is JsonConverter polymorphicConverter) { @@ -133,7 +133,7 @@ namespace System.Text.Json.Serialization.Converters obj = jsonTypeInfo.CreateObject()!; - if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Id)) + if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Id) != 0) { Debug.Assert(state.ReferenceId != null); Debug.Assert(options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs index 064a9a6..0edf2d2 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs @@ -132,7 +132,7 @@ namespace System.Text.Json.Serialization.Converters } // Dispatch to any polymorphic converters: should always be entered regardless of ObjectState progress - if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Type) && + if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Type) != 0 && state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntryStarted && ResolvePolymorphicConverter(jsonTypeInfo, ref state) is JsonConverter polymorphicConverter) { @@ -170,7 +170,7 @@ namespace System.Text.Json.Serialization.Converters obj = (T)CreateObject(ref state.Current); - if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Id)) + if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Id) != 0) { Debug.Assert(state.ReferenceId != null); Debug.Assert(options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs index 4a5edd1..08d3e37 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs @@ -5,6 +5,7 @@ using System.Buffers; using System.Collections.Concurrent; using System.Diagnostics; using System.Globalization; +using System.Runtime.CompilerServices; using System.Text.Encodings.Web; namespace System.Text.Json.Serialization.Converters @@ -93,14 +94,13 @@ namespace System.Text.Json.Serialization.Converters } } -#pragma warning disable 8500 // address of managed types - public override unsafe T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { JsonTokenType token = reader.TokenType; if (token == JsonTokenType.String) { - if (!_converterOptions.HasFlag(EnumConverterOptions.AllowStrings)) + if ((_converterOptions & EnumConverterOptions.AllowStrings) == 0) { ThrowHelper.ThrowJsonException(); return default; @@ -122,7 +122,7 @@ namespace System.Text.Json.Serialization.Converters #endif } - if (token != JsonTokenType.Number || !_converterOptions.HasFlag(EnumConverterOptions.AllowNumbers)) + if (token != JsonTokenType.Number || (_converterOptions & EnumConverterOptions.AllowNumbers) == 0) { ThrowHelper.ThrowJsonException(); return default; @@ -135,49 +135,51 @@ namespace System.Text.Json.Serialization.Converters case TypeCode.Int32: if (reader.TryGetInt32(out int int32)) { - return *(T*)&int32; + // Use Unsafe.As instead of raw pointers for .NET Standard support. + // https://github.com/dotnet/runtime/issues/84895 + return Unsafe.As(ref int32); } break; case TypeCode.UInt32: if (reader.TryGetUInt32(out uint uint32)) { - return *(T*)&uint32; + return Unsafe.As(ref uint32); } break; case TypeCode.UInt64: if (reader.TryGetUInt64(out ulong uint64)) { - return *(T*)&uint64; + return Unsafe.As(ref uint64); } break; case TypeCode.Int64: if (reader.TryGetInt64(out long int64)) { - return *(T*)&int64; + return Unsafe.As(ref int64); } break; case TypeCode.SByte: if (reader.TryGetSByte(out sbyte byte8)) { - return *(T*)&byte8; + return Unsafe.As(ref byte8); } break; case TypeCode.Byte: if (reader.TryGetByte(out byte ubyte8)) { - return *(T*)&ubyte8; + return Unsafe.As(ref ubyte8); } break; case TypeCode.Int16: if (reader.TryGetInt16(out short int16)) { - return *(T*)&int16; + return Unsafe.As(ref int16); } break; case TypeCode.UInt16: if (reader.TryGetUInt16(out ushort uint16)) { - return *(T*)&uint16; + return Unsafe.As(ref uint16); } break; } @@ -186,10 +188,10 @@ namespace System.Text.Json.Serialization.Converters return default; } - public override unsafe void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { // If strings are allowed, attempt to write it out as a string value - if (_converterOptions.HasFlag(EnumConverterOptions.AllowStrings)) + if ((_converterOptions & EnumConverterOptions.AllowStrings) != 0) { ulong key = ConvertToUInt64(value); @@ -226,7 +228,7 @@ namespace System.Text.Json.Serialization.Converters } } - if (!_converterOptions.HasFlag(EnumConverterOptions.AllowNumbers)) + if ((_converterOptions & EnumConverterOptions.AllowNumbers) == 0) { ThrowHelper.ThrowJsonException(); } @@ -234,35 +236,36 @@ namespace System.Text.Json.Serialization.Converters switch (s_enumTypeCode) { case TypeCode.Int32: - writer.WriteNumberValue(*(int*)&value); + // Use Unsafe.As instead of raw pointers for .NET Standard support. + // https://github.com/dotnet/runtime/issues/84895 + writer.WriteNumberValue(Unsafe.As(ref value)); break; case TypeCode.UInt32: - writer.WriteNumberValue(*(uint*)&value); + writer.WriteNumberValue(Unsafe.As(ref value)); break; case TypeCode.UInt64: - writer.WriteNumberValue(*(ulong*)&value); + writer.WriteNumberValue(Unsafe.As(ref value)); break; case TypeCode.Int64: - writer.WriteNumberValue(*(long*)&value); + writer.WriteNumberValue(Unsafe.As(ref value)); break; case TypeCode.Int16: - writer.WriteNumberValue(*(short*)&value); + writer.WriteNumberValue(Unsafe.As(ref value)); break; case TypeCode.UInt16: - writer.WriteNumberValue(*(ushort*)&value); + writer.WriteNumberValue(Unsafe.As(ref value)); break; case TypeCode.Byte: - writer.WriteNumberValue(*(byte*)&value); + writer.WriteNumberValue(Unsafe.As(ref value)); break; case TypeCode.SByte: - writer.WriteNumberValue(*(sbyte*)&value); + writer.WriteNumberValue(Unsafe.As(ref value)); break; default: ThrowHelper.ThrowJsonException(); break; } } -#pragma warning restore 8500 internal override T ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.MetadataHandling.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.MetadataHandling.cs index 74345fa..6a3de62 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.MetadataHandling.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.MetadataHandling.cs @@ -15,7 +15,7 @@ namespace System.Text.Json.Serialization { Debug.Assert(!IsValueType); Debug.Assert(CanHaveMetadata); - Debug.Assert(state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Type)); + Debug.Assert((state.Current.MetadataPropertyNames & MetadataPropertyName.Type) != 0); Debug.Assert(state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntryStarted); Debug.Assert(jsonTypeInfo.PolymorphicTypeResolver?.UsesTypeDiscriminators == true); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs index 4326af6..c9a9e60 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs @@ -50,7 +50,7 @@ namespace System.Text.Json // We just read a property. The only valid next tokens are EndObject and PropertyName. Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); - if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Ref)) + if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Ref) != 0) { // No properties whatsoever should follow a $ref property. ThrowHelper.ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(reader.GetSpan(), ref state); @@ -427,7 +427,7 @@ namespace System.Text.Json internal static void ValidateMetadataForObjectConverter(ref ReadStack state) { - if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Values)) + if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Values) != 0) { // Object converters do not support $values metadata. ThrowHelper.ThrowJsonException_MetadataUnexpectedProperty(s_valuesPropertyName, ref state); -- 2.7.4