diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
index b1fe30e7f4acee..3718cc53c4186c 100644
--- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
+++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
@@ -101,7 +101,7 @@ private sealed class Parser
// Unsupported types
private readonly Type _delegateType;
- private readonly Type _typeType;
+ private readonly Type _memberInfoType;
private readonly Type _serializationInfoType;
private readonly Type _intPtrType;
private readonly Type _uIntPtrType;
@@ -232,15 +232,15 @@ public Parser(Compilation compilation, in JsonSourceGenerationContext sourceGene
_jsonObjectType = _metadataLoadContext.Resolve(JsonObjectFullName);
_jsonValueType = _metadataLoadContext.Resolve(JsonValueFullName);
_jsonDocumentType = _metadataLoadContext.Resolve(JsonDocumentFullName);
+ _dateOnlyType = _metadataLoadContext.Resolve(DateOnlyFullName);
+ _timeOnlyType = _metadataLoadContext.Resolve(TimeOnlyFullName);
// Unsupported types.
_delegateType = _metadataLoadContext.Resolve(SpecialType.System_Delegate);
- _typeType = _metadataLoadContext.Resolve(typeof(Type));
+ _memberInfoType = _metadataLoadContext.Resolve(typeof(MemberInfo));
_serializationInfoType = _metadataLoadContext.Resolve(typeof(Runtime.Serialization.SerializationInfo));
_intPtrType = _metadataLoadContext.Resolve(typeof(IntPtr));
_uIntPtrType = _metadataLoadContext.Resolve(typeof(UIntPtr));
- _dateOnlyType = _metadataLoadContext.Resolve(DateOnlyFullName);
- _timeOnlyType = _metadataLoadContext.Resolve(TimeOnlyFullName);
_jsonConverterOfTType = _metadataLoadContext.Resolve(JsonConverterOfTFullName);
@@ -960,7 +960,10 @@ private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type, JsonSourceGener
}
}
}
- else if (_knownUnsupportedTypes.Contains(type) || _delegateType.IsAssignableFrom(type))
+ else if (
+ _knownUnsupportedTypes.Contains(type) ||
+ _memberInfoType.IsAssignableFrom(type) ||
+ _delegateType.IsAssignableFrom(type))
{
classType = ClassType.KnownUnsupportedType;
}
@@ -1590,7 +1593,6 @@ private void PopulateKnownTypes()
AddTypeIfNotNull(_knownTypes, _jsonValueType);
AddTypeIfNotNull(_knownTypes, _jsonDocumentType);
- _knownUnsupportedTypes.Add(_typeType);
_knownUnsupportedTypes.Add(_serializationInfoType);
_knownUnsupportedTypes.Add(_intPtrType);
_knownUnsupportedTypes.Add(_uIntPtrType);
diff --git a/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs b/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs
index 92dd222a5fecab..c34339e6ca153c 100644
--- a/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs
+++ b/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs
@@ -595,17 +595,11 @@ protected override bool IsPrimitiveImpl()
public override bool IsAssignableFrom(Type c)
{
- if (c is TypeWrapper tr)
- {
- return tr._typeSymbol.AllInterfaces.Contains(_typeSymbol, SymbolEqualityComparer.Default) ||
- (tr._namedTypeSymbol != null && tr._namedTypeSymbol.BaseTypes().Contains(_typeSymbol, SymbolEqualityComparer.Default));
- }
- else if (_metadataLoadContext.Resolve(c) is TypeWrapper trr)
- {
- return trr._typeSymbol.AllInterfaces.Contains(_typeSymbol, SymbolEqualityComparer.Default) ||
- (trr._namedTypeSymbol != null && trr._namedTypeSymbol.BaseTypes().Contains(_typeSymbol, SymbolEqualityComparer.Default));
- }
- return false;
+ TypeWrapper? tr = c as TypeWrapper ?? _metadataLoadContext.Resolve(c) as TypeWrapper;
+
+ return tr is not null &&
+ (tr._typeSymbol.AllInterfaces.Contains(_typeSymbol, SymbolEqualityComparer.Default) ||
+ (tr._namedTypeSymbol != null && tr._namedTypeSymbol.BaseTypes().Contains(_typeSymbol, SymbolEqualityComparer.Default)));
}
#pragma warning disable RS1024 // Compare symbols correctly
diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx
index 01d0af8f556137..46477c7f34e4f9 100644
--- a/src/libraries/System.Text.Json/src/Resources/Strings.resx
+++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx
@@ -518,6 +518,9 @@
The collection type '{0}' is abstract, an interface, or is read only, and could not be instantiated and populated.
+
+ The deserialization constructor for type '{0}' contains parameters with null names. This might happen because the parameter names have been trimmed by the linker. Consider using the source generated serializer instead.
+
'IgnoreNullValues' and 'DefaultIgnoreCondition' cannot both be set to non-default values.
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UnsupportedTypeConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UnsupportedTypeConverterFactory.cs
index 6265bec854fd75..cc39341a9c0a73 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UnsupportedTypeConverterFactory.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UnsupportedTypeConverterFactory.cs
@@ -15,8 +15,8 @@ public override bool CanConvert(Type type)
// If a type is added, also add to the SourceGeneration project.
return
- // There's no safe way to construct a Type from untrusted user input.
- typeof(Type).IsAssignableFrom(type) ||
+ // There's no safe way to construct a Type/MemberInfo from untrusted user input.
+ typeof(MemberInfo).IsAssignableFrom(type) ||
// (De)serialization of SerializationInfo is already disallowed due to Type being disallowed
// (the two ctors on SerializationInfo take a Type, and a Type member is present when serializing).
// Explicitly disallowing this type provides a clear exception when ctors with
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionJsonTypeInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionJsonTypeInfoOfT.cs
index f176e154011b85..fbcf67e202786e 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionJsonTypeInfoOfT.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionJsonTypeInfoOfT.cs
@@ -230,12 +230,8 @@ private static bool PropertyIsOverriddenAndIgnored(
internal override JsonParameterInfoValues[] GetParameterInfoValues()
{
- ParameterInfo[] parameters = Converter.ConstructorInfo!.GetParameters();
- return GetParameterInfoArray(parameters);
- }
-
- private static JsonParameterInfoValues[] GetParameterInfoArray(ParameterInfo[] parameters)
- {
+ Debug.Assert(Converter.ConstructorInfo != null);
+ ParameterInfo[] parameters = Converter.ConstructorInfo.GetParameters();
int parameterCount = parameters.Length;
JsonParameterInfoValues[] jsonParameters = new JsonParameterInfoValues[parameterCount];
@@ -243,9 +239,16 @@ private static JsonParameterInfoValues[] GetParameterInfoArray(ParameterInfo[] p
{
ParameterInfo reflectionInfo = parameters[i];
+ // Trimmed parameter names are reported as null in CoreCLR or "" in Mono.
+ if (string.IsNullOrEmpty(reflectionInfo.Name))
+ {
+ Debug.Assert(Converter.ConstructorInfo.DeclaringType != null);
+ ThrowHelper.ThrowNotSupportedException_BaseConverterDoesNotSupportMetadata(Converter.ConstructorInfo.DeclaringType);
+ }
+
JsonParameterInfoValues jsonInfo = new()
{
- Name = reflectionInfo.Name!,
+ Name = reflectionInfo.Name,
ParameterType = reflectionInfo.ParameterType,
Position = reflectionInfo.Position,
HasDefaultValue = reflectionInfo.HasDefaultValue,
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 22d33955c81299..cd8c980162129c 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
@@ -709,6 +709,13 @@ public static void ThrowNotSupportedException_NoMetadataForType(Type type, IJson
throw new NotSupportedException(SR.Format(SR.NoMetadataForType, type, resolver?.GetType().FullName ?? ""));
}
+
+ [DoesNotReturn]
+ public static void ThrowNotSupportedException_ConstructorContainsNullParameterNames(Type declaringType)
+ {
+ throw new NotSupportedException(SR.Format(SR.ConstructorContainsNullParameterNames, declaringType));
+ }
+
[DoesNotReturn]
public static void ThrowInvalidOperationException_NoMetadataForType(Type type, IJsonTypeInfoResolver? resolver)
{
diff --git a/src/libraries/System.Text.Json/tests/Common/UnsupportedTypesTests.cs b/src/libraries/System.Text.Json/tests/Common/UnsupportedTypesTests.cs
index 738f3397bcdf99..0c4566c529d16e 100644
--- a/src/libraries/System.Text.Json/tests/Common/UnsupportedTypesTests.cs
+++ b/src/libraries/System.Text.Json/tests/Common/UnsupportedTypesTests.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
+using System.Reflection;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
@@ -20,110 +21,117 @@ public UnsupportedTypesTests(
SupportsJsonPathOnSerialize = supportsJsonPathOnSerialize;
}
- [Fact]
- public async Task DeserializeUnsupportedType()
+ [Theory]
+ [MemberData(nameof(GetUnsupportedValues))]
+ public async Task DeserializeUnsupportedType(ValueWrapper wrapper)
{
- // Any test payload is fine.
- string json = @"""Some string""";
+ _ = wrapper; // only used to instantiate T
- await RunTest(json);
- await RunTest(json);
- await RunTest(json);
- await RunTest(json); // One nullable variation.
- await RunTest(json);
+ string json = @"""Some string"""; // Any test payload is fine.
- async Task RunTest(string json)
- {
- Type type = GetNullableOfTUnderlyingType(typeof(T), out bool isNullableOfT);
- string fullName = type.FullName;
+ Type type = GetNullableOfTUnderlyingType(typeof(T), out bool isNullableOfT);
+ string fullName = type.FullName;
- NotSupportedException ex = await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper(json));
- string exAsStr = ex.ToString();
- Assert.Contains(fullName, exAsStr);
- Assert.Contains("$", exAsStr);
+ NotSupportedException ex = await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper(json));
+ string exAsStr = ex.ToString();
+ Assert.Contains(fullName, exAsStr);
+ Assert.Contains("$", exAsStr);
- json = $@"{{""Prop"":{json}}}";
+ json = $@"{{""Prop"":{json}}}";
- ex = await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper>(json));
- exAsStr = ex.ToString();
- Assert.Contains(fullName, exAsStr);
- Assert.Contains("$.Prop", exAsStr);
+ ex = await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper>(json));
+ exAsStr = ex.ToString();
+ Assert.Contains(fullName, exAsStr);
+ Assert.Contains("$.Prop", exAsStr);
- // Verify Nullable<> semantics. NSE is not thrown because the serializer handles null.
- if (isNullableOfT)
- {
- Assert.Null(JsonSerializer.Deserialize("null"));
+ // Verify Nullable<> semantics. NSE is not thrown because the serializer handles null.
+ if (isNullableOfT)
+ {
+ Assert.Null(JsonSerializer.Deserialize("null"));
- json = $@"{{""Prop"":null}}";
- ClassWithType obj = await Serializer.DeserializeWrapper>(json);
- Assert.Null(obj.Prop);
- }
+ json = $@"{{""Prop"":null}}";
+ ClassWithType obj = await Serializer.DeserializeWrapper>(json);
+ Assert.Null(obj.Prop);
}
}
- [Fact]
- public async Task SerializeUnsupportedType()
+ [Theory]
+ [MemberData(nameof(GetUnsupportedValues))]
+ public async Task SerializeUnsupportedType(ValueWrapper wrapper)
{
- // TODO refactor to Xunit theory
- await RunTest(typeof(int));
- await RunTest(new SerializationInfo(typeof(Type), new FormatterConverter()));
- await RunTest((IntPtr)123);
- await RunTest(new IntPtr(123)); // One nullable variation.
- await RunTest((UIntPtr)123);
-
- async Task RunTest(T value)
+ T value = wrapper.value;
+
+ Type type = GetNullableOfTUnderlyingType(typeof(T), out bool isNullableOfT);
+ string fullName = type.FullName;
+
+ NotSupportedException ex = await Assert.ThrowsAsync(async () => await Serializer.SerializeWrapper(value));
+ string exAsStr = ex.ToString();
+ Assert.Contains(fullName, exAsStr);
+ Assert.Contains("$", exAsStr);
+
+ ClassWithType obj = new ClassWithType { Prop = value };
+ ex = await Assert.ThrowsAsync(async () => await Serializer.SerializeWrapper(obj));
+ exAsStr = ex.ToString();
+ Assert.Contains(fullName, exAsStr);
+
+ if (SupportsJsonPathOnSerialize)
+ {
+ Assert.Contains("$.Prop", exAsStr);
+ }
+ else
{
- Type type = GetNullableOfTUnderlyingType(typeof(T), out bool isNullableOfT);
- string fullName = type.FullName;
-
- NotSupportedException ex = await Assert.ThrowsAsync(async () => await Serializer.SerializeWrapper(value));
- string exAsStr = ex.ToString();
- Assert.Contains(fullName, exAsStr);
- Assert.Contains("$", exAsStr);
-
- ClassWithType obj = new ClassWithType { Prop = value };
- ex = await Assert.ThrowsAsync(async () => await Serializer.SerializeWrapper(obj));
- exAsStr = ex.ToString();
- Assert.Contains(fullName, exAsStr);
-
- if (SupportsJsonPathOnSerialize)
- {
- Assert.Contains("$.Prop", exAsStr);
- }
- else
- {
- Assert.Contains("$.", exAsStr);
- Assert.DoesNotContain("$.Prop", exAsStr);
- }
-
- // Verify null semantics. NSE is not thrown because the serializer handles null.
- if (!type.IsValueType || isNullableOfT)
- {
- string serialized = await Serializer.SerializeWrapper((T)(object)null);
- Assert.Equal("null", serialized);
-
- obj.Prop = (T)(object)null;
- serialized = await Serializer.SerializeWrapper(obj);
- Assert.Equal(@"{""Prop"":null}", serialized);
-
- serialized = await Serializer.SerializeWrapper(obj, new JsonSerializerOptions { IgnoreNullValues = true });
- Assert.Equal(@"{}", serialized);
- }
+ Assert.Contains("$.", exAsStr);
+ Assert.DoesNotContain("$.Prop", exAsStr);
+ }
+
+ // Verify null semantics. NSE is not thrown because the serializer handles null.
+ if (!type.IsValueType || isNullableOfT)
+ {
+ string serialized = await Serializer.SerializeWrapper((T)(object)null);
+ Assert.Equal("null", serialized);
+
+ obj.Prop = (T)(object)null;
+ serialized = await Serializer.SerializeWrapper(obj);
+ Assert.Equal(@"{""Prop"":null}", serialized);
+
+ serialized = await Serializer.SerializeWrapper(obj, new JsonSerializerOptions { IgnoreNullValues = true });
+ Assert.Equal(@"{}", serialized);
+ }
#if !BUILDING_SOURCE_GENERATOR_TESTS
- Type runtimeType = GetNullableOfTUnderlyingType(value.GetType(), out bool _);
+ Type runtimeType = GetNullableOfTUnderlyingType(value.GetType(), out bool _);
- ex = await Assert.ThrowsAsync(async () => await Serializer.SerializeWrapper