typeInfo.PopulatePolymorphismMetadata();
typeInfo.MapInterfaceTypesToCallbacks();
- Func<object>? createObject = MemberAccessor.CreateConstructor(typeInfo.Type);
+ Func<object>? createObject = DetermineCreateObjectDelegate(type, converter);
typeInfo.SetCreateObjectIfCompatible(createObject);
typeInfo.CreateObjectForExtensionDataProperty = createObject;
break;
}
}
+
+ [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
+ [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
+ private static Func<object>? DetermineCreateObjectDelegate(Type type, JsonConverter converter)
+ {
+ ConstructorInfo? defaultCtor = null;
+
+ if (converter.ConstructorInfo != null && !converter.ConstructorIsParameterized)
+ {
+ // A parameterless constructor has been resolved by the converter
+ // (e.g. it might be a non-public ctor with JsonConverterAttribute).
+ defaultCtor = converter.ConstructorInfo;
+ }
+
+ // Fall back to resolving any public constructors on the type.
+ defaultCtor ??= type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, binder: null, Type.EmptyTypes, modifiers: null);
+
+ return MemberAccessor.CreateParameterlessConstructor(type, defaultCtor);
+ }
}
}
{
internal abstract class MemberAccessor
{
- public abstract Func<object>? CreateConstructor(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type classType);
+ public abstract Func<object>? CreateParameterlessConstructor(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type,
+ ConstructorInfo? constructorInfo);
public abstract Func<object[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor);
=> s_cache.GetOrAdd((nameof(CreateAddMethodDelegate), typeof(TCollection), null),
static (_) => s_sourceAccessor.CreateAddMethodDelegate<TCollection>());
- public override Func<object>? CreateConstructor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type classType)
- => s_cache.GetOrAdd((nameof(CreateConstructor), classType, null),
+ public override Func<object>? CreateParameterlessConstructor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type, ConstructorInfo? ctorInfo)
+ => s_cache.GetOrAdd((nameof(CreateParameterlessConstructor), type, ctorInfo),
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2077:UnrecognizedReflectionPattern",
Justification = "Cannot apply DynamicallyAccessedMembersAttribute to tuple properties.")]
#pragma warning disable IL2077 // The suppression doesn't work for the trim analyzer: https://github.com/dotnet/roslyn/issues/59746
- static (key) => s_sourceAccessor.CreateConstructor(key.declaringType));
+ static (key) => s_sourceAccessor.CreateParameterlessConstructor(key.declaringType, (ConstructorInfo?)key.member));
#pragma warning restore IL2077
public override Func<object, TProperty> CreateFieldGetter<TProperty>(FieldInfo fieldInfo)
[RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
internal sealed class ReflectionEmitMemberAccessor : MemberAccessor
{
- public override Func<object>? CreateConstructor(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type)
+ public override Func<object>? CreateParameterlessConstructor(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type,
+ ConstructorInfo? constructorInfo)
{
Debug.Assert(type != null);
- ConstructorInfo? realMethod = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, binder: null, Type.EmptyTypes, modifiers: null);
+ Debug.Assert(constructorInfo is null || constructorInfo.GetParameters().Length == 0);
if (type.IsAbstract)
{
return null;
}
- if (realMethod == null && !type.IsValueType)
+ if (constructorInfo is null && !type.IsValueType)
{
return null;
}
ILGenerator generator = dynamicMethod.GetILGenerator();
- if (realMethod == null)
+ if (constructorInfo is null)
{
+ Debug.Assert(type.IsValueType);
+
LocalBuilder local = generator.DeclareLocal(type);
generator.Emit(OpCodes.Ldloca_S, local);
}
else
{
- generator.Emit(OpCodes.Newobj, realMethod);
+ generator.Emit(OpCodes.Newobj, constructorInfo);
if (type.IsValueType)
{
// Since C# 10 it's now possible to have parameterless constructors in structs
{
internal sealed class ReflectionMemberAccessor : MemberAccessor
{
- private sealed class ConstructorContext
- {
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
- private readonly Type _type;
-
- public ConstructorContext([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type)
- => _type = type;
-
- public object? CreateInstance()
- => Activator.CreateInstance(_type, nonPublic: false);
- }
-
- public override Func<object>? CreateConstructor(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type)
+ public override Func<object>? CreateParameterlessConstructor(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type,
+ ConstructorInfo? ctorInfo)
{
Debug.Assert(type != null);
- ConstructorInfo? realMethod = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, binder: null, Type.EmptyTypes, modifiers: null);
+ Debug.Assert(ctorInfo is null || ctorInfo.GetParameters().Length == 0);
if (type.IsAbstract)
{
return null;
}
- if (realMethod == null && !type.IsValueType)
+ if (ctorInfo is null)
{
- return null;
+ return type.IsValueType
+ ? () => Activator.CreateInstance(type, nonPublic: false)!
+ : null;
}
- return new ConstructorContext(type).CreateInstance!;
+ return () => ctorInfo.Invoke(null);
}
public override Func<object[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor)
}
}
+ [Theory]
+ [InlineData(typeof(PrivateParameterlessCtor_WithAttribute), false)]
+ [InlineData(typeof(InternalParameterlessCtor_WithAttribute), true)]
+ [InlineData(typeof(ProtectedParameterlessCtor_WithAttribute), false)]
+ public async Task NonPublicParameterlessCtors_WithJsonConstructorAttribute_WorksAsExpected(Type type, bool isAccessibleBySourceGen)
+ {
+ if (!Serializer.IsSourceGeneratedSerializer || isAccessibleBySourceGen)
+ {
+ object? result = await Serializer.DeserializeWrapper("{}", type);
+ Assert.IsType(type, result);
+ }
+ else
+ {
+ NotSupportedException ex = await Assert.ThrowsAsync<NotSupportedException>(() => Serializer.DeserializeWrapper("{}", type));
+ Assert.Contains("JsonConstructorAttribute", ex.ToString());
+ }
+ }
+
[Fact]
public async Task SinglePublicParameterizedCtor_SingleParameterlessCtor_NoAttribute_Supported_UseParameterlessCtor()
{
protected ProtectedParameterizedCtor_WithAttribute(int x) => X = x;
}
+ public class PrivateParameterlessCtor_WithAttribute
+ {
+ public int X { get; }
+
+ [JsonConstructor]
+ private PrivateParameterlessCtor_WithAttribute()
+ => X = 42;
+ }
+
+ public class ProtectedParameterlessCtor_WithAttribute
+ {
+ public int X { get; }
+
+ [JsonConstructor]
+ protected ProtectedParameterlessCtor_WithAttribute()
+ => X = 42;
+ }
+
+ public class InternalParameterlessCtor_WithAttribute
+ {
+ public int X { get; }
+
+ [JsonConstructor]
+ internal InternalParameterlessCtor_WithAttribute()
+ => X = 42;
+ }
+
public class PrivateParameterlessCtor_InternalParameterizedCtor_WithMultipleAttributes
{
[JsonConstructor]
[JsonSerializable(typeof(PrivateParameterizedCtor_WithAttribute))]
[JsonSerializable(typeof(InternalParameterizedCtor_WithAttribute))]
[JsonSerializable(typeof(ProtectedParameterizedCtor_WithAttribute))]
+ [JsonSerializable(typeof(PrivateParameterlessCtor_WithAttribute))]
+ [JsonSerializable(typeof(InternalParameterlessCtor_WithAttribute))]
+ [JsonSerializable(typeof(ProtectedParameterlessCtor_WithAttribute))]
[JsonSerializable(typeof(SinglePublicParameterizedCtor))]
[JsonSerializable(typeof(SingleParameterlessCtor_MultiplePublicParameterizedCtor))]
[JsonSerializable(typeof(SingleParameterlessCtor_MultiplePublicParameterizedCtor_Struct))]
[JsonSerializable(typeof(PrivateParameterizedCtor_WithAttribute))]
[JsonSerializable(typeof(InternalParameterizedCtor_WithAttribute))]
[JsonSerializable(typeof(ProtectedParameterizedCtor_WithAttribute))]
+ [JsonSerializable(typeof(PrivateParameterlessCtor_WithAttribute))]
+ [JsonSerializable(typeof(InternalParameterlessCtor_WithAttribute))]
+ [JsonSerializable(typeof(ProtectedParameterlessCtor_WithAttribute))]
[JsonSerializable(typeof(SinglePublicParameterizedCtor))]
[JsonSerializable(typeof(SingleParameterlessCtor_MultiplePublicParameterizedCtor))]
[JsonSerializable(typeof(SingleParameterlessCtor_MultiplePublicParameterizedCtor_Struct))]