Ensure Memory<T>/ReadOnlyMemory<T> are marked as unsupported types. (#86900)
authorEirik Tsarpalis <eirik.tsarpalis@gmail.com>
Tue, 30 May 2023 22:12:23 +0000 (23:12 +0100)
committerGitHub <noreply@github.com>
Tue, 30 May 2023 22:12:23 +0000 (23:12 +0100)
* Ensure Memory<T>/ReadOnlyMemory<T> are marked as unsupported types.

* Update src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/InvalidTypeTests.cs

src/libraries/System.Text.Json/gen/Helpers/KnownTypeSymbols.cs
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UnsupportedTypeConverterFactory.cs
src/libraries/System.Text.Json/tests/Common/UnsupportedTypesTests.cs
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/UnsupportedTypesTests.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/InvalidTypeTests.cs

index 2c32d00..9d02fa2 100644 (file)
@@ -213,6 +213,11 @@ namespace System.Text.Json.SourceGeneration
         public INamedTypeSymbol? UIntPtrType => GetOrResolveType(typeof(UIntPtr), ref _UIntPtrType);
         private Option<INamedTypeSymbol?> _UIntPtrType;
 
+        public INamedTypeSymbol? MemoryType => GetOrResolveType(typeof(Memory<>), ref _MemoryType);
+        private Option<INamedTypeSymbol?> _MemoryType;
+
+        public INamedTypeSymbol? ReadOnlyMemoryType => GetOrResolveType(typeof(ReadOnlyMemory<>), ref _ReadOnlyMemoryType);
+        private Option<INamedTypeSymbol?> _ReadOnlyMemoryType;
 
         public bool IsImmutableEnumerableType(ITypeSymbol type, out string? factoryTypeFullName)
         {
index 6fe2320..f64624d 100644 (file)
@@ -1510,6 +1510,8 @@ namespace System.Text.Json.SourceGeneration
                     SymbolEqualityComparer.Default.Equals(_knownSymbols.UIntPtrType, type) ||
                     _knownSymbols.MemberInfoType.IsAssignableFrom(type) ||
                     _knownSymbols.DelegateType.IsAssignableFrom(type) ||
+                    SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, _knownSymbols.MemoryType) ||
+                    SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, _knownSymbols.ReadOnlyMemoryType) ||
                     type is IArrayTypeSymbol { Rank: > 1 };
             }
 
index c911b30..bd88a2b 100644 (file)
@@ -1,6 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 using System.Runtime.Serialization;
@@ -25,8 +26,21 @@ namespace System.Text.Json.Serialization.Converters
                 type == typeof(SerializationInfo) ||
                 type == typeof(IntPtr) ||
                 type == typeof(UIntPtr) ||
+                // Exclude Memory<T> and ReadOnlyMemory<T> types.
+                IsMemoryType(type) ||
                 // Exclude delegates.
                 typeof(Delegate).IsAssignableFrom(type);
+
+            static bool IsMemoryType(Type type)
+            {
+                if (!type.IsGenericType || !type.IsValueType)
+                {
+                    return false;
+                }
+
+                Type typeDef = type.GetGenericTypeDefinition();
+                return typeDef == typeof(Memory<>) || typeDef == typeof(ReadOnlyMemory<>);
+            }
         }
 
         public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options)
index df8850b..e28cfd5 100644 (file)
@@ -124,6 +124,8 @@ namespace System.Text.Json.Serialization.Tests
             yield return WrapArgs((IntPtr)123);
             yield return WrapArgs<IntPtr?>(new IntPtr(123)); // One nullable variation.
             yield return WrapArgs((UIntPtr)123);
+            yield return WrapArgs((Memory<byte>)new byte[] { 1, 2, 3 });
+            yield return WrapArgs((ReadOnlyMemory<byte>)new byte[] { 1, 2, 3 });
 
             static object[] WrapArgs<T>(T value) => new object[] { new ValueWrapper<T>(value) };
         }
index 8dc14d3..d00c183 100644 (file)
@@ -41,6 +41,10 @@ namespace System.Text.Json.SourceGeneration.Tests
         [JsonSerializable(typeof(ClassWithType<IntPtr?>))]
         [JsonSerializable(typeof(UIntPtr))]
         [JsonSerializable(typeof(ClassWithType<UIntPtr>))]
+        [JsonSerializable(typeof(Memory<byte>))]
+        [JsonSerializable(typeof(ClassWithType<Memory<byte>>))]
+        [JsonSerializable(typeof(ReadOnlyMemory<byte>))]
+        [JsonSerializable(typeof(ClassWithType<ReadOnlyMemory<byte>>))]
         [JsonSerializable(typeof(IAsyncEnumerable<int>))]
         [JsonSerializable(typeof(ClassWithType<IAsyncEnumerable<int>>))]
         [JsonSerializable(typeof(ClassThatImplementsIAsyncEnumerable))]
@@ -82,6 +86,10 @@ namespace System.Text.Json.SourceGeneration.Tests
         [JsonSerializable(typeof(ClassWithType<IntPtr?>))]
         [JsonSerializable(typeof(UIntPtr))]
         [JsonSerializable(typeof(ClassWithType<UIntPtr>))]
+        [JsonSerializable(typeof(Memory<byte>))]
+        [JsonSerializable(typeof(ClassWithType<Memory<byte>>))]
+        [JsonSerializable(typeof(ReadOnlyMemory<byte>))]
+        [JsonSerializable(typeof(ClassWithType<ReadOnlyMemory<byte>>))]
         [JsonSerializable(typeof(IAsyncEnumerable<int>))]
         [JsonSerializable(typeof(ClassWithType<IAsyncEnumerable<int>>))]
         [JsonSerializable(typeof(ClassThatImplementsIAsyncEnumerable))]
index 88e3714..91ca5c0 100644 (file)
@@ -157,11 +157,16 @@ namespace System.Text.Json.Serialization.Tests
         // and typeof(int*) can't be a generic parameter to the generic overload.
         public static IEnumerable<object[]> TypesWithInvalidMembers_WithMembers()
         {
-            yield return new object[] { typeof(Memory<byte>), typeof(Span<byte>), "Span" }; // Contains Span<byte> property.
+            yield return new object[] { typeof(ClassWithSpan), typeof(Span<byte>), "Span" };
 
             yield return new object[] { typeof(ClassWithIntPtr), s_intPtrType, "IntPtr" };
         }
 
+        private class ClassWithSpan
+        {
+            public Span<byte> Span => Array.Empty<byte>();
+        }
+
         private class ClassWithIntPtr
         {
             public unsafe int* IntPtr { get; }