From 5be70920626207098ffc56ee7e13bf14c9b4c483 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Mon, 27 Jun 2022 21:44:33 +0200 Subject: [PATCH] Argument tests for CreateJsonTypeInfo and CreateJsonPropertyInfo (#71324) * Argument tests for CreateJsonTypeInfo and CreateJsonPropertyInfo * add check for typeof(void) - possibly workaround #71339 --- .../Serialization/Metadata/JsonTypeInfo.cs | 37 +++++++++++++- .../Text/Json/ThrowHelper.Serialization.cs | 13 +++++ ...tJsonTypeInfoResolverTests.JsonTypeInfo.cs | 48 +++++++++++++++++++ .../DefaultJsonTypeInfoResolverTests.cs | 8 ++++ 4 files changed, 104 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs index 055c94c8334b9..1f60afd061d2d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs @@ -412,6 +412,11 @@ internal virtual void LateAddProperties() { } [RequiresDynamicCode("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use generic overload or System.Text.Json source generation for native AOT applications.")] public static JsonTypeInfo CreateJsonTypeInfo(JsonSerializerOptions options) { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + return new CustomJsonTypeInfo(options); } @@ -427,6 +432,21 @@ public static JsonTypeInfo CreateJsonTypeInfo(JsonSerializerOptions option [RequiresDynamicCode("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use generic overload or System.Text.Json source generation for native AOT applications.")] public static JsonTypeInfo CreateJsonTypeInfo(Type type, JsonSerializerOptions options) { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + if (IsInvalidForSerialization(type)) + { + ThrowHelper.ThrowArgumentException_CannotSerializeInvalidType(nameof(type), type, null, null); + } + s_createJsonTypeInfo ??= typeof(JsonTypeInfo).GetMethod(nameof(CreateJsonTypeInfo), new Type[] { typeof(JsonSerializerOptions) })!; return (JsonTypeInfo)s_createJsonTypeInfo.MakeGenericMethod(type) .Invoke(null, new object[] { options })!; @@ -440,7 +460,20 @@ public static JsonTypeInfo CreateJsonTypeInfo(Type type, JsonSerializerOptions o /// JsonPropertyInfo instance public JsonPropertyInfo CreateJsonPropertyInfo(Type propertyType, string name) { - ValidateType(propertyType, Type, null, Options); + if (propertyType == null) + { + throw new ArgumentNullException(nameof(propertyType)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (IsInvalidForSerialization(propertyType)) + { + ThrowHelper.ThrowArgumentException_CannotSerializeInvalidType(nameof(propertyType), propertyType, Type, name); + } JsonConverter converter = GetConverter(propertyType, parentClassType: null, @@ -609,7 +642,7 @@ internal static void ValidateType(Type type, Type? parentClassType, MemberInfo? internal static bool IsInvalidForSerialization(Type type) { - return type.IsPointer || type.IsByRef || IsByRefLike(type) || type.ContainsGenericParameters; + return type == typeof(void) || type.IsPointer || type.IsByRef || IsByRefLike(type) || type.ContainsGenericParameters; } private static bool IsByRefLike(Type type) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs index 0f08484bdc69a..460bc0cc02afc 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs @@ -85,6 +85,19 @@ public static void ThrowJsonException(string? message = null) throw new JsonException(message) { AppendPathInformation = true }; } + [DoesNotReturn] + public static void ThrowArgumentException_CannotSerializeInvalidType(string paramName, Type type, Type? parentClassType, string? propertyName) + { + if (parentClassType == null) + { + Debug.Assert(propertyName == null); + throw new ArgumentException(SR.Format(SR.CannotSerializeInvalidType, type), paramName); + } + + Debug.Assert(propertyName != null); + throw new ArgumentException(SR.Format(SR.CannotSerializeInvalidMember, type, propertyName, parentClassType), paramName); + } + [DoesNotReturn] public static void ThrowInvalidOperationException_CannotSerializeInvalidType(Type type, Type? parentClassType, MemberInfo? memberInfo) { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonTypeInfo.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonTypeInfo.cs index d46978ee314fb..62fba16e678b7 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonTypeInfo.cs @@ -580,6 +580,54 @@ public static void AddingNullJsonPropertyInfoIsNotPossible() Assert.NotNull(typeInfo.Properties[0]); } + [Fact] + public static void CreateJsonTypeInfoWithNullArgumentsThrows() + { + Assert.Throws(() => JsonTypeInfo.CreateJsonTypeInfo(null, new JsonSerializerOptions())); + Assert.Throws(() => JsonTypeInfo.CreateJsonTypeInfo(typeof(string), null)); + Assert.Throws(() => JsonTypeInfo.CreateJsonTypeInfo(null, null)); + Assert.Throws(() => JsonTypeInfo.CreateJsonTypeInfo(null)); + } + + [Theory] + [InlineData(typeof(void))] + [InlineData(typeof(Dictionary<,>))] + [InlineData(typeof(List<>))] + [InlineData(typeof(Nullable<>))] + [InlineData(typeof(int*))] + [InlineData(typeof(RefStruct))] + public static void CreateJsonTypeInfoWithInappropriateTypeThrows(Type type) + { + Assert.Throws(() => JsonTypeInfo.CreateJsonTypeInfo(type, new JsonSerializerOptions())); + } + + ref struct RefStruct + { + public int Foo { get; set; } + } + + [Fact] + public static void CreateJsonPropertyInfoWithNullArgumentsThrows() + { + JsonTypeInfo ti = JsonTypeInfo.CreateJsonTypeInfo(new JsonSerializerOptions()); + Assert.Throws(() => ti.CreateJsonPropertyInfo(null, "test")); + Assert.Throws(() => ti.CreateJsonPropertyInfo(typeof(string), null)); + Assert.Throws(() => ti.CreateJsonPropertyInfo(null, null)); + } + + [Theory] + [InlineData(typeof(void))] + [InlineData(typeof(Dictionary<,>))] + [InlineData(typeof(List<>))] + [InlineData(typeof(Nullable<>))] + [InlineData(typeof(int*))] + [InlineData(typeof(RefStruct))] + public static void CreateJsonPropertyInfoWithInappropriateTypeThrows(Type type) + { + JsonTypeInfo ti = JsonTypeInfo.CreateJsonTypeInfo(new JsonSerializerOptions()); + Assert.Throws(() => ti.CreateJsonPropertyInfo(type, "test")); + } + [Theory] [InlineData(typeof(object))] [InlineData(typeof(string))] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.cs index a1b1b3d2cc038..c9264f32535dd 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.cs @@ -178,6 +178,14 @@ public static void ModifiersAreCalledAndModifyTypeInfos() Assert.True(secondModifierCalled); } + [Fact] + public static void AddingNullModifierThrows() + { + DefaultJsonTypeInfoResolver r = new(); + Assert.Throws(() => r.Modifiers.Add(null)); + Assert.Throws(() => r.Modifiers.Insert(0, null)); + } + private static void InvokeGeneric(Type type, string methodName, params object[] args) { typeof(DefaultJsonTypeInfoResolverTests)