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
1 change: 1 addition & 0 deletions src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ The System.Text.Json library is built-in as part of the shared framework in .NET
</PropertyGroup>

<ItemGroup>
<Compile Include="$(CommonPath)Polyfills\ReflectionPolyfills.cs" Link="Common\Polyfills\ReflectionPolyfills.cs" />
<Compile Include="$(CommonPath)System\HexConverter.cs" Link="Common\System\HexConverter.cs" />
<Compile Include="$(CommonPath)System\Net\ArrayBuffer.cs" Link="Common\System\Net\ArrayBuffer.cs" />
<Compile Include="$(CommonPath)System\Text\ValueStringBuilder.cs" Link="Common\ValueStringBuilder.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<JsonConstructorAttribute>() is not null;
=> constructorInfo.IsDefined(typeof(JsonConstructorAttribute), inherit: false);

public static bool HasRequiredMemberAttribute(this MemberInfo memberInfo)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ static MemberAccessor Initialize()

public abstract Func<object>? CreateParameterlessConstructor(Type type, ConstructorInfo? constructorInfo);

public abstract Func<object[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor);
public abstract Func<object?[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor);

public abstract JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3>? CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>(ConstructorInfo constructor);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerab
key: (nameof(CreateImmutableEnumerableCreateRangeDelegate), typeof((TCollection, TElement)), null),
valueFactory: _ => _sourceAccessor.CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>());

public override Func<object[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor) =>
public override Func<object?[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor) =>
_cache.GetOrAdd(
key: (nameof(CreateParameterizedConstructor), typeof(T), constructor),
valueFactory: key => _sourceAccessor.CreateParameterizedConstructor<T>((ConstructorInfo)key.member!));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ public ReflectionEmitMemberAccessor()
return CreateDelegate<Func<object>>(dynamicMethod);
}

public override Func<object[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor) =>
CreateDelegate<Func<object[], T>>(CreateParameterizedConstructor(constructor));
public override Func<object?[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor) =>
CreateDelegate<Func<object?[], T>>(CreateParameterizedConstructor(constructor));

private static DynamicMethod CreateParameterizedConstructor(ConstructorInfo constructor)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<object[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor)
public override Func<object?[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor)
{
Type type = typeof(T);

Expand All @@ -46,22 +51,24 @@ public override Func<object[], T> CreateParameterizedConstructor<T>(ConstructorI

int parameterCount = constructor.GetParameters().Length;

#if NET
ConstructorInvoker invoker = ConstructorInvoker.Create(constructor);
return arguments => (T)invoker.Invoke(arguments.AsSpan(0, parameterCount));
#else
Comment thread
eiriktsarpalis marked this conversation as resolved.
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<T, TArg0, TArg1, TArg2, TArg3>?
Expand All @@ -73,36 +80,55 @@ public override JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, T
Debug.Assert(constructor.DeclaringType == type && !constructor.IsStatic);

int parameterCount = constructor.GetParameters().Length;
#if NET
ConstructorInvoker invoker = ConstructorInvoker.Create(constructor);
#endif

Debug.Assert(parameterCount <= JsonConstants.UnboxedParameterCountThreshold);

return (arg0, arg1, arg2, arg3) =>
{
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
};
}

Expand All @@ -114,10 +140,12 @@ public override JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, T
Debug.Assert(constructor.DeclaringType == type && !constructor.IsStatic);
Debug.Assert(constructor.GetParameters().Length == 1);

return value =>
{
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<TCollection, object?> CreateAddMethodDelegate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TCollection>()
Expand All @@ -127,11 +155,12 @@ public override JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, T

// We verified this won't be null when we created the converter for the collection type.
MethodInfo addMethod = (collectionType.GetMethod("Push") ?? collectionType.GetMethod("Enqueue"))!;

return delegate (TCollection collection, object? element)
{
addMethod.InvokeNoWrapExceptions(collection, new object[] { element! });
};
#if NET
MethodInvoker invoker = MethodInvoker.Create(addMethod);
return (collection, element) => invoker.Invoke(collection, element);
#else
return (collection, element) => addMethod.InvokeNoWrapExceptions(collection, new object[] { element });
#endif
Comment thread
teo-tsirpanis marked this conversation as resolved.
}

public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>()
Expand All @@ -151,31 +180,41 @@ public override Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> Creat
public override Func<object, TProperty> CreatePropertyGetter<TProperty>(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
Comment thread
teo-tsirpanis marked this conversation as resolved.
}

private delegate TProperty ValueTypePropertyGetter<TProperty, TDeclaringType>(ref TDeclaringType obj);

public override Func<TDeclaringType, TProperty> CreatePropertyGetter<TDeclaringType, TProperty>(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<TProperty, TDeclaringType> f = getMethodInfo.CreateDelegate<ValueTypePropertyGetter<TProperty, TDeclaringType>>();
return obj => f(ref obj);
}

return getMethodInfo.CreateDelegate<Func<TDeclaringType, TProperty>>();
}

public override Action<object, TProperty> CreatePropertySetter<TProperty>(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
Comment thread
teo-tsirpanis marked this conversation as resolved.
}

public override Func<object, TProperty> CreateFieldGetter<TProperty>(FieldInfo fieldInfo) =>
Expand Down