bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
StackAllocedArguments stackArgs = default;
- Span<object?> arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig);
+ Span<object?> arguments = default;
+ if (actualCount != 0)
+ {
+ arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig);
+ }
+
object? retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, false, wrapExceptions);
// copy out. This should be made only if ByRef are present.
return parameterTypes;
}
- private protected Span<object?> CheckArguments(ref StackAllocedArguments stackArgs, object?[]? parameters, Binder? binder,
+ private protected Span<object?> CheckArguments(ref StackAllocedArguments stackArgs, ReadOnlySpan<object?> parameters, Binder? binder,
BindingFlags invokeAttr, CultureInfo? culture, Signature sig)
{
Debug.Assert(Unsafe.SizeOf<StackAllocedArguments>() == StackAllocedArguments.MaxStackAllocArgCount * Unsafe.SizeOf<object>(),
"MaxStackAllocArgCount not properly defined.");
+ Debug.Assert(!parameters.IsEmpty);
- Span<object?> copyOfParameters = default;
+ // We need to perform type safety validation against the incoming arguments, but we also need
+ // to be resilient against the possibility that some other thread (or even the binder itself!)
+ // may mutate the array after we've validated the arguments but before we've properly invoked
+ // the method. The solution is to copy the arguments to a different, not-user-visible buffer
+ // as we validate them. n.b. This disallows use of ArrayPool, as ArrayPool-rented arrays are
+ // considered user-visible to threads which may still be holding on to returned instances.
- if (parameters is not null)
+ Span<object?> copyOfParameters = (parameters.Length <= StackAllocedArguments.MaxStackAllocArgCount)
+ ? MemoryMarshal.CreateSpan(ref stackArgs._arg0, parameters.Length)
+ : new Span<object?>(new object?[parameters.Length]);
+
+ ParameterInfo[]? p = null;
+ for (int i = 0; i < parameters.Length; i++)
{
- // We need to perform type safety validation against the incoming arguments, but we also need
- // to be resilient against the possibility that some other thread (or even the binder itself!)
- // may mutate the array after we've validated the arguments but before we've properly invoked
- // the method. The solution is to copy the arguments to a different, not-user-visible buffer
- // as we validate them. n.b. This disallows use of ArrayPool, as ArrayPool-rented arrays are
- // considered user-visible to threads which may still be holding on to returned instances.
-
- copyOfParameters = (parameters.Length <= StackAllocedArguments.MaxStackAllocArgCount)
- ? MemoryMarshal.CreateSpan(ref stackArgs._arg0, parameters.Length)
- : new Span<object?>(new object?[parameters.Length]);
-
- ParameterInfo[]? p = null;
- for (int i = 0; i < parameters.Length; i++)
+ object? arg = parameters[i];
+ RuntimeType argRT = sig.Arguments[i];
+
+ if (arg == Type.Missing)
{
- object? arg = parameters[i];
- RuntimeType argRT = sig.Arguments[i];
-
- if (arg == Type.Missing)
- {
- p ??= GetParametersNoCopy();
- if (p[i].DefaultValue == System.DBNull.Value)
- throw new ArgumentException(SR.Arg_VarMissNull, nameof(parameters));
- arg = p[i].DefaultValue!;
- }
- copyOfParameters[i] = argRT.CheckValue(arg, binder, culture, invokeAttr);
+ p ??= GetParametersNoCopy();
+ if (p[i].DefaultValue == System.DBNull.Value)
+ throw new ArgumentException(SR.Arg_VarMissNull, nameof(parameters));
+ arg = p[i].DefaultValue!;
}
+ copyOfParameters[i] = argRT.CheckValue(arg, binder, culture, invokeAttr);
}
return copyOfParameters;
bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
StackAllocedArguments stackArgs = default;
- Span<object?> arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig);
+ Span<object?> arguments = default;
+ if (actualCount != 0)
+ {
+ arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig);
+ }
+
object? retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, sig, false, wrapExceptions);
// copy out. This should be made only if ByRef are present.
bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
StackAllocedArguments stackArgs = default;
- Span<object?> arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig);
+ Span<object?> arguments = default;
+ if (actualCount != 0)
+ {
+ arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig);
+ }
+
object retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, true, wrapExceptions)!; // ctor must return non-null
// copy out. This should be made only if ByRef are present.
[Diagnostics.DebuggerHidden]
public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture)
{
+ // INVOCATION_FLAGS_CONTAINS_STACK_POINTERS means that the struct (either the declaring type or the return type)
+ // contains pointers that point to the stack. This is either a ByRef or a TypedReference. These structs cannot
+ // be boxed and thus cannot be invoked through reflection which only deals with boxed value type objects.
+ if ((InvocationFlags & (INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE | INVOCATION_FLAGS.INVOCATION_FLAGS_CONTAINS_STACK_POINTERS)) != 0)
+ ThrowNoInvokeException();
+
+ // check basic method consistency. This call will throw if there are problems in the target/method relationship
+ CheckConsistency(obj);
+
+ Signature sig = Signature;
+ int actualCount = (parameters != null) ? parameters.Length : 0;
+ if (sig.Arguments.Length != actualCount)
+ throw new TargetParameterCountException(SR.Arg_ParmCnt);
+
StackAllocedArguments stackArgs = default; // try to avoid intermediate array allocation if possible
- Span<object?> arguments = InvokeArgumentsCheck(ref stackArgs, obj, invokeAttr, binder, parameters, culture);
+ Span<object?> arguments = default;
+ if (actualCount != 0)
+ {
+ arguments = CheckArguments(ref stackArgs, parameters!, binder, invokeAttr, culture, sig);
+ }
bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
object? retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, false, wrapExceptions);
[DebuggerStepThroughAttribute]
[Diagnostics.DebuggerHidden]
- private Span<object?> InvokeArgumentsCheck(ref StackAllocedArguments stackArgs, object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture)
+ internal object? InvokeOneParameter(object? obj, BindingFlags invokeAttr, Binder? binder, object? parameter, CultureInfo? culture)
{
- Signature sig = Signature;
-
- // get the signature
- int formalCount = sig.Arguments.Length;
- int actualCount = (parameters != null) ? parameters.Length : 0;
-
- INVOCATION_FLAGS invocationFlags = InvocationFlags;
-
// INVOCATION_FLAGS_CONTAINS_STACK_POINTERS means that the struct (either the declaring type or the return type)
// contains pointers that point to the stack. This is either a ByRef or a TypedReference. These structs cannot
// be boxed and thus cannot be invoked through reflection which only deals with boxed value type objects.
- if ((invocationFlags & (INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE | INVOCATION_FLAGS.INVOCATION_FLAGS_CONTAINS_STACK_POINTERS)) != 0)
+ if ((InvocationFlags & (INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE | INVOCATION_FLAGS.INVOCATION_FLAGS_CONTAINS_STACK_POINTERS)) != 0)
+ {
ThrowNoInvokeException();
+ }
// check basic method consistency. This call will throw if there are problems in the target/method relationship
CheckConsistency(obj);
- if (formalCount != actualCount)
- throw new TargetParameterCountException(SR.Arg_ParmCnt);
-
- Span<object?> retVal = default;
- if (actualCount != 0)
+ Signature sig = Signature;
+ if (sig.Arguments.Length != 1)
{
- retVal = CheckArguments(ref stackArgs, parameters!, binder, invokeAttr, culture, sig);
+ throw new TargetParameterCountException(SR.Arg_ParmCnt);
}
- return retVal;
+
+ StackAllocedArguments stackArgs = default;
+ Span<object?> arguments = CheckArguments(ref stackArgs, new ReadOnlySpan<object?>(ref parameter, 1), binder, invokeAttr, culture, sig);
+
+ bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
+ return RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, constructor: false, wrapExceptions);
}
#endregion
public override Type PropertyType => Signature.ReturnType;
- public override MethodInfo? GetGetMethod(bool nonPublic)
+ public override RuntimeMethodInfo? GetGetMethod(bool nonPublic)
{
if (!Associates.IncludeAccessor(m_getterMethod, nonPublic))
return null;
return m_getterMethod;
}
- public override MethodInfo? GetSetMethod(bool nonPublic)
+ public override RuntimeMethodInfo? GetSetMethod(bool nonPublic)
{
if (!Associates.IncludeAccessor(m_setterMethod, nonPublic))
return null;
ParameterInfo[]? methParams = null;
// First try to get the Get method.
- MethodInfo? m = GetGetMethod(true);
+ RuntimeMethodInfo? m = GetGetMethod(true);
if (m != null)
{
// There is a Get method so use it.
[Diagnostics.DebuggerHidden]
public override object? GetValue(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture)
{
- MethodInfo? m = GetGetMethod(true);
+ RuntimeMethodInfo? m = GetGetMethod(true);
if (m == null)
throw new ArgumentException(System.SR.Arg_GetMethNotFnd);
return m.Invoke(obj, invokeAttr, binder, index, null);
[Diagnostics.DebuggerHidden]
public override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture)
{
- MethodInfo? m = GetSetMethod(true);
+ RuntimeMethodInfo? m = GetSetMethod(true);
if (m == null)
throw new ArgumentException(System.SR.Arg_SetMethNotFnd);
- object?[] args;
- if (index != null)
+ if (index is null)
{
- args = new object[index.Length + 1];
+ m.InvokeOneParameter(obj, invokeAttr, binder, value, culture);
+ }
+ else
+ {
+ var args = new object?[index.Length + 1];
for (int i = 0; i < index.Length; i++)
args[i] = index[i];
args[index.Length] = value;
- }
- else
- {
- args = new object[1];
- args[0] = value;
- }
- m.Invoke(obj, invokeAttr, binder, args, culture);
+ m.Invoke(obj, invokeAttr, binder, args, culture);
+ }
}
#endregion