Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -212,17 +212,17 @@ private void InvokeClassConstructor()
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private object? InvokeWorker(object? obj, BindingFlags invokeAttr, in Span<object?> arguments)
private object? InvokeWorker(object? obj, BindingFlags invokeAttr, Span<object?> arguments)
{
bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
return RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, false, wrapExceptions);
return RuntimeMethodHandle.InvokeMethod(obj, in arguments, Signature, false, wrapExceptions);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private object InvokeCtorWorker(BindingFlags invokeAttr, in Span<object?> arguments)
private object InvokeCtorWorker(BindingFlags invokeAttr, Span<object?> arguments)
{
bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
return RuntimeMethodHandle.InvokeMethod(null, arguments, Signature, true, wrapExceptions)!;
return RuntimeMethodHandle.InvokeMethod(null, in arguments, Signature, true, wrapExceptions)!;
}

[RequiresUnreferencedCode("Trimming may change method bodies. For example it can change some instructions, remove branches or local variables.")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,10 +316,10 @@ public override MethodImplAttributes GetMethodImplementationFlags()

#region Invocation Logic(On MemberBase)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private object? InvokeWorker(object? obj, BindingFlags invokeAttr, in Span<object?> arguments)
private object? InvokeWorker(object? obj, BindingFlags invokeAttr, Span<object?> arguments)
{
bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
return RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, false, wrapExceptions);
return RuntimeMethodHandle.InvokeMethod(obj, in arguments, Signature, false, wrapExceptions);
}

[DebuggerStepThroughAttribute]
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3627,9 +3627,9 @@ bool CEEInfo::isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE clsHnd)

JIT_TO_EE_TRANSITION_LEAF();

TypeHandle VMClsHnd(clsHnd);
MethodTable * pMT = VMClsHnd.GetMethodTable();
ret = (pMT != NULL && pMT->IsStructRequiringStackAllocRetBuf());
// Disable this optimization. It has limited value (only kicks in on x86, and only for less common structs),
// causes bugs and introduces odd ABI differences not compatible with ReadyToRun.
ret = false;

EE_TO_JIT_TRANSITION_LEAF();

Expand Down
14 changes: 0 additions & 14 deletions src/coreclr/vm/methodtable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6002,20 +6002,6 @@ BOOL MethodTable::SanityCheck()
return (pCanonMT == this) || IsArray();
}

//==========================================================================================

// Structs containing GC pointers whose size is at most this are always stack-allocated.
const unsigned MaxStructBytesForLocalVarRetBuffBytes = 2 * sizeof(void*); // 4 pointer-widths.

BOOL MethodTable::IsStructRequiringStackAllocRetBuf()
{
LIMITED_METHOD_DAC_CONTRACT;

// Disable this optimization. It has limited value (only kicks in on x86, and only for less common structs),
// causes bugs and introduces odd ABI differences not compatible with ReadyToRun.
return FALSE;
}

//==========================================================================================
unsigned MethodTable::GetTypeDefRid()
{
Expand Down
8 changes: 0 additions & 8 deletions src/coreclr/vm/methodtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -2511,14 +2511,6 @@ class MethodTable
// Is this value type? Returns false for System.ValueType and System.Enum.
inline BOOL IsValueType();

// Returns "TRUE" iff "this" is a struct type such that return buffers used for returning a value
// of this type must be stack-allocated. This will generally be true only if the struct
// contains GC pointers, and does not exceed some size limit. Maintaining this as an invariant allows
// an optimization: the JIT may assume that return buffer pointers for return types for which this predicate
// returns TRUE are always stack allocated, and thus, that stores to the GC-pointer fields of such return
// buffers do not require GC write barriers.
BOOL IsStructRequiringStackAllocRetBuf();

// Is this enum? Returns false for System.Enum.
inline BOOL IsEnum();

Expand Down
51 changes: 13 additions & 38 deletions src/coreclr/vm/reflectioninvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ struct ByRefToNullable {
}
};

void CallDescrWorkerReflectionWrapper(CallDescrData * pCallDescrData, Frame * pFrame)
static void CallDescrWorkerReflectionWrapper(CallDescrData * pCallDescrData, Frame * pFrame)
{
// Use static contracts b/c we have SEH.
STATIC_CONTRACT_THROWS;
Expand All @@ -478,7 +478,7 @@ void CallDescrWorkerReflectionWrapper(CallDescrData * pCallDescrData, Frame * pF
PAL_ENDTRY
} // CallDescrWorkerReflectionWrapper

OBJECTREF InvokeArrayConstructor(TypeHandle th, MethodDesc* pMeth, Span<OBJECTREF>* objs, int argCnt)
static OBJECTREF InvokeArrayConstructor(TypeHandle th, Span<OBJECTREF>* objs, int argCnt)
{
CONTRACTL {
THROWS;
Expand Down Expand Up @@ -510,7 +510,9 @@ OBJECTREF InvokeArrayConstructor(TypeHandle th, MethodDesc* pMeth, Span<OBJECTRE
if (!InvokeUtil::IsPrimitiveType(oType) || !InvokeUtil::CanPrimitiveWiden(ELEMENT_TYPE_I4,oType))
COMPlusThrow(kArgumentException,W("Arg_PrimWiden"));

memcpy(&indexes[i], objs->GetAt(i)->UnBox(),pMT->GetNumInstanceFieldBytes());
ARG_SLOT value;
InvokeUtil::CreatePrimitiveValue(ELEMENT_TYPE_I4, oType, objs->GetAt(i), &value);
memcpyNoGCRefs(indexes + i, ArgSlotEndianessFixup(&value, sizeof(INT32)), sizeof(INT32));
}

return AllocateArrayEx(th, indexes, argCnt);
Expand Down Expand Up @@ -769,8 +771,6 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,

HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);

Assembly *pAssem = pMeth->GetAssembly();

if (ownerType.IsSharedByGenericInstantiations())
COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));

Expand All @@ -786,23 +786,20 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
if (fConstructor)
{
// If we are invoking a constructor on an array then we must
// handle this specially. String objects allocate themselves
// so they are a special case.
// handle this specially.
if (ownerType.IsArray()) {
gc.retVal = InvokeArrayConstructor(ownerType,
pMeth,
objs,
gc.pSig->NumFixedArgs());
goto Done;
}

// Variable sized objects, like String instances, allocate themselves
// so they are a special case.
MethodTable * pMT = ownerType.AsMethodTable();

{
fCtorOfVariableSizedObject = pMT->HasComponentSize();
if (!fCtorOfVariableSizedObject)
gc.retVal = pMT->Allocate();
}
fCtorOfVariableSizedObject = pMT->HasComponentSize();
if (!fCtorOfVariableSizedObject)
gc.retVal = pMT->Allocate();
}

{
Expand Down Expand Up @@ -936,8 +933,6 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
// If an exception occurs a gc may happen but we are going to dump the stack anyway and we do
// not need to protect anything.

PVOID pRetBufStackCopy = NULL;

{
BEGINFORBIDGC();
#ifdef _DEBUG
Expand All @@ -947,24 +942,8 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
// Take care of any return arguments
if (fHasRetBuffArg)
{
// We stack-allocate this ret buff, to preserve the invariant that ret-buffs are always in the
// caller's stack frame. We'll copy into gc.retVal later.
TypeHandle retTH = gc.pSig->GetReturnTypeHandle();
MethodTable* pMT = retTH.GetMethodTable();
if (pMT->IsStructRequiringStackAllocRetBuf())
{
SIZE_T sz = pMT->GetNumInstanceFieldBytes();
pRetBufStackCopy = _alloca(sz);
memset(pRetBufStackCopy, 0, sz);

pValueClasses = new (_alloca(sizeof(ValueClassInfo))) ValueClassInfo(pRetBufStackCopy, pMT, pValueClasses);
*((LPVOID*) (pTransitionBlock + argit.GetRetBuffArgOffset())) = pRetBufStackCopy;
}
else
{
PVOID pRetBuff = gc.retVal->GetData();
*((LPVOID*) (pTransitionBlock + argit.GetRetBuffArgOffset())) = pRetBuff;
}
PVOID pRetBuff = gc.retVal->GetData();
*((LPVOID*) (pTransitionBlock + argit.GetRetBuffArgOffset())) = pRetBuff;
}

// copy args
Expand Down Expand Up @@ -1128,10 +1107,6 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
{
CopyValueClass(gc.retVal->GetData(), &callDescrData.returnValue, gc.retVal->GetMethodTable());
}
else if (pRetBufStackCopy)
{
CopyValueClass(gc.retVal->GetData(), pRetBufStackCopy, gc.retVal->GetMethodTable());
}
// From here on out, it is OK to have GCs since the return object (which may have had
// GC pointers has been put into a GC object and thus protected.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ internal void ThrowNoInvokeException()

ValidateInvokeTarget(obj);

// Correct number of arguments supplied
int actualCount = (parameters is null) ? 0 : parameters.Length;
if (ArgumentTypes.Length != actualCount)
{
throw new TargetParameterCountException(SR.Arg_ParmCnt);
}

if ((InvocationFlags & InvocationFlags.RunClassConstructor) != 0)
{
// Run the class constructor through the class constructor mechanism instead of the Invoke path.
Expand All @@ -113,13 +120,6 @@ internal void ThrowNoInvokeException()
return null;
}

// Correct number of arguments supplied
int actualCount = (parameters is null) ? 0 : parameters.Length;
if (ArgumentTypes.Length != actualCount)
{
throw new TargetParameterCountException(SR.Arg_ParmCnt);
}

StackAllocedArguments stackArgs = default;
Span<object?> arguments = default;
if (actualCount != 0)
Expand Down
23 changes: 23 additions & 0 deletions src/libraries/System.Reflection/tests/Common.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Globalization;

namespace System.Reflection.Tests
{
public enum PublicEnum
Expand Down Expand Up @@ -105,4 +107,25 @@ public class Helpers
{
public static Assembly ExecutingAssembly => typeof(Helpers).GetTypeInfo().Assembly;
}

public class ConvertStringToIntBinder : Binder
{
public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo? culture)
=> throw new NotImplementedException();

public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object?[] args, ParameterModifier[]? modifiers, CultureInfo? culture, string[]? names, out object? state)
=> throw new NotImplementedException();

public override object ChangeType(object value, Type type, CultureInfo? culture)
=> int.Parse((string)value);

public override void ReorderArgumentArray(ref object?[] args, object state)
=> throw new NotImplementedException();

public override MethodBase? SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[]? modifiers)
=> throw new NotImplementedException();

public override PropertyInfo? SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type? returnType, Type[]? indexes, ParameterModifier[]? modifiers)
=> throw new NotImplementedException();
}
}
26 changes: 24 additions & 2 deletions src/libraries/System.Reflection/tests/ConstructorInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,17 @@ public void Invoke_OneDimensionalArray_NegativeLengths_ThrowsOverflowException()
}
}

[Fact]
public void Invoke_TwoDimensionalArray_CustomBinder_IncorrectTypeArguments()
{
var ctor = typeof(int[,]).GetConstructor(new[] { typeof(int), typeof(int) });
var args = new object[] { "1", "2" };
var arr = (int[,])ctor.Invoke(BindingFlags.Default, new ConvertStringToIntBinder(), args, null);
Assert.Equal(2, arr.Length);
Assert.True(args[0] is int);
Assert.True(args[1] is int);
}

[Fact]
public void Invoke_OneParameter()
{
Expand All @@ -143,6 +154,19 @@ public void Invoke_TwoParameters()
Assert.Equal("hello", obj.stringValue);
}

[Fact]
public void Invoke_TwoParameters_CustomBinder_IncorrectTypeArgument()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));

var args = new object[] { "101", "hello" };
ClassWith3Constructors obj = (ClassWith3Constructors)constructors[2].Invoke(BindingFlags.Default, new ConvertStringToIntBinder(), args, null);
Assert.Equal(101, obj.intValue);
Assert.Equal("hello", obj.stringValue);
Assert.True(args[0] is int);
Assert.True(args[1] is string);
}

[Fact]
public void Invoke_NoParameters_ThowsTargetParameterCountException()
{
Expand Down Expand Up @@ -248,8 +272,6 @@ public ClassWith3Constructors(int intValue, string stringValue)
this.intValue = intValue;
this.stringValue = stringValue;
}

public string Method1(DateTime dt) => "";
}

public static class ClassWithStaticConstructor
Expand Down
10 changes: 10 additions & 0 deletions src/libraries/System.Reflection/tests/MethodInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,16 @@ public static void Invoke_OptionalParameterUnassingableFromMissing_WithMissingVa
AssertExtensions.Throws<ArgumentException>(null, () => GetMethod(typeof(MethodInfoDefaultParameters), "OptionalStringParameter").Invoke(new MethodInfoDefaultParameters(), new object[] { Type.Missing }));
}

[Fact]
public void Invoke_TwoParameters_CustomBinder_IncorrectTypeArguments()
{
MethodInfo method = GetMethod(typeof(MI_SubClass), nameof(MI_SubClass.StaticIntIntMethodReturningInt));
var args = new object[] { "10", "100" };
Assert.Equal(110, method.Invoke(null, BindingFlags.Default, new ConvertStringToIntBinder(), args, null));
Assert.True(args[0] is int);
Assert.True(args[1] is int);
}

[Theory]
[InlineData(typeof(MI_SubClass), nameof(MI_SubClass.GenericMethod1), new Type[] { typeof(int) })]
[InlineData(typeof(MI_SubClass), nameof(MI_SubClass.GenericMethod2), new Type[] { typeof(string), typeof(int) })]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ private RuntimeType[] ArgumentTypes
internal extern object? InternalInvoke(object? obj, in Span<object?> parameters, out Exception? exc);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private object? InvokeWorker(object? obj, BindingFlags invokeAttr, in Span<object?> parameters)
private object? InvokeWorker(object? obj, BindingFlags invokeAttr, Span<object?> parameters)
{
Exception? exc;
object? o = null;
Expand Down Expand Up @@ -862,7 +862,7 @@ private void InvokeClassConstructor()
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal object? InvokeWorker(object? obj, BindingFlags invokeAttr, in Span<object?> arguments)
internal object? InvokeWorker(object? obj, BindingFlags invokeAttr, Span<object?> arguments)
{
bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
return InternalInvoke(obj, arguments, wrapExceptions);
Expand Down
11 changes: 3 additions & 8 deletions src/mono/mono/metadata/icall.c
Original file line number Diff line number Diff line change
Expand Up @@ -3373,9 +3373,6 @@ ves_icall_InternalInvoke (MonoReflectionMethodHandle method_handle, MonoObjectHa
MonoMethodSignature* const sig = mono_method_signature_internal (m);
int pcount = 0;
void *obj = this_arg;
char *this_name = NULL;
char *target_name = NULL;
char *msg = NULL;
MonoObject *result = NULL;
MonoArray *arr = NULL;
MonoException *exception = NULL;
Expand Down Expand Up @@ -3403,6 +3400,7 @@ ves_icall_InternalInvoke (MonoReflectionMethodHandle method_handle, MonoObjectHa
}
}

/* Array constructor */
if (m_class_get_rank (m->klass) && !strcmp (m->name, ".ctor")) {
int i;
pcount = mono_span_length (params_span);
Expand Down Expand Up @@ -3436,12 +3434,12 @@ ves_icall_InternalInvoke (MonoReflectionMethodHandle method_handle, MonoObjectHa
g_assert (pcount == (m_class_get_rank (m->klass) * 2));
/* The arguments are lower-bound-length pairs */
intptr_t * const lower_bounds = (intptr_t *)g_alloca (sizeof (intptr_t) * pcount);

for (i = 0; i < pcount / 2; ++i) {
lower_bounds [i] = *(int32_t*) ((char*)mono_span_get (params_span, MonoObject*, (i * 2)) + sizeof (MonoObject));
lengths [i] = *(int32_t*) ((char*)mono_span_get (params_span, MonoObject*, (i * 2) + 1) + sizeof (MonoObject));
}

arr = mono_array_new_full_checked (m->klass, lengths, lower_bounds, error);
goto_if_nok (error, return_null);
goto exit;
Expand All @@ -3457,9 +3455,6 @@ ves_icall_InternalInvoke (MonoReflectionMethodHandle method_handle, MonoObjectHa
MONO_HANDLE_NEW (MonoException, exception); // FIXME? overkill?
mono_gc_wbarrier_generic_store_internal (MONO_HANDLE_REF (exception_out), (MonoObject*)exception);
}
g_free (target_name);
g_free (this_name);
g_free (msg);
g_assert (!result || !arr); // only one, or neither, should be set
return result ? MONO_HANDLE_NEW (MonoObject, result) : arr ? MONO_HANDLE_NEW (MonoObject, (MonoObject*)arr) : NULL_HANDLE;
}
Expand Down
Loading