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
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,12 @@ public unsafe DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk)
}
else if (argCount > MaxStackAllocArgCount)
{
ret = ref InvokeWithManyArguments(methodToCall, ref thisArg, ref ret,
return InvokeWithManyArguments(methodToCall, ref thisArg, ref ret, ref returnObject,
parameters, binderBundle, wrapInTargetInvocationException);
}
else
{
ret = ref InvokeWithFewArguments(methodToCall, ref thisArg, ref ret,
return InvokeWithFewArguments(methodToCall, ref thisArg, ref ret, ref returnObject,
parameters, binderBundle, wrapInTargetInvocationException);
}

Expand Down Expand Up @@ -296,11 +296,11 @@ public unsafe DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk)
}
else if (argCount > MaxStackAllocArgCount)
{
ret = ref InvokeWithManyArguments(methodToCall, ref thisArg, ref ret, parameters);
return InvokeWithManyArguments(methodToCall, ref thisArg, ref ret, ref returnObject, parameters);
}
else
{
ret = ref InvokeWithFewArguments(methodToCall, ref thisArg, ref ret, parameters);
return InvokeWithFewArguments(methodToCall, ref thisArg, ref ret, ref returnObject, parameters);
}

return ((_returnTransform & (Transform.Nullable | Transform.Pointer | Transform.FunctionPointer | Transform.ByRef)) != 0) ?
Expand Down Expand Up @@ -375,7 +375,7 @@ public unsafe DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk)
}
else
{
ret = ref InvokeDirectWithFewArguments(methodToCall, ref thisArg, ref ret, parameters);
return InvokeDirectWithFewArguments(methodToCall, ref thisArg, ref ret, ref returnObject, parameters);
}

return ((_returnTransform & (Transform.Nullable | Transform.Pointer | Transform.FunctionPointer | Transform.ByRef)) != 0) ?
Expand All @@ -395,8 +395,8 @@ private void ThrowForArgCountMismatch()
throw new TargetParameterCountException(SR.Arg_ParmCnt);
}

private unsafe ref byte InvokeWithManyArguments(
IntPtr methodToCall, ref byte thisArg, ref byte ret,
private unsafe object? InvokeWithManyArguments(
IntPtr methodToCall, ref byte thisArg, ref byte ret, ref object? returnObject,
object?[] parameters, BinderBundle binderBundle, bool wrapInTargetInvocationException)
{
int argCount = _argumentCount;
Expand Down Expand Up @@ -433,20 +433,21 @@ private unsafe ref byte InvokeWithManyArguments(

if (needsCopyBack)
CopyBackToArray(ref Unsafe.As<IntPtr, object?>(ref *pStorage), parameters, shouldCopyBack);

return ((_returnTransform & (Transform.Nullable | Transform.Pointer | Transform.FunctionPointer | Transform.ByRef)) != 0) ?
ReturnTransform(ref ret, wrapInTargetInvocationException) : returnObject;
}
finally
{
RuntimeImports.RhUnregisterForGCReporting(&regByRefStorage);
RuntimeImports.RhUnregisterForGCReporting(&regArgStorage);
}

return ref ret;
}

// This method is equivalent to the one above except that it takes 'Span<object>' instead of 'object[]'
// for the parameters and does not require re-throw capability.
private unsafe ref byte InvokeWithManyArguments(
IntPtr methodToCall, ref byte thisArg, ref byte ret, Span<object?> parameters)
private unsafe object? InvokeWithManyArguments(
IntPtr methodToCall, ref byte thisArg, ref byte ret, ref object? returnObject, Span<object?> parameters)
{
int argCount = _argumentCount;

Expand Down Expand Up @@ -475,19 +476,20 @@ private unsafe ref byte InvokeWithManyArguments(

if (needsCopyBack)
CopyBackToSpan(copyOfParameters, parameters, shouldCopyBack);

return ((_returnTransform & (Transform.Nullable | Transform.Pointer | Transform.FunctionPointer | Transform.ByRef)) != 0) ?
ReturnTransform(ref ret, wrapInTargetInvocationException: false) : returnObject;
}
finally
{
RuntimeImports.RhUnregisterForGCReporting(&regByRefStorage);
RuntimeImports.RhUnregisterForGCReporting(&regArgStorage);
}

return ref ret;
}

// This is a separate method to localize the overhead of stack allocs for 'StackAllocatedByRefs' and 'StackAllocatedByRefs'.
private unsafe ref byte InvokeWithFewArguments(
IntPtr methodToCall, ref byte thisArg, ref byte ret,
private unsafe object? InvokeWithFewArguments(
IntPtr methodToCall, ref byte thisArg, ref byte ret, ref object? returnObject,
object?[] parameters, BinderBundle? binderBundle, bool wrapInTargetInvocationException)
{
Debug.Assert(_argumentCount <= MaxStackAllocArgCount);
Expand All @@ -514,13 +516,14 @@ private unsafe ref byte InvokeWithFewArguments(
if (needsCopyBack)
CopyBackToArray(ref copyOfParameters[0], parameters, shouldCopyBack);

return ref ret;
return ((_returnTransform & (Transform.Nullable | Transform.Pointer | Transform.FunctionPointer | Transform.ByRef)) != 0) ?
ReturnTransform(ref ret, wrapInTargetInvocationException) : returnObject;
}

// This method is equivalent to the one above except that it takes 'Span<object>' instead of 'object[]'
// for the parameters and does not require 'BinderBundle' or re-throw capability.
private unsafe ref byte InvokeWithFewArguments(
IntPtr methodToCall, ref byte thisArg, ref byte ret, Span<object?> parameters)
private unsafe object? InvokeWithFewArguments(
IntPtr methodToCall, ref byte thisArg, ref byte ret, ref object? returnObject, Span<object?> parameters)
{
Debug.Assert(_argumentCount <= MaxStackAllocArgCount);

Expand All @@ -539,12 +542,13 @@ private unsafe ref byte InvokeWithFewArguments(
if (needsCopyBack)
CopyBackToSpan(copyOfParameters, parameters, shouldCopyBack);

return ref ret;
return ((_returnTransform & (Transform.Nullable | Transform.Pointer | Transform.FunctionPointer | Transform.ByRef)) != 0) ?
ReturnTransform(ref ret, wrapInTargetInvocationException: false) : returnObject;
}

// This method is equivalent to the one above except that it does not require a copy of the args or CopyBack.
private unsafe ref byte InvokeDirectWithFewArguments(
IntPtr methodToCall, ref byte thisArg, ref byte ret, Span<object?> parameters)
private unsafe object? InvokeDirectWithFewArguments(
IntPtr methodToCall, ref byte thisArg, ref byte ret, ref object? returnObject, Span<object?> parameters)
{
Debug.Assert(_argumentCount <= MaxStackAllocArgCount);

Expand All @@ -559,7 +563,8 @@ private unsafe ref byte InvokeDirectWithFewArguments(

// No need to call CopyBack here since no copy of the arguments was made.

return ref ret;
return ((_returnTransform & (Transform.Nullable | Transform.Pointer | Transform.FunctionPointer | Transform.ByRef)) != 0) ?
ReturnTransform(ref ret, wrapInTargetInvocationException: false) : returnObject;
}

private unsafe object? GetCoercedDefaultValue(int index, in ArgumentInfo argumentInfo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,43 @@ public class MethodInvokerTests : MethodCommonTests

protected override bool SupportsMissing => false;

[Theory]
[InlineData(nameof(RefReturningArgument))]
[InlineData(nameof(RefReturningArgumentFew))]
[InlineData(nameof(RefReturningArgumentMany))]
public void Invoke_RefReturnAliasesArgument(string methodName)
{
MethodInfo method = typeof(MethodInvokerTests).GetMethod(methodName)!;
MethodInvoker invoker = MethodInvoker.Create(method);
object?[] arguments = new object?[method.GetParameters().Length];
Array.Fill(arguments, new object());

for (int i = 0; i < 100; i++)
{
object? expected = i % 2 == 0 ? new object() : null;
Comment thread
jkotas marked this conversation as resolved.
arguments[0] = expected;
Assert.Same(expected, method.Invoke(null, arguments));
Assert.Same(expected, arguments[0]);
Assert.Same(expected, invoker.Invoke(null, arguments.AsSpan()));
Assert.Same(expected, arguments[0]);

if (arguments.Length == 1)
{
Assert.Same(expected, invoker.Invoke(null, arguments[0]));
}
else if (arguments.Length == 4)
{
Assert.Same(expected, invoker.Invoke(null, arguments[0], arguments[1], arguments[2], arguments[3]));
}
}
}

public static ref object? RefReturningArgument(ref object? value) => ref value;

public static ref object? RefReturningArgumentFew(ref object? value, object? a, object? b, object? c) => ref value;

public static ref object? RefReturningArgumentMany(ref object? value, object? a, object? b, object? c, object? d) => ref value;

[Theory]
[InlineData(false)]
[InlineData(true)]
Expand Down
Loading