Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions src/libraries/System.Text.Json/gen/ClassType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@ namespace System.Text.Json.SourceGeneration
{
internal enum ClassType
{
/// <summary>
/// Types that are not supported yet by source gen including types with constructor parameters.
/// </summary>
TypeUnsupportedBySourceGen = 0,
Object = 1,
KnownType = 2,
TypeWithDesignTimeProvidedCustomConverter = 3,
Enumerable = 4,
Dictionary = 5,
Nullable = 6,
Enum = 7
/// <summary>
/// Known types such as System.Type and System.IntPtr that throw NotSupportedException.
/// </summary>
KnownUnsupportedType = 3,
TypeWithDesignTimeProvidedCustomConverter = 4,
Enumerable = 5,
Dictionary = 6,
Nullable = 7,
Enum = 8
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ private sealed partial class Emitter
private const string JsonParameterInfoValuesTypeRef = "global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues";
private const string JsonPropertyInfoTypeRef = "global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo";
private const string JsonTypeInfoTypeRef = "global::System.Text.Json.Serialization.Metadata.JsonTypeInfo";
private const string NotSupportedExceptionTypeRef = "global::System.NotSupportedException";

private static DiagnosticDescriptor TypeNotSupported { get; } = new DiagnosticDescriptor(
id: "SYSLIB1030",
Expand Down Expand Up @@ -254,6 +255,11 @@ private void GenerateTypeInfo(TypeGenerationSpec typeGenerationSpec)
}
}
break;
case ClassType.KnownUnsupportedType:
{
source = GenerateForUnsupportedType(typeGenerationSpec);
}
break;
case ClassType.TypeUnsupportedBySourceGen:
{
_sourceProductionContext.ReportDiagnostic(
Expand Down Expand Up @@ -353,6 +359,16 @@ private string GenerateForNullable(TypeGenerationSpec typeMetadata)
return GenerateForType(typeMetadata, metadataInitSource);
}

private string GenerateForUnsupportedType(TypeGenerationSpec typeMetadata)
{
string typeCompilableName = typeMetadata.TypeRef;
string typeFriendlyName = typeMetadata.TypeInfoPropertyName;

string metadataInitSource = $"_{typeFriendlyName} = {JsonMetadataServicesTypeRef}.{GetCreateValueInfoMethodRef(typeCompilableName)}({OptionsInstanceVariableName}, {JsonMetadataServicesTypeRef}.GetUnsupportedTypeConverter<{typeCompilableName}>());";

return GenerateForType(typeMetadata, metadataInitSource);
}

private string GenerateForEnum(TypeGenerationSpec typeMetadata)
{
string typeCompilableName = typeMetadata.TypeRef;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ private sealed class Parser
private const string JsonSerializerAttributeFullName = "System.Text.Json.Serialization.JsonSerializableAttribute";
private const string JsonSourceGenerationOptionsAttributeFullName = "System.Text.Json.Serialization.JsonSourceGenerationOptionsAttribute";

private const string DateOnlyFullName = "System.DateOnly";
private const string TimeOnlyFullName = "System.TimeOnly";
private const string IAsyncEnumerableFullName = "System.Collections.Generic.IAsyncEnumerable`1";

private readonly Compilation _compilation;
private readonly SourceProductionContext _sourceProductionContext;
private readonly MetadataLoadContextInternal _metadataLoadContext;
Expand Down Expand Up @@ -74,8 +78,20 @@ private sealed class Parser
private readonly Type? _versionType;
private readonly Type? _jsonElementType;

// Unsupported types
private readonly Type _typeType;
private readonly Type _serializationInfoType;
private readonly Type _intPtrType;
private readonly Type _uIntPtrType;

// Unsupported types that may not resolve
private readonly Type? _iAsyncEnumerableGenericType;
private readonly Type? _dateOnlyType;
private readonly Type? _timeOnlyType;

private readonly HashSet<Type> _numberTypes = new();
private readonly HashSet<Type> _knownTypes = new();
private readonly HashSet<Type> _knownUnsupportedTypes = new();

/// <summary>
/// Type information for member types in input object graphs.
Expand Down Expand Up @@ -142,6 +158,15 @@ public Parser(Compilation compilation, in SourceProductionContext sourceProducti
_versionType = _metadataLoadContext.Resolve(typeof(Version));
_jsonElementType = _metadataLoadContext.Resolve(JsonElementFullName);

// Unsupported types.
_typeType = _metadataLoadContext.Resolve(typeof(Type));
_serializationInfoType = _metadataLoadContext.Resolve(typeof(Runtime.Serialization.SerializationInfo));
_intPtrType = _metadataLoadContext.Resolve(typeof(IntPtr));
_uIntPtrType = _metadataLoadContext.Resolve(typeof(UIntPtr));
_iAsyncEnumerableGenericType = _metadataLoadContext.Resolve(IAsyncEnumerableFullName);
_dateOnlyType = _metadataLoadContext.Resolve(DateOnlyFullName);
_timeOnlyType = _metadataLoadContext.Resolve(TimeOnlyFullName);

PopulateKnownTypes();
}

Expand Down Expand Up @@ -765,6 +790,11 @@ private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type, JsonSourceGener
collectionValueType = _objectType;
}
}
else if (_knownUnsupportedTypes.Contains(type) ||
ImplementsIAsyncEnumerableInterface(type))
{
classType = ClassType.KnownUnsupportedType;
}
else
{
bool useDefaultCtorInAnnotatedStructs = !type.IsKeyValuePair(_keyValuePair);
Expand Down Expand Up @@ -893,6 +923,16 @@ void CacheMemberHelper()
return typeMetadata;
}

private bool ImplementsIAsyncEnumerableInterface(Type type)
{
if (_iAsyncEnumerableGenericType == null)
{
return false;
}

return type.GetCompatibleGenericInterface(_iAsyncEnumerableGenericType) is not null;
}

private Type GetCompatibleGenericBaseClass(Type type, Type baseType)
=> type.GetCompatibleGenericBaseClass(baseType, _objectType);

Expand Down Expand Up @@ -1268,8 +1308,10 @@ private void PopulateNumberTypes()
private void PopulateKnownTypes()
{
PopulateNumberTypes();

Debug.Assert(_knownTypes != null);
Debug.Assert(_numberTypes != null);
Debug.Assert(_knownUnsupportedTypes != null);

_knownTypes.UnionWith(_numberTypes);
_knownTypes.Add(_booleanType);
Expand All @@ -1283,6 +1325,21 @@ private void PopulateKnownTypes()
_knownTypes.Add(_uriType);
_knownTypes.Add(_versionType);
_knownTypes.Add(_jsonElementType);

_knownUnsupportedTypes.Add(_typeType);
_knownUnsupportedTypes.Add(_serializationInfoType);
_knownUnsupportedTypes.Add(_intPtrType);
_knownUnsupportedTypes.Add(_uIntPtrType);

if (_dateOnlyType != null)
{
_knownUnsupportedTypes.Add(_dateOnlyType);
}

if (_timeOnlyType != null)
{
_knownUnsupportedTypes.Add(_timeOnlyType);
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/libraries/System.Text.Json/ref/System.Text.Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,7 @@ public static partial class JsonMetadataServices
public static JsonTypeInfo<TCollection> CreateStackInfo<TCollection, TElement>(System.Text.Json.JsonSerializerOptions options, System.Func<TCollection>? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action<Utf8JsonWriter, TCollection>? serializeFunc) where TCollection : System.Collections.Generic.Stack<TElement> { throw null; }
public static JsonTypeInfo<TCollection> CreateStackOrQueueInfo<TCollection>(System.Text.Json.JsonSerializerOptions options, System.Func<TCollection>? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action<Utf8JsonWriter, TCollection>? serializeFunc, System.Action<TCollection, object?> addFunc) where TCollection : System.Collections.IEnumerable { throw null; }
public static System.Text.Json.Serialization.Metadata.JsonTypeInfo<T> CreateValueInfo<T>(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.JsonConverter converter) { throw null; }
public static System.Text.Json.Serialization.JsonConverter<T> GetUnsupportedTypeConverter<T>() { throw null; }
public static System.Text.Json.Serialization.JsonConverter<T> GetEnumConverter<T>(System.Text.Json.JsonSerializerOptions options) where T : struct { throw null; }
public static System.Text.Json.Serialization.JsonConverter<T?> GetNullableConverter<T>(System.Text.Json.Serialization.Metadata.JsonTypeInfo<T> underlyingTypeInfo) where T : struct { throw null; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@
<value>The type '{0}' is invalid for serialization or deserialization because it is a pointer type, is a ref struct, or contains generic parameters that have not been replaced by specific types.</value>
</data>
<data name="SerializeTypeInstanceNotSupported" xml:space="preserve">
<value>Serialization and deserialization of '{0}' instances are not supported and should be avoided since they can lead to security issues.</value>
<value>Serialization and deserialization of '{0}' instances are not supported.</value>
</data>
<data name="JsonIncludeOnNonPublicInvalid" xml:space="preserve">
<value>The non-public property '{0}' on type '{1}' is annotated with 'JsonIncludeAttribute' which is invalid.</value>
Expand Down
4 changes: 2 additions & 2 deletions src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,6 @@ System.Text.Json.Utf8JsonReader</PackageDescription>
<Compile Include="System\Text\Json\Serialization\Converters\Value\DateTimeConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Value\DateTimeOffsetConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Value\DecimalConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Value\DisallowedTypeConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Value\DisallowedTypeConverterFactory.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Value\DoubleConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Value\EnumConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Value\EnumConverterFactory.cs" />
Expand All @@ -201,6 +199,8 @@ System.Text.Json.Utf8JsonReader</PackageDescription>
<Compile Include="System\Text\Json\Serialization\Converters\Value\UInt16Converter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Value\UInt32Converter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Value\UInt64Converter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Value\UnsupportedTypeConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Value\UnsupportedTypeConverterFactory.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Value\UriConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Value\VersionConverter.cs" />
<Compile Include="System\Text\Json\Serialization\IgnoreReferenceHandler.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace System.Text.Json.Serialization.Converters
{
internal sealed class DisallowedTypeConverter<T> : JsonConverter<T>
internal sealed class UnsupportedTypeConverter<T> : JsonConverter<T>
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
throw new NotSupportedException(SR.Format(SR.SerializeTypeInstanceNotSupported, typeof(T).FullName));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@

namespace System.Text.Json.Serialization.Converters
{
internal sealed class DisallowedTypeConverterFactory : JsonConverterFactory
internal sealed class UnsupportedTypeConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type type)
{
// If a value type is added, also add a test that
// shows NSE is thrown when Nullable<T> is (de)serialized.
// 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.
Expand Down Expand Up @@ -42,7 +41,7 @@ public override bool CanConvert(Type type)
public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options)
{
JsonConverter converter = (JsonConverter)Activator.CreateInstance(
typeof(DisallowedTypeConverter<>).MakeGenericType(type),
typeof(UnsupportedTypeConverter<>).MakeGenericType(type),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
args: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ private void RootBuiltInConverters()
s_defaultFactoryConverters = new JsonConverter[]
{
// Check for disallowed types.
new DisallowedTypeConverterFactory(),
new UnsupportedTypeConverterFactory(),
// Nullable converter should always be next since it forwards to any nullable type.
new NullableConverterFactory(),
new EnumConverterFactory(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ public static partial class JsonMetadataServices
public static JsonConverter<Version> VersionConverter => s_versionConverter ??= new VersionConverter();
private static JsonConverter<Version>? s_versionConverter;

/// <summary>
/// Creates a <see cref="JsonConverter{T}"/> instance that throws <see cref="NotSupportedException"/>.
/// </summary>
/// <typeparam name="T">The generic definition for the type.</typeparam>
/// <returns></returns>
public static JsonConverter<T> GetUnsupportedTypeConverter<T>()
=> new UnsupportedTypeConverter<T>();

/// <summary>
/// Creates a <see cref="JsonConverter{T}"/> instance that converts <typeparamref name="T"/> values.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ public string PropertyPath()
static void AppendStackFrame(StringBuilder sb, ref WriteStackFrame frame)
{
// Append the property name.
string? propertyName = frame.DeclaredJsonPropertyInfo?.MemberInfo?.Name;
string? propertyName = frame.DeclaredJsonPropertyInfo?.ClrName;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a functional change for the non-generator scenario?

if (propertyName == null)
{
// Attempt to get the JSON property name from the property name specified in re-entry.
Expand Down
Loading