diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs index e6f2f76ae0faef..adb6faba1d3d40 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs @@ -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); } @@ -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) ? @@ -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) ? @@ -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; @@ -433,20 +433,21 @@ private unsafe ref byte InvokeWithManyArguments( if (needsCopyBack) CopyBackToArray(ref Unsafe.As(ref *pStorage), parameters, shouldCopyBack); + + return ((_returnTransform & (Transform.Nullable | Transform.Pointer | Transform.FunctionPointer | Transform.ByRef)) != 0) ? + ReturnTransform(ref ret, wrapInTargetInvocationException) : returnObject; } finally { RuntimeImports.RhUnregisterForGCReporting(®ByRefStorage); RuntimeImports.RhUnregisterForGCReporting(®ArgStorage); } - - return ref ret; } // This method is equivalent to the one above except that it takes 'Span' 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 parameters) + private unsafe object? InvokeWithManyArguments( + IntPtr methodToCall, ref byte thisArg, ref byte ret, ref object? returnObject, Span parameters) { int argCount = _argumentCount; @@ -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(®ByRefStorage); RuntimeImports.RhUnregisterForGCReporting(®ArgStorage); } - - 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); @@ -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' 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 parameters) + private unsafe object? InvokeWithFewArguments( + IntPtr methodToCall, ref byte thisArg, ref byte ret, ref object? returnObject, Span parameters) { Debug.Assert(_argumentCount <= MaxStackAllocArgCount); @@ -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 parameters) + private unsafe object? InvokeDirectWithFewArguments( + IntPtr methodToCall, ref byte thisArg, ref byte ret, ref object? returnObject, Span parameters) { Debug.Assert(_argumentCount <= MaxStackAllocArgCount); @@ -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) diff --git a/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodInvokerTests.cs b/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodInvokerTests.cs index bfe9adedbe695c..d936dd1d7fd58a 100644 --- a/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodInvokerTests.cs +++ b/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodInvokerTests.cs @@ -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; + 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)]