diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 0a80a0e73974c8..b588e933c2acba 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -21,6 +21,7 @@ The System.Text.Json library is built-in as part of the shared framework in .NET + diff --git a/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs b/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs index a8acfb552f2f99..9e7d70d5d2e931 100644 --- a/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs +++ b/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs @@ -45,7 +45,7 @@ public static bool IsInSubtypeRelationshipWith(this Type type, Type other) => type.IsAssignableFromInternal(other) || other.IsAssignableFromInternal(type); private static bool HasJsonConstructorAttribute(ConstructorInfo constructorInfo) - => constructorInfo.GetCustomAttribute() is not null; + => constructorInfo.IsDefined(typeof(JsonConstructorAttribute), inherit: false); public static bool HasRequiredMemberAttribute(this MemberInfo memberInfo) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/MemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/MemberAccessor.cs index 248a560cd58ce6..6695b503e09fd5 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/MemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/MemberAccessor.cs @@ -49,7 +49,7 @@ static MemberAccessor Initialize() public abstract Func? CreateParameterlessConstructor(Type type, ConstructorInfo? constructorInfo); - public abstract Func CreateParameterizedConstructor(ConstructorInfo constructor); + public abstract Func CreateParameterizedConstructor(ConstructorInfo constructor); public abstract JsonTypeInfo.ParameterizedConstructorDelegate? CreateParameterizedConstructor(ConstructorInfo constructor); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitCachingMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitCachingMemberAccessor.cs index 099c1fb0f93e54..0d80689e660a69 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitCachingMemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitCachingMemberAccessor.cs @@ -53,7 +53,7 @@ public override Func, TCollection> CreateImmutableEnumerab key: (nameof(CreateImmutableEnumerableCreateRangeDelegate), typeof((TCollection, TElement)), null), valueFactory: _ => _sourceAccessor.CreateImmutableEnumerableCreateRangeDelegate()); - public override Func CreateParameterizedConstructor(ConstructorInfo constructor) => + public override Func CreateParameterizedConstructor(ConstructorInfo constructor) => _cache.GetOrAdd( key: (nameof(CreateParameterizedConstructor), typeof(T), constructor), valueFactory: key => _sourceAccessor.CreateParameterizedConstructor((ConstructorInfo)key.member!)); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitMemberAccessor.cs index 5a9fde9c017305..836954b24872a3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitMemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitMemberAccessor.cs @@ -68,8 +68,8 @@ public ReflectionEmitMemberAccessor() return CreateDelegate>(dynamicMethod); } - public override Func CreateParameterizedConstructor(ConstructorInfo constructor) => - CreateDelegate>(CreateParameterizedConstructor(constructor)); + public override Func CreateParameterizedConstructor(ConstructorInfo constructor) => + CreateDelegate>(CreateParameterizedConstructor(constructor)); private static DynamicMethod CreateParameterizedConstructor(ConstructorInfo constructor) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs index b0d9f2503f0840..775cb1323b07c3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs @@ -34,10 +34,15 @@ public ReflectionMemberAccessor() : null; } +#if NET + ConstructorInvoker invoker = ConstructorInvoker.Create(ctorInfo); + return invoker.Invoke; +#else return () => ctorInfo.InvokeNoWrapExceptions(null); +#endif } - public override Func CreateParameterizedConstructor(ConstructorInfo constructor) + public override Func CreateParameterizedConstructor(ConstructorInfo constructor) { Type type = typeof(T); @@ -46,22 +51,24 @@ public override Func CreateParameterizedConstructor(ConstructorI int parameterCount = constructor.GetParameters().Length; +#if NET + ConstructorInvoker invoker = ConstructorInvoker.Create(constructor); + return arguments => (T)invoker.Invoke(arguments.AsSpan(0, parameterCount)); +#else return (arguments) => { // The input array was rented from the shared ArrayPool, so its size is likely to be larger than the param count. // The emit equivalent of this method does not (need to) allocate here + transfer the objects. object[] argsToPass = new object[parameterCount]; - for (int i = 0; i < parameterCount; i++) - { - argsToPass[i] = arguments[i]; - } + Array.Copy(arguments, 0, argsToPass, 0, parameterCount); // Not wrapping in TargetInvocationException also plumbs ArgumentException through for // tuples with more than 7 generic parameters, e.g. // System.ArgumentException : The last element of an eight element tuple must be a Tuple. return (T)constructor.InvokeNoWrapExceptions(argsToPass); }; +#endif } public override JsonTypeInfo.ParameterizedConstructorDelegate? @@ -73,36 +80,55 @@ public override JsonTypeInfo.ParameterizedConstructorDelegate { - object[] arguments = new object[parameterCount]; +#if NET + switch (parameterCount) + { + case 0: + return (T)invoker.Invoke(); + case 1: + return (T)invoker.Invoke(arg0); + case 2: + return (T)invoker.Invoke(arg0, arg1); + case 3: + return (T)invoker.Invoke(arg0, arg1, arg2); + case 4: + return (T)invoker.Invoke(arg0, arg1, arg2, arg3); + default: + Debug.Fail("We shouldn't be here if there are more than 4 parameters."); + throw new InvalidOperationException(); + } +#else + object?[] arguments = new object?[parameterCount]; - for (int i = 0; i < parameterCount; i++) + switch (parameterCount) { - switch (i) - { - case 0: - arguments[0] = arg0!; - break; - case 1: - arguments[1] = arg1!; - break; - case 2: - arguments[2] = arg2!; - break; - case 3: - arguments[3] = arg3!; - break; - default: - Debug.Fail("We shouldn't be here if there are more than 4 parameters."); - throw new InvalidOperationException(); - } + case > 4: + Debug.Fail("We shouldn't be here if there are more than 4 parameters."); + throw new InvalidOperationException(); + case 4: + arguments[3] = arg3; + goto case 3; + case 3: + arguments[2] = arg2; + goto case 2; + case 2: + arguments[1] = arg1; + goto case 1; + case 1: + arguments[0] = arg0; + break; } return (T)constructor.InvokeNoWrapExceptions(arguments); +#endif }; } @@ -114,10 +140,12 @@ public override JsonTypeInfo.ParameterizedConstructorDelegate - { - return (T)constructor.InvokeNoWrapExceptions(new object?[] { value }); - }; +#if NET + ConstructorInvoker invoker = ConstructorInvoker.Create(constructor); + return value => (T)invoker.Invoke(value); +#else + return value => (T)constructor.InvokeNoWrapExceptions(new object?[] { value }); +#endif } public override Action CreateAddMethodDelegate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TCollection>() @@ -127,11 +155,12 @@ public override JsonTypeInfo.ParameterizedConstructorDelegate invoker.Invoke(collection, element); +#else + return (collection, element) => addMethod.InvokeNoWrapExceptions(collection, new object[] { element }); +#endif } public override Func, TCollection> CreateImmutableEnumerableCreateRangeDelegate() @@ -151,31 +180,41 @@ public override Func>, TCollection> Creat public override Func CreatePropertyGetter(PropertyInfo propertyInfo) { MethodInfo getMethodInfo = propertyInfo.GetMethod!; - - return delegate (object obj) - { - return (TProperty)getMethodInfo.InvokeNoWrapExceptions(obj, null)!; - }; +#if NET + MethodInvoker invoker = MethodInvoker.Create(getMethodInfo); + return obj => (TProperty)invoker.Invoke(obj)!; +#else + return obj => (TProperty)getMethodInfo.InvokeNoWrapExceptions(obj, null)!; +#endif } + private delegate TProperty ValueTypePropertyGetter(ref TDeclaringType obj); + public override Func CreatePropertyGetter(PropertyInfo propertyInfo) { MethodInfo getMethodInfo = propertyInfo.GetMethod!; - return delegate (TDeclaringType obj) + // If TDeclaringType is a value type, the instance is passed by reference, + // so we need to create a wrapper delegate to account for this. + if (typeof(TDeclaringType).IsValueType) { - return (TProperty)getMethodInfo.InvokeNoWrapExceptions(obj, null)!; - }; + ValueTypePropertyGetter f = getMethodInfo.CreateDelegate>(); + return obj => f(ref obj); + } + + return getMethodInfo.CreateDelegate>(); } public override Action CreatePropertySetter(PropertyInfo propertyInfo) { MethodInfo setMethodInfo = propertyInfo.SetMethod!; - return delegate (object obj, TProperty value) - { - setMethodInfo.InvokeNoWrapExceptions(obj, new object[] { value! }); - }; +#if NET + MethodInvoker invoker = MethodInvoker.Create(setMethodInfo); + return (obj, value) => invoker.Invoke(obj, value); +#else + return (obj, value) => setMethodInfo.InvokeNoWrapExceptions(obj, new object[] { value }); +#endif } public override Func CreateFieldGetter(FieldInfo fieldInfo) =>