diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index ec433b913518dc..bcd4a42d8d1d8f 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -1,4 +1,4 @@ - + false @@ -155,12 +155,12 @@ - + @@ -187,7 +187,6 @@ - diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.CoreCLR.cs deleted file mode 100644 index 05f69d2670e3ed..00000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.CoreCLR.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System.Reflection -{ - internal partial class ConstructorInvoker - { - public InvocationFlags _invocationFlags; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe object? InterpretedInvoke(object? obj, IntPtr* arguments) - { - return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, _method.Signature, isConstructor: obj is null)!; - } - } -} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs index bc1966d1e3bfaa..24641e490a1ada 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs @@ -23,7 +23,7 @@ public sealed class DynamicMethod : MethodInfo private RuntimeModule m_module = null!; internal bool m_skipVisibility; internal RuntimeType? m_typeOwner; // can be null - private MethodInvoker? _invoker; + private DynamicMethodInvoker? _invoker; private Signature? _signature; // We want the creator of the DynamicMethod to control who has access to the @@ -434,12 +434,13 @@ internal RuntimeMethodHandle GetMethodDescriptor() public override bool IsSecurityTransparent => false; - private MethodInvoker Invoker + private DynamicMethodInvoker Invoker { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - _invoker ??= new MethodInvoker(this, Signature); + _invoker ??= new DynamicMethodInvoker(this); + return _invoker; } } @@ -489,7 +490,7 @@ Signature LazyCreateSignature() { if (argCount == 0) { - retValue = Invoker.InlinedInvoke(obj, args: default, invokeAttr); + retValue = Invoker.InvokeUnsafe(obj, args: default, invokeAttr); } else if (argCount > MaxStackAllocArgCount) { @@ -500,8 +501,8 @@ Signature LazyCreateSignature() { Debug.Assert(parameters != null); StackAllocedArguments argStorage = default; - Span copyOfParameters = new(ref argStorage._arg0, argCount); - Span shouldCopyBackParameters = new(ref argStorage._copyBack0, argCount); + Span copyOfParameters = new Span(ref argStorage._arg0, argCount); + Span shouldCopyBackParameters = new Span(ref argStorage._copyBack0, argCount); StackAllocatedByRefs byrefStorage = default; IntPtr* pByRefStorage = (IntPtr*)&byrefStorage; @@ -516,25 +517,14 @@ Signature LazyCreateSignature() culture, invokeAttr); - retValue = Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr); + retValue = Invoker.InvokeUnsafe(obj, pByRefStorage, invokeAttr); // Copy modified values out. This should be done only with ByRef or Type.Missing parameters. for (int i = 0; i < argCount; i++) { - ParameterCopyBackAction action = shouldCopyBackParameters[i]; - if (action != ParameterCopyBackAction.None) + if (shouldCopyBackParameters[i]) { - if (action == ParameterCopyBackAction.Copy) - { - parameters[i] = copyOfParameters[i]; - } - else - { - Debug.Assert(action == ParameterCopyBackAction.CopyNullable); - Debug.Assert(copyOfParameters[i] != null); - Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT); - parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]); - } + parameters[i] = copyOfParameters[i]; } } } @@ -556,15 +546,15 @@ Signature LazyCreateSignature() CultureInfo? culture) { object[] objHolder = new object[argCount]; - Span copyOfParameters = new(objHolder, 0, argCount); + Span copyOfParameters = new Span(objHolder, 0, argCount); // We don't check a max stack size since we are invoking a method which // naturally requires a stack size that is dependent on the arg count\size. IntPtr* pByRefStorage = stackalloc IntPtr[argCount]; Buffer.ZeroMemory((byte*)pByRefStorage, (uint)(argCount * sizeof(IntPtr))); - ParameterCopyBackAction* copyBackActions = stackalloc ParameterCopyBackAction[argCount]; - Span shouldCopyBackParameters = new(copyBackActions, argCount); + bool* boolHolder = stackalloc bool[argCount]; + Span shouldCopyBackParameters = new Span(boolHolder, argCount); GCFrameRegistration reg = new(pByRefStorage, (uint)argCount, areByRefs: true); @@ -582,7 +572,7 @@ Signature LazyCreateSignature() culture, invokeAttr); - retValue = mi.Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr); + retValue = mi.Invoker.InvokeUnsafe(obj, pByRefStorage, invokeAttr); } finally { @@ -592,26 +582,36 @@ Signature LazyCreateSignature() // Copy modified values out. This should be done only with ByRef or Type.Missing parameters. for (int i = 0; i < argCount; i++) { - ParameterCopyBackAction action = shouldCopyBackParameters[i]; - if (action != ParameterCopyBackAction.None) + if (shouldCopyBackParameters[i]) { - if (action == ParameterCopyBackAction.Copy) - { - parameters[i] = copyOfParameters[i]; - } - else - { - Debug.Assert(action == ParameterCopyBackAction.CopyNullable); - Debug.Assert(copyOfParameters[i] != null); - Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT); - parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]); - } + parameters[i] = copyOfParameters[i]; } } return retValue; } + [DebuggerHidden] + [DebuggerStepThrough] + internal unsafe object? InvokeNonEmitUnsafe(object? obj, IntPtr* arguments, BindingFlags invokeAttr) + { + if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0) + { + try + { + return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false); + } + catch (Exception e) + { + throw new TargetInvocationException(e); + } + } + else + { + return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false); + } + } + public override object[] GetCustomAttributes(Type attributeType, bool inherit) { return m_dynMethod.GetCustomAttributes(attributeType, inherit); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethodInvoker.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethodInvoker.cs new file mode 100644 index 00000000000000..9e5a5e17be4e80 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethodInvoker.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Reflection.Emit +{ + internal sealed partial class DynamicMethodInvoker + { + private readonly DynamicMethod _dynamicMethod; + + public DynamicMethodInvoker(DynamicMethod dynamicMethod) + { + _dynamicMethod = dynamicMethod; + } + + public unsafe object? InvokeUnsafe(object? obj, IntPtr* args, BindingFlags invokeAttr) + { + // Todo: add strategy for calling IL Emit-based version + return _dynamicMethod.InvokeNonEmitUnsafe(obj, args, invokeAttr); + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodInvoker.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodInvoker.CoreCLR.cs deleted file mode 100644 index 6acf77be549c01..00000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodInvoker.CoreCLR.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System.Reflection -{ - internal partial class MethodInvoker - { - private readonly Signature _signature; - internal InvocationFlags _invocationFlags; - - public MethodInvoker(MethodBase method, Signature signature) - { - _method = method; - _signature = signature; - -#if USE_NATIVE_INVOKE - // Always use the native invoke; useful for testing. - _strategyDetermined = true; -#elif USE_EMIT_INVOKE - // Always use emit invoke (if IsDynamicCodeCompiled == true); useful for testing. - _invoked = true; -#endif - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe object? InterpretedInvoke(object? obj, IntPtr* arguments) - { - return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, _signature, isConstructor: false); - } - } -} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs index 5831eca455adc1..8eb09a7756e8f5 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs @@ -236,7 +236,7 @@ public override void SetValue(object? obj, object? value, BindingFlags invokeAtt CheckConsistency(obj); - ParameterCopyBackAction _ref = default; + bool _ref = false; RuntimeType fieldType = (RuntimeType)FieldType; if (value is null) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.CoreCLR.cs index 51ee0061420e0f..5b6efa638e9b2a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.CoreCLR.cs @@ -95,6 +95,27 @@ Signature LazyCreateSignature() internal BindingFlags BindingFlags => m_bindingFlags; + + [DebuggerStepThrough] + [DebuggerHidden] + internal unsafe object InvokeNonEmitUnsafe(object? obj, IntPtr* args, Span argsForTemporaryMonoSupport, BindingFlags invokeAttr) + { + if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0) + { + try + { + return RuntimeMethodHandle.InvokeMethod(obj, (void**)args, Signature, isConstructor: obj is null)!; + } + catch (Exception ex) + { + throw new TargetInvocationException(ex); + } + } + else + { + return RuntimeMethodHandle.InvokeMethod(obj, (void**)args, Signature, isConstructor: obj is null)!; + } + } #endregion #region Object Overrides diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs index 229e2981bb6f25..c80aa9af08a766 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs @@ -48,7 +48,7 @@ private MethodInvoker Invoker [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - m_invoker ??= new MethodInvoker(this, Signature); + m_invoker ??= new MethodInvoker(this); return m_invoker; } } @@ -351,7 +351,7 @@ public override MethodImplAttributes GetMethodImplementationFlags() StackAllocedArguments argStorage = default; Span copyOfParameters = new(ref argStorage._arg0, 1); ReadOnlySpan parameters = new(in parameter); - Span shouldCopyBackParameters = new(ref argStorage._copyBack0, 1); + Span shouldCopyBackParameters = new(ref argStorage._copyBack0, 1); StackAllocatedByRefs byrefStorage = default; IntPtr* pByRefStorage = (IntPtr*)&byrefStorage; @@ -366,16 +366,33 @@ public override MethodImplAttributes GetMethodImplementationFlags() culture, invokeAttr); -#if MONO // Temporary until Mono is updated. - retValue = Invoker.InlinedInvoke(obj, copyOfParameters, invokeAttr); -#else - retValue = Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr); -#endif + retValue = Invoker.InvokeUnsafe(obj, pByRefStorage, copyOfParameters, invokeAttr); } return retValue; } + [DebuggerHidden] + [DebuggerStepThrough] + internal unsafe object? InvokeNonEmitUnsafe(object? obj, IntPtr* arguments, Span argsForTemporaryMonoSupport, BindingFlags invokeAttr) + { + if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0) + { + try + { + return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false); + } + catch (Exception e) + { + throw new TargetInvocationException(e); + } + } + else + { + return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false); + } + } + #endregion #region MethodInfo Overrides diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index fe506be7153953..166ecd396186f3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -138,19 +138,6 @@ internal static bool IsByRef(RuntimeType type) return corElemType == CorElementType.ELEMENT_TYPE_BYREF; } - internal static bool TryGetByRefElementType(RuntimeType type, [NotNullWhen(true)] out RuntimeType? elementType) - { - CorElementType corElemType = GetCorElementType(type); - if (corElemType == CorElementType.ELEMENT_TYPE_BYREF) - { - elementType = GetElementType(type); - return true; - } - - elementType = null; - return false; - } - internal static bool IsPointer(RuntimeType type) { CorElementType corElemType = GetCorElementType(type); @@ -994,12 +981,6 @@ internal static MdUtf8String GetUtf8Name(RuntimeMethodHandleInternal method) [MethodImpl(MethodImplOptions.InternalCall)] internal static extern object? InvokeMethod(object? target, void** arguments, Signature sig, bool isConstructor); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object? ReboxFromNullable(object? src); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object ReboxToNullable(object? src, RuntimeType destNullableType); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeMethodHandle_GetMethodInstantiation")] private static partial void GetMethodInstantiation(RuntimeMethodHandleInternal method, ObjectHandleOnStack types, Interop.BOOL fAsRuntimeTypeArray); diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 374cdcf662b841..ed4c5630b1e051 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3495,7 +3495,7 @@ public override Type MakeArrayType(int rank) private static extern bool CanValueSpecialCast(RuntimeType valueType, RuntimeType targetType); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern object AllocateValueType(RuntimeType type, object? value); + private static extern object AllocateValueType(RuntimeType type, object? value, bool fForceTypeChange); private enum CheckValueStatus { @@ -3504,13 +3504,26 @@ private enum CheckValueStatus NotSupported_ByRefLike } +#if DEBUG + internal void VerifyValueType(object? value) + { + Debug.Assert(value != null); + Debug.Assert( + value.GetType() == this || + (IsPointer && value.GetType() == typeof(IntPtr)) || + (IsByRef && value.GetType() == RuntimeTypeHandle.GetElementType(this)) || + (value.GetType().IsEnum && GetUnderlyingType((RuntimeType)value.GetType()) == GetUnderlyingType(this)) || + (IsEnum && GetUnderlyingType((RuntimeType)value.GetType()) == GetUnderlyingType(this))); + } +#endif + /// /// Verify and optionally convert the value for special cases. /// - /// True if is a value type, False otherwise + /// True if the value should be considered a value type, False otherwise internal bool CheckValue( ref object? value, - ref ParameterCopyBackAction copyBack, + ref bool copyBack, Binder? binder, CultureInfo? culture, BindingFlags invokeAttr) @@ -3518,22 +3531,20 @@ internal bool CheckValue( // Already fast-pathed by the caller. Debug.Assert(!ReferenceEquals(value?.GetType(), this)); - // Since this cannot be a generic parameter, we use RuntimeTypeHandle.IsValueType here - // because it is faster than IsValueType - Debug.Assert(!IsGenericParameter); - // Fast path to whether a value can be assigned without conversion. if (IsInstanceOfType(value)) { - if (IsNullableOfT) + // Since this cannot be a generic parameter, we use RuntimeTypeHandle.IsValueType here + // because it is faster than IsValueType + Debug.Assert(!IsGenericParameter); + + if (RuntimeTypeHandle.IsValueType(this)) { - // Pass as a true boxed Nullable, not as a T or null. - value = RuntimeMethodHandle.ReboxToNullable(value, this); - return true; - } + // Nullable is the only value type that will get here. + Debug.Assert(IsNullableOfT); - // Other value types won't get here since Type equality was previous checked. - Debug.Assert(!RuntimeTypeHandle.IsValueType(this)); + // Fall through and treat as a reference type. + } return false; } @@ -3555,18 +3566,8 @@ internal bool CheckValue( value = binder.ChangeType(value, this, culture); if (IsInstanceOfType(value)) { - if (IsNullableOfT) - { - // Pass as a true boxed Nullable, not as a T or null. - value = RuntimeMethodHandle.ReboxToNullable(value, this); - copyBack = ParameterCopyBackAction.CopyNullable; - } - else - { - copyBack = ParameterCopyBackAction.Copy; - } - - return IsValueType; // Note the call to IsValueType, not the variable. + copyBack = true; + return IsValueType; } result = TryChangeType(ref value, out copyBack, out isValueType); @@ -3591,94 +3592,116 @@ internal bool CheckValue( private CheckValueStatus TryChangeType( ref object? value, - out ParameterCopyBackAction copyBack, + out bool copyBack, out bool isValueType) { - RuntimeType? sigElementType; - if (RuntimeTypeHandle.TryGetByRefElementType(this, out sigElementType)) + // If this is a ByRef get the element type and check if it's compatible + if (IsByRef) { - copyBack = ParameterCopyBackAction.Copy; - Debug.Assert(!sigElementType.IsGenericParameter); + RuntimeType sigElementType = RuntimeTypeHandle.GetElementType(this); - if (sigElementType.IsInstanceOfType(value)) + // If a nullable, pass the object even though it's a value type. + if (sigElementType.IsNullableOfT) { - isValueType = RuntimeTypeHandle.IsValueType(sigElementType); - if (isValueType) - { - if (sigElementType.IsNullableOfT) - { - // Pass as a true boxed Nullable, not as a T or null. - value = RuntimeMethodHandle.ReboxToNullable(value, sigElementType); - copyBack = ParameterCopyBackAction.CopyNullable; - } - else - { - // Make a copy to prevent the boxed instance from being directly modified by the method. - value = AllocateValueType(sigElementType, value); - } - } + // Treat as a reference type since a null value may be replaced with T or vise-versa. + isValueType = false; + copyBack = true; + return CheckValueStatus.Success; + } + if (sigElementType.IsInstanceOfType(value)) + { + // Need to create an instance of the ByRef if null was provided, but only if primitive, enum or value type + value = AllocateValueType(sigElementType, value, fForceTypeChange: false); + isValueType = sigElementType.IsValueType; + copyBack = true; return CheckValueStatus.Success; } if (value == null) { - isValueType = RuntimeTypeHandle.IsValueType(sigElementType); - if (!isValueType) - { - // Normally we don't get here since 'null' was previosuly checked, but due to binders we can. - return CheckValueStatus.Success; - } - - if (sigElementType.IsByRefLike) + if (IsByRefLike) { + isValueType = copyBack = default; return CheckValueStatus.NotSupported_ByRefLike; } - // Allocate default. - value = AllocateValueType(sigElementType, value: null); - copyBack = sigElementType.IsNullableOfT ? ParameterCopyBackAction.CopyNullable : ParameterCopyBackAction.Copy; + value = AllocateValueType(sigElementType, value, fForceTypeChange: false); + isValueType = sigElementType.IsValueType; + copyBack = true; return CheckValueStatus.Success; } - isValueType = false; + if (NeedsSpecialCast()) + { + if (SpecialCast(sigElementType, ref value) == CheckValueStatus.Success) + { + isValueType = true; + copyBack = false; + return CheckValueStatus.Success; + } + } + + isValueType = copyBack = default; return CheckValueStatus.ArgumentException; } if (value == null) { - copyBack = ParameterCopyBackAction.None; - isValueType = RuntimeTypeHandle.IsValueType(this); - if (!isValueType) + if (!RuntimeTypeHandle.IsValueType(this)) + { + isValueType = false; + copyBack = false; + return CheckValueStatus.Success; + } + + if (IsNullableOfT) { - // Normally we don't get here since 'null' was previosuly checked, but due to binders we can. + // Treat as a boxed value. + isValueType = false; + copyBack = false; return CheckValueStatus.Success; } - if (IsByRefLike) + if (RuntimeTypeHandle.IsByRefLike(this)) { + isValueType = copyBack = default; return CheckValueStatus.NotSupported_ByRefLike; } - // Allocate default. - value = AllocateValueType(this, value: null); + // Need to create a default instance of the value type. + value = AllocateValueType(this, value: null, fForceTypeChange: false); + isValueType = true; + copyBack = false; return CheckValueStatus.Success; } + if (NeedsSpecialCast()) + { + if (SpecialCast(this, ref value) == CheckValueStatus.Success) + { + isValueType = true; + copyBack = false; + return CheckValueStatus.Success; + } + } + + isValueType = copyBack = default; + return CheckValueStatus.ArgumentException; + // Check the strange ones courtesy of reflection: - // - Implicit cast between primitives - // - Enum treated as underlying type - // - Pointer (*) types to IntPtr (if dest is IntPtr) - // - System.Reflection.Pointer to appropriate pointer (*) type (if dest is pointer type) - if (IsPointer || IsEnum || IsPrimitive) + // - implicit cast between primitives + // - enum treated as underlying type + // - IntPtr and System.Reflection.Pointer to pointer types + bool NeedsSpecialCast() => IsPointer || IsEnum || IsPrimitive; + + static CheckValueStatus SpecialCast(RuntimeType type, ref object value) { Pointer? pointer = value as Pointer; RuntimeType srcType = pointer != null ? pointer.GetPointerType() : (RuntimeType)value.GetType(); - if (!CanValueSpecialCast(srcType, this)) + if (!CanValueSpecialCast(srcType, type)) { - isValueType = false; - copyBack = ParameterCopyBackAction.None; return CheckValueStatus.ArgumentException; } @@ -3689,42 +3712,18 @@ private CheckValueStatus TryChangeType( else { CorElementType srcElementType = GetUnderlyingType(srcType); - CorElementType dstElementType = GetUnderlyingType(this); + CorElementType dstElementType = GetUnderlyingType(type); if (dstElementType != srcElementType) { - value = InvokeUtils.ConvertOrWiden(srcType, srcElementType, value, this, dstElementType); + value = InvokeUtils.ConvertOrWiden(srcType, srcElementType, value, type, dstElementType); } } - isValueType = true; - copyBack = ParameterCopyBackAction.None; return CheckValueStatus.Success; } - - isValueType = false; - copyBack = ParameterCopyBackAction.None; - return CheckValueStatus.ArgumentException; - } - - internal bool TryByRefFastPath(ref object arg, ref bool isValueType) - { - if (RuntimeTypeHandle.TryGetByRefElementType(this, out RuntimeType? sigElementType) && - ReferenceEquals(sigElementType, arg.GetType())) - { - isValueType = sigElementType.IsValueType; - if (isValueType) - { - // Make a copy to prevent the boxed instance from being directly modified by the method. - arg = AllocateValueType(sigElementType, arg); - } - - return true; - } - - return false; } - internal static CorElementType GetUnderlyingType(RuntimeType type) + private static CorElementType GetUnderlyingType(RuntimeType type) { if (type.IsEnum) { diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 1ed177fdb6c7bc..e8531d7bc42cec 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -1521,15 +1521,9 @@ bool SystemDomain::IsReflectionInvocationMethod(MethodDesc* pMeth) } CONTRACTL_END; - // Check for dynamically generated Invoke methods. - if (pMeth->IsDynamicMethod()) - { - if (strncmp(pMeth->GetName(), "InvokeStub_", ARRAY_SIZE("InvokeStub_") - 1) == 0) - return true; - } - - // All other reflection invocation methods are defined in CoreLib. MethodTable* pCaller = pMeth->GetMethodTable(); + + // All Reflection Invocation methods are defined in CoreLib if (!pCaller->GetModule()->IsSystem()) return false; @@ -1563,6 +1557,7 @@ bool SystemDomain::IsReflectionInvocationMethod(MethodDesc* pMeth) CLASS__MULTICAST_DELEGATE, CLASS__METHOD_INVOKER, CLASS__CONSTRUCTOR_INVOKER, + CLASS__DYNAMIC_METHOD_INVOKER }; static bool fInited = false; @@ -1749,6 +1744,8 @@ StackWalkAction SystemDomain::CallersMethodCallbackWithStackMark(CrawlFrame* pCf Frame* frame = pCf->GetFrame(); _ASSERTE(pCf->IsFrameless() || frame); + + // Skipping reflection frames. We don't need to be quite as exhaustive here // as the security or reflection stack walking code since we know this logic // is only invoked for selected methods in CoreLib itself. So we're @@ -1822,8 +1819,10 @@ StackWalkAction SystemDomain::CallersMethodCallback(CrawlFrame* pCf, VOID* data) pCaller->skip--; return SWA_CONTINUE; } + } + void AppDomain::Create() { STANDARD_VM_CONTRACT; diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 77ba0f3bde22c1..5d02b0881d563f 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -485,6 +485,7 @@ DEFINE_CLASS(MEMBER, Reflection, MemberInfo) DEFINE_CLASS(METHOD_INVOKER, Reflection, MethodInvoker) DEFINE_CLASS(CONSTRUCTOR_INVOKER, Reflection, ConstructorInvoker) +DEFINE_CLASS(DYNAMIC_METHOD_INVOKER,ReflectionEmit, DynamicMethodInvoker) DEFINE_CLASS_U(Reflection, RuntimeMethodInfo, NoClass) DEFINE_FIELD_U(m_handle, ReflectMethodObject, m_pMD) diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index ccb31d35cd8f78..593284165f1651 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -217,8 +217,6 @@ FCFuncEnd() FCFuncStart(gRuntimeMethodHandle) FCFuncElement("_GetCurrentMethod", RuntimeMethodHandle::GetCurrentMethod) FCFuncElement("InvokeMethod", RuntimeMethodHandle::InvokeMethod) - FCFuncElement("ReboxFromNullable", RuntimeMethodHandle::ReboxFromNullable) - FCFuncElement("ReboxToNullable", RuntimeMethodHandle::ReboxToNullable) FCFuncElement("GetImplAttributes", RuntimeMethodHandle::GetImplAttributes) FCFuncElement("GetAttributes", RuntimeMethodHandle::GetAttributes) FCFuncElement("GetDeclaringType", RuntimeMethodHandle::GetDeclaringType) diff --git a/src/coreclr/vm/invokeutil.cpp b/src/coreclr/vm/invokeutil.cpp index 944490f280b721..e50cdc7f1511d5 100644 --- a/src/coreclr/vm/invokeutil.cpp +++ b/src/coreclr/vm/invokeutil.cpp @@ -180,8 +180,20 @@ void InvokeUtil::CopyArg(TypeHandle th, PVOID argRef, ArgDestination *argDest) { case ELEMENT_TYPE_VALUETYPE: { - MethodTable* pMT = th.GetMethodTable(); - CopyValueClassArg(argDest, argRef, pMT, 0); + if (Nullable::IsNullableType(th)) + { + // ASSUMPTION: we only receive T or NULL values, not Nullable values + // and the values are boxed, unlike other value types. + MethodTable* pMT = th.AsMethodTable(); + OBJECTREF src = (OBJECTREF)(Object*)*(PVOID*)argRef; + if (!pMT->UnBoxIntoArg(argDest, src)) + COMPlusThrow(kArgumentException, W("Arg_ObjObj")); + } + else + { + MethodTable* pMT = th.GetMethodTable(); + CopyValueClassArg(argDest, argRef, pMT, 0); + } break; } @@ -201,6 +213,10 @@ void InvokeUtil::CopyArg(TypeHandle th, PVOID argRef, ArgDestination *argDest) { case ELEMENT_TYPE_BYREF: { + // We should never get here for nullable types. Instead invoke + // heads these off and morphs the type handle to not be byref anymore + _ASSERTE(!Nullable::IsNullableType(th.AsTypeDesc()->GetTypeParam())); + *(PVOID *)pArgDst = argRef; break; } @@ -1060,8 +1076,8 @@ OBJECTREF InvokeUtil::GetFieldValue(FieldDesc* pField, TypeHandle fieldType, OBJ CopyValueClass(obj->GetData(), p, fieldType.AsMethodTable()); } - // If it is a Nullable, box it using Nullable conventions. - // TODO: this double allocates on constructions which is wastefull + // If it is a Nullable, box it using Nullable conventions. + // TODO: this double allocates on constructions which is wastefull obj = Nullable::NormalizeBox(obj); break; } @@ -1099,3 +1115,5 @@ OBJECTREF InvokeUtil::GetFieldValue(FieldDesc* pField, TypeHandle fieldType, OBJ return obj; } + + diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 52eb8f90955693..38e2d9c71e7adc 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -2390,6 +2390,7 @@ class MethodTable OBJECTREF FastBox(void** data); #ifndef DACCESS_COMPILE BOOL UnBoxInto(void *dest, OBJECTREF src); + BOOL UnBoxIntoArg(ArgDestination *argDest, OBJECTREF src); void UnBoxIntoUnchecked(void *dest, OBJECTREF src); #endif diff --git a/src/coreclr/vm/methodtable.inl b/src/coreclr/vm/methodtable.inl index 049e4da57f3e14..72b628dae9545a 100644 --- a/src/coreclr/vm/methodtable.inl +++ b/src/coreclr/vm/methodtable.inl @@ -1282,6 +1282,31 @@ inline BOOL MethodTable::UnBoxInto(void *dest, OBJECTREF src) return TRUE; } +//========================================================================================== +// unbox src into argument, making sure src is of the correct type. + +inline BOOL MethodTable::UnBoxIntoArg(ArgDestination *argDest, OBJECTREF src) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + if (Nullable::IsNullableType(TypeHandle(this))) + return Nullable::UnBoxIntoArgNoGC(argDest, src, this); + else + { + if (src == NULL || src->GetMethodTable() != this) + return FALSE; + + CopyValueClassArg(argDest, src->UnBox(), this, 0); + } + return TRUE; +} + //========================================================================================== // unbox src into dest, No checks are done diff --git a/src/coreclr/vm/object.cpp b/src/coreclr/vm/object.cpp index 83ac8e718c3378..451bb1e5dbea94 100644 --- a/src/coreclr/vm/object.cpp +++ b/src/coreclr/vm/object.cpp @@ -929,6 +929,7 @@ BOOL StringObject::CaseInsensitiveCompHelper(_In_reads_(aLength) WCHAR *strAChar // Next char strAChars++; strBChars++; } + } /*============================InternalTrailByteCheck============================ @@ -1616,7 +1617,7 @@ void* Nullable::ValueAddr(MethodTable* nullableMT) { } //=============================================================================== -// Special logic to box a nullable as a boxed +// Special Logic to box a nullable as a boxed OBJECTREF Nullable::Box(void* srcPtr, MethodTable* nullableMT) { @@ -1633,7 +1634,7 @@ OBJECTREF Nullable::Box(void* srcPtr, MethodTable* nullableMT) Nullable* src = (Nullable*) srcPtr; _ASSERTE(IsNullableType(nullableMT)); - // We better have a concrete instantiation, or our field offset asserts are not useful + // We better have a concrete instantiation, or our field offset asserts are not useful _ASSERTE(!nullableMT->ContainsGenericVariables()); if (!*src->HasValueAddr(nullableMT)) @@ -1664,10 +1665,10 @@ BOOL Nullable::UnBox(void* destPtr, OBJECTREF boxedVal, MethodTable* destMT) Nullable* dest = (Nullable*) destPtr; BOOL fRet = TRUE; - // We should only get here if we are unboxing a T as a Nullable + // We should only get here if we are unboxing a T as a Nullable _ASSERTE(IsNullableType(destMT)); - // We better have a concrete instantiation, or our field offset asserts are not useful + // We better have a concrete instantiation, or our field offset asserts are not useful _ASSERTE(!destMT->ContainsGenericVariables()); if (boxedVal == NULL) @@ -1753,6 +1754,101 @@ BOOL Nullable::UnBoxNoGC(void* destPtr, OBJECTREF boxedVal, MethodTable* destMT) return TRUE; } +//=============================================================================== +// Special Logic to unbox a boxed T as a nullable into an argument +// specified by the argDest. +// Does not handle type equivalence (may conservatively return FALSE) +BOOL Nullable::UnBoxIntoArgNoGC(ArgDestination *argDest, OBJECTREF boxedVal, MethodTable* destMT) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + +#if defined(UNIX_AMD64_ABI) + if (argDest->IsStructPassedInRegs()) + { + // We should only get here if we are unboxing a T as a Nullable + _ASSERTE(IsNullableType(destMT)); + + // We better have a concrete instantiation, or our field offset asserts are not useful + _ASSERTE(!destMT->ContainsGenericVariables()); + + if (boxedVal == NULL) + { + // Logically we are doing *dest->HasValueAddr(destMT) = false; + // We zero out the whole structure because it may contain GC references + // and these need to be initialized to zero. (could optimize in the non-GC case) + InitValueClassArg(argDest, destMT); + } + else + { + if (!IsNullableForTypeNoGC(destMT, boxedVal->GetMethodTable())) + { + // For safety's sake, also allow true nullables to be unboxed normally. + // This should not happen normally, but we want to be robust + if (destMT == boxedVal->GetMethodTable()) + { + CopyValueClassArg(argDest, boxedVal->GetData(), destMT, 0); + return TRUE; + } + return FALSE; + } + + Nullable* dest = (Nullable*)argDest->GetStructGenRegDestinationAddress(); + *dest->HasValueAddr(destMT) = true; + int destOffset = (BYTE*)dest->ValueAddr(destMT) - (BYTE*)dest; + CopyValueClassArg(argDest, boxedVal->UnBox(), boxedVal->GetMethodTable(), destOffset); + } + return TRUE; + } + +#endif // UNIX_AMD64_ABI + +#if defined(TARGET_LOONGARCH64) + if (argDest->IsStructPassedInRegs()) + { + // We should only get here if we are unboxing a T as a Nullable + _ASSERTE(IsNullableType(destMT)); + + // We better have a concrete instantiation, or our field offset asserts are not useful + _ASSERTE(!destMT->ContainsGenericVariables()); + + if (boxedVal == NULL) + { + // Logically we are doing *dest->HasValueAddr(destMT) = false; + // We zero out the whole structure becasue it may contain GC references + // and these need to be initialized to zero. (could optimize in the non-GC case) + InitValueClassArg(argDest, destMT); + } + else + { + if (!IsNullableForTypeNoGC(destMT, boxedVal->GetMethodTable())) + { + // For safety's sake, also allow true nullables to be unboxed normally. + // This should not happen normally, but we want to be robust + if (destMT == boxedVal->GetMethodTable()) + { + CopyValueClassArg(argDest, boxedVal->GetData(), destMT, 0); + return TRUE; + } + return FALSE; + } + + CopyValueClassArg(argDest, boxedVal->UnBox(), boxedVal->GetMethodTable(), 0); + *(UINT64*)(argDest->GetStructGenRegDestinationAddress()) = 1; + } + return TRUE; + } + +#endif + + return UnBoxNoGC(argDest->GetDestinationAddress(), boxedVal, destMT); +} + //=============================================================================== // Special Logic to unbox a boxed T as a nullable // Does not do any type checks. @@ -1767,10 +1863,10 @@ void Nullable::UnBoxNoCheck(void* destPtr, OBJECTREF boxedVal, MethodTable* dest CONTRACTL_END; Nullable* dest = (Nullable*) destPtr; - // We should only get here if we are unboxing a T as a Nullable + // We should only get here if we are unboxing a T as a Nullable _ASSERTE(IsNullableType(destMT)); - // We better have a concrete instantiation, or our field offset asserts are not useful + // We better have a concrete instantiation, or our field offset asserts are not useful _ASSERTE(!destMT->ContainsGenericVariables()); if (boxedVal == NULL) diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index ac0798045bd222..7d5e4b121f1360 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -2614,6 +2614,7 @@ class Nullable { static OBJECTREF Box(void* src, MethodTable* nullable); static BOOL UnBox(void* dest, OBJECTREF boxedVal, MethodTable* destMT); static BOOL UnBoxNoGC(void* dest, OBJECTREF boxedVal, MethodTable* destMT); + static BOOL UnBoxIntoArgNoGC(ArgDestination *argDest, OBJECTREF boxedVal, MethodTable* destMT); static void UnBoxNoCheck(void* dest, OBJECTREF boxedVal, MethodTable* destMT); static OBJECTREF BoxedNullableNull(TypeHandle nullableType) { return 0; } diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 2b6e75b6817fb6..197d4ac0262322 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -25,6 +25,29 @@ #include "dbginterface.h" #include "argdestination.h" +/**************************************************************************/ +/* if the type handle 'th' is a byref to a nullable type, return the + type handle to the nullable type in the byref. Otherwise return + the null type handle */ +static TypeHandle NullableTypeOfByref(TypeHandle th) { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if (th.GetVerifierCorElementType() != ELEMENT_TYPE_BYREF) + return TypeHandle(); + + TypeHandle subType = th.AsTypeDesc()->GetTypeParam(); + if (!Nullable::IsNullableType(subType)) + return TypeHandle(); + + return subType; +} + FCIMPL5(Object*, RuntimeFieldHandle::GetValue, ReflectFieldObject *pFieldUNSAFE, Object *instanceUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, ReflectClassBaseObject *pDeclaringTypeUNSAFE, CLR_BOOL *pDomainInitialized) { CONTRACTL { FCALL_CHECK; @@ -122,10 +145,7 @@ FCIMPL2(FC_BOOL_RET, ReflectionInvocation::CanValueSpecialCast, ReflectClassBase } FCIMPLEND -/// -/// Allocate the value type and copy the optional value into it. -/// -FCIMPL2(Object*, ReflectionInvocation::AllocateValueType, ReflectClassBaseObject *pTargetTypeUNSAFE, Object *valueUNSAFE) { +FCIMPL3(Object*, ReflectionInvocation::AllocateValueType, ReflectClassBaseObject *pTargetTypeUNSAFE, Object *valueUNSAFE, CLR_BOOL fForceTypeChange) { CONTRACTL { FCALL_CHECK; PRECONDITION(CheckPointer(pTargetTypeUNSAFE)); @@ -144,23 +164,41 @@ FCIMPL2(Object*, ReflectionInvocation::AllocateValueType, ReflectClassBaseObject gc.obj = gc.value; gc.refTargetType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTargetTypeUNSAFE); - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - TypeHandle targetType = gc.refTargetType->GetType(); - // This method is only intended for value types; it is not called directly by any public APIs - // so we don't expect validation issues here. - _ASSERTE(targetType.IsValueType()); + HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); + CorElementType targetElementType = targetType.GetSignatureCorElementType(); + if (InvokeUtil::IsPrimitiveType(targetElementType) || targetElementType == ELEMENT_TYPE_VALUETYPE) + { + MethodTable* allocMT = targetType.AsMethodTable(); - MethodTable* allocMT = targetType.AsMethodTable(); - _ASSERTE(!allocMT->IsByRefLike()); + if (allocMT->IsByRefLike()) { + COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike")); + } - gc.obj = allocMT->Allocate(); - _ASSERTE(gc.obj != NULL); + if (gc.value != NULL) + { + // ignore the type of the incoming box if fForceTypeChange is set + // and the target type is not nullable + if (!fForceTypeChange || Nullable::IsNullableType(targetType)) + allocMT = gc.value->GetMethodTable(); + } - if (gc.value != NULL) { - _ASSERTE(allocMT->IsEquivalentTo(gc.value->GetMethodTable())); - CopyValueClass(gc.obj->UnBox(), gc.value->UnBox(), allocMT); + // for null Nullable we don't want a default value being created. + // just allow the null value to be passed, as it will be converted to + // a true nullable + if (!(gc.value == NULL && Nullable::IsNullableType(targetType))) + { + // boxed value type are 'read-only' in the sence that you can't + // only the implementor of the value type can expose mutators. + // To insure byrefs don't mutate value classes in place, we make + // a copy (and if we were not given one, we create a null value type + // instance. + gc.obj = allocMT->Allocate(); + + if (gc.value != NULL) + CopyValueClass(gc.obj->UnBox(), gc.value->UnBox(), allocMT); + } } HELPER_METHOD_FRAME_END(); @@ -308,6 +346,29 @@ FCIMPL2(FC_BOOL_RET, RuntimeTypeHandle::IsInstanceOfType, ReflectClassBaseObject } FCIMPLEND +/****************************************************************************/ +/* boxed Nullable are represented as a boxed T, so there is no unboxed + Nullable inside to point at by reference. Because of this a byref + parameters of type Nullable are copied out of the boxed instance + (to a place on the stack), before the call is made (and this copy is + pointed at). After the call returns, this copy must be copied back to + the original argument array. ByRefToNullable, is a simple linked list + that remembers what copy-backs are needed */ + +struct ByRefToNullable { + unsigned argNum; // The argument number for this byrefNullable argument + void* data; // The data to copy back to the ByRefNullable. This points to the stack + TypeHandle type; // The type of Nullable for this argument + ByRefToNullable* next; // list of these + + ByRefToNullable(unsigned aArgNum, void* aData, TypeHandle aType, ByRefToNullable* aNext) { + argNum = aArgNum; + data = aData; + type = aType; + next = aNext; + } +}; + static OBJECTREF InvokeArrayConstructor(TypeHandle th, PVOID* args, int argCnt) { CONTRACTL { @@ -580,6 +641,7 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod, FrameWithCookie *pProtectValueClassFrame = NULL; ValueClassInfo *pValueClasses = NULL; + ByRefToNullable* byRefToNullables = NULL; // if we have the magic Value Class return, we need to allocate that class // and place a pointer to it on the stack. @@ -623,7 +685,8 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod, else pThisPtr = OBJECTREFToObject(gc.retVal); } - else if (!pMeth->GetMethodTable()->IsValueType()) + else + if (!pMeth->GetMethodTable()->IsValueType()) pThisPtr = OBJECTREFToObject(gc.target); else { if (pMeth->IsUnboxingStub()) @@ -698,8 +761,23 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod, bool needsStackCopy = false; ArgDestination argDest(pTransitionBlock, ofs, argit.GetArgLocDescForStructInRegs()); + TypeHandle nullableType = NullableTypeOfByref(th); + if (!nullableType.IsNull()) { + // A boxed Nullable is represented as boxed T. So to pass a Nullable by reference, + // we have to create a Nullable on stack, copy the T into it, then pass it to the callee and + // after returning from the call, copy the T out of the Nullable back to the boxed T. + th = nullableType; + structSize = th.GetSize(); + needsStackCopy = true; + } #ifdef ENREGISTERED_PARAMTYPE_MAXSIZE - if (argit.IsArgPassedByRef()) + else if (argit.IsArgPassedByRef()) + { + needsStackCopy = true; + } +#endif + + if (needsStackCopy) { MethodTable* pMT = th.GetMethodTable(); _ASSERTE(pMT && pMT->IsValueType()); @@ -709,6 +787,11 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod, PVOID pStackCopy = _alloca(structSize); *(PVOID *)pArgDst = pStackCopy; + if (!nullableType.IsNull()) + { + byRefToNullables = new(_alloca(sizeof(ByRefToNullable))) ByRefToNullable(i, pStackCopy, nullableType, byRefToNullables); + } + // save the info into ValueClassInfo if (pMT->ContainsPointers()) { @@ -718,7 +801,6 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod, // We need a new ArgDestination that points to the stack copy argDest = ArgDestination(pStackCopy, 0, NULL); } -#endif InvokeUtil::CopyArg(th, args[i], &argDest); } @@ -792,6 +874,12 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod, gc.retVal = InvokeUtil::CreateObjectAfterInvoke(retTH, &callDescrData.returnValue); } + while (byRefToNullables != NULL) { + OBJECTREF obj = Nullable::Box(byRefToNullables->data, byRefToNullables->type.GetMethodTable()); + SetObjectReference((OBJECTREF*)args[byRefToNullables->argNum], obj); + byRefToNullables = byRefToNullables->next; + } + if (pProtectValueClassFrame != NULL) pProtectValueClassFrame->Pop(pThread); @@ -805,68 +893,6 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod, } FCIMPLEND -/// -/// Convert a boxed value of {T} (which is either {T} or null) to a true boxed Nullable{T}. -/// -FCIMPL2(Object*, RuntimeMethodHandle::ReboxToNullable, Object* pBoxedValUNSAFE, ReflectClassBaseObject *pDestUNSAFE) -{ - FCALL_CONTRACT; - - struct { - OBJECTREF pBoxed; - REFLECTCLASSBASEREF destType; - OBJECTREF retVal; - } gc; - - gc.pBoxed = ObjectToOBJECTREF(pBoxedValUNSAFE); - gc.destType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pDestUNSAFE); - gc.retVal = NULL; - - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - - MethodTable* destMT = gc.destType->GetType().AsMethodTable(); - - gc.retVal = destMT->Allocate(); - void* buffer = gc.retVal->GetData(); - BOOL result = Nullable::UnBox(buffer, gc.pBoxed, destMT); - _ASSERTE(result == TRUE); - - HELPER_METHOD_FRAME_END(); - - return OBJECTREFToObject(gc.retVal); -} -FCIMPLEND - -/// -/// For a true boxed Nullable{T}, re-box to a boxed {T} or null, otherwise just return the input. -/// -FCIMPL1(Object*, RuntimeMethodHandle::ReboxFromNullable, Object* pBoxedValUNSAFE) -{ - FCALL_CONTRACT; - - struct { - OBJECTREF pBoxed; - OBJECTREF retVal; - } gc; - - if (pBoxedValUNSAFE == NULL) - return NULL; - - gc.pBoxed = ObjectToOBJECTREF(pBoxedValUNSAFE); - MethodTable* retMT = gc.pBoxed->GetMethodTable(); - if (!Nullable::IsNullableType(retMT)) - return pBoxedValUNSAFE; - - gc.retVal = NULL; - - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - gc.retVal = Nullable::Box(gc.pBoxed->GetData(), retMT); - HELPER_METHOD_FRAME_END(); - - return OBJECTREFToObject(gc.retVal); -} -FCIMPLEND - struct SkipStruct { StackCrawlMark* pStackMark; MethodDesc* pMeth; diff --git a/src/coreclr/vm/reflectioninvocation.h b/src/coreclr/vm/reflectioninvocation.h index 5e909b6b4c51ec..76eef427a7969f 100644 --- a/src/coreclr/vm/reflectioninvocation.h +++ b/src/coreclr/vm/reflectioninvocation.h @@ -62,7 +62,7 @@ class ReflectionInvocation { // helper fcalls for invocation static FCDECL2(FC_BOOL_RET, CanValueSpecialCast, ReflectClassBaseObject *valueType, ReflectClassBaseObject *targetType); - static FCDECL2(Object*, AllocateValueType, ReflectClassBaseObject *targetType, Object *valueUNSAFE); + static FCDECL3(Object*, AllocateValueType, ReflectClassBaseObject *targetType, Object *valueUNSAFE, CLR_BOOL fForceTypeChange); }; extern "C" void QCALLTYPE ReflectionInvocation_CompileMethod(MethodDesc * pMD); diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index 02ec1e078f4c99..29bdfa08a846f9 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -224,9 +224,6 @@ class RuntimeMethodHandle { static FCDECL4(Object*, InvokeMethod, Object *target, PVOID* args, SignatureNative* pSig, CLR_BOOL fConstructor); - static FCDECL2(Object*, ReboxToNullable, Object *pBoxedValUNSAFE, ReflectClassBaseObject *pDestUNSAFE); - static FCDECL1(Object*, ReboxFromNullable, Object *pBoxedValUNSAFE); - struct StreamingContextData { Object * additionalContext; // additionalContex was changed from OBJECTREF to Object to avoid having a INT32 contextStates; // constructor in this struct. GCC doesn't allow structs with constructors to be diff --git a/src/libraries/System.Diagnostics.StackTrace/tests/StackFrameTests.cs b/src/libraries/System.Diagnostics.StackTrace/tests/StackFrameTests.cs index 67ba8fd483eb12..2f541d9e2a851d 100644 --- a/src/libraries/System.Diagnostics.StackTrace/tests/StackFrameTests.cs +++ b/src/libraries/System.Diagnostics.StackTrace/tests/StackFrameTests.cs @@ -186,8 +186,7 @@ private static void VerifyStackFrameSkipFrames(StackFrame stackFrame, bool isFil } // GetNativeOffset returns StackFrame.OFFSET_UNKNOWN for unknown frames. - // For a positive skipFrame, the GetNativeOffset return value is dependent upon the implementation of reflection - // Invoke() which can be native (where the value would be zero) or managed (where the value is likely non-zero). + // GetNativeOffset returns 0 for known frames with a positive skipFrames. if (skipFrames == int.MaxValue || skipFrames == int.MinValue) { Assert.Equal(StackFrame.OFFSET_UNKNOWN, stackFrame.GetNativeOffset()); @@ -199,7 +198,7 @@ private static void VerifyStackFrameSkipFrames(StackFrame stackFrame, bool isFil } else { - Assert.True(stackFrame.GetNativeOffset() >= 0); + Assert.Equal(0, stackFrame.GetNativeOffset()); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index dcb4f00b78d0e9..db7442f1cd2444 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -635,7 +635,6 @@ - @@ -644,7 +643,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs index c79036380b8e78..48fe08e491026a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs @@ -2,101 +2,25 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Runtime.CompilerServices; namespace System.Reflection { internal sealed partial class ConstructorInvoker { - private readonly RuntimeConstructorInfo _method; - -#if !MONO // Temporary until Mono is updated. - private bool _invoked; - private bool _strategyDetermined; - private InvokerEmitUtil.InvokeFunc? _invokeFunc; -#endif + private readonly RuntimeConstructorInfo _constructorInfo; + public InvocationFlags _invocationFlags; public ConstructorInvoker(RuntimeConstructorInfo constructorInfo) { - _method = constructorInfo; - -#if USE_NATIVE_INVOKE - // Always use the native invoke; useful for testing. - _strategyDetermined = true; -#elif USE_EMIT_INVOKE - // Always use emit invoke (if IsDynamicCodeCompiled == true); useful for testing. - _invoked = true; -#endif + _constructorInfo = constructorInfo; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#if MONO // Temporary until Mono is updated. - public unsafe object? InlinedInvoke(object? obj, Span args, BindingFlags invokeAttr) => InterpretedInvoke(obj, args, invokeAttr); -#else - public unsafe object? InlinedInvoke(object? obj, IntPtr* args, BindingFlags invokeAttr) - { - if (_invokeFunc != null && (invokeAttr & BindingFlags.DoNotWrapExceptions) != 0 && obj == null) - { - return _invokeFunc(target: null, args); - } - return Invoke(obj, args, invokeAttr); - } -#endif - -#if !MONO // Temporary until Mono is updated. [DebuggerStepThrough] [DebuggerHidden] - private unsafe object? Invoke(object? obj, IntPtr* args, BindingFlags invokeAttr) + public unsafe object? InvokeUnsafe(object? obj, IntPtr* args, Span argsForTemporaryMonoSupport, BindingFlags invokeAttr) { - if (!_strategyDetermined) - { - if (!_invoked) - { - // The first time, ignoring race conditions, use the slow path. - _invoked = true; - } - else - { - if (RuntimeFeature.IsDynamicCodeCompiled) - { - _invokeFunc = InvokerEmitUtil.CreateInvokeDelegate(_method); - } - _strategyDetermined = true; - } - } - - object? ret; - if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0) - { - try - { - // For the rarely used scenario of calling the constructor directly through MethodBase.Invoke() - // with a non-null 'obj', we use the slow path to avoid having two emit-based delegates. - if (_invokeFunc != null && obj == null) - { - ret = _invokeFunc(target: null, args); - } - else - { - ret = InterpretedInvoke(obj, args); - } - } - catch (Exception e) - { - throw new TargetInvocationException(e); - } - } - else if (_invokeFunc != null && obj == null) - { - ret = _invokeFunc(target: null, args); - } - else - { - ret = InterpretedInvoke(obj, args); - } - - return ret; + // Todo: add strategy for calling IL Emit-based version + return _constructorInfo.InvokeNonEmitUnsafe(obj, args, argsForTemporaryMonoSupport, invokeAttr); } -#endif } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokeUtils.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokeUtils.cs index c5b26694eebb17..74cf3c81817815 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokeUtils.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokeUtils.cs @@ -16,7 +16,7 @@ public static object ConvertOrWiden(Type srcType, CorElementType srcElementType, if (dstType.IsPointer) { - if (TryConvertPointer(srcObject, out IntPtr dstIntPtr)) + if (TryConvertPointer(srcObject, srcElementType, dstElementType, out IntPtr dstIntPtr)) { return dstIntPtr; } @@ -109,7 +109,7 @@ public static object ConvertOrWiden(Type srcType, CorElementType srcElementType, return dstObject; } - private static bool TryConvertPointer(object srcObject, out IntPtr dstIntPtr) + private static bool TryConvertPointer(object srcObject, CorElementType srcEEType, CorElementType dstEEType, out IntPtr dstIntPtr) { if (srcObject is IntPtr srcIntPtr) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs deleted file mode 100644 index f79b802b62cd4f..00000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs +++ /dev/null @@ -1,187 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Reflection.Emit; - -namespace System.Reflection -{ - internal static class InvokerEmitUtil - { - // If changed, update native stack walking code that also uses this prefix to ignore reflection frames. - private const string InvokeStubPrefix = "InvokeStub_"; - - internal unsafe delegate object? InvokeFunc(object? target, IntPtr* arguments); - - public static unsafe InvokeFunc CreateInvokeDelegate(MethodBase method) - { - Debug.Assert(!method.ContainsGenericParameters); - - bool emitNew = method is RuntimeConstructorInfo; - bool hasThis = !(emitNew || method.IsStatic); - - // The first parameter is unused but supports treating the DynamicMethod as an instance method which is slightly faster than a static. - Type[] delegateParameters = new Type[3] { typeof(object), typeof(object), typeof(IntPtr*) }; - - string declaringTypeName = method.DeclaringType != null ? method.DeclaringType.Name + "." : string.Empty; - var dm = new DynamicMethod( - InvokeStubPrefix + declaringTypeName + method.Name, - returnType: typeof(object), - delegateParameters, - restrictedSkipVisibility: true); - - ILGenerator il = dm.GetILGenerator(); - - // Handle instance methods. - if (hasThis) - { - il.Emit(OpCodes.Ldarg_1); - if (method.DeclaringType!.IsValueType) - { - il.Emit(OpCodes.Unbox, method.DeclaringType); - } - } - - // Push the arguments. - ParameterInfo[] parameters = method.GetParametersNoCopy(); - for (int i = 0; i < parameters.Length; i++) - { - il.Emit(OpCodes.Ldarg_2); - if (i != 0) - { - il.Emit(OpCodes.Ldc_I4, i * IntPtr.Size); - il.Emit(OpCodes.Add); - } - - il.Emit(OpCodes.Call, Methods.ByReferenceOfByte_Value()); // This can be replaced by ldfld once byref fields are available in C# - - RuntimeType parameterType = (RuntimeType)parameters[i].ParameterType; - if (!parameterType.IsByRef) - { - il.Emit(OpCodes.Ldobj, parameterType.IsPointer ? typeof(IntPtr) : parameterType); - } - } - - // Invoke the method. - if (emitNew) - { - il.Emit(OpCodes.Newobj, (ConstructorInfo)method); - } - else if (method.IsStatic || method.DeclaringType!.IsValueType) - { - il.Emit(OpCodes.Call, (MethodInfo)method); - } - else - { - il.Emit(OpCodes.Callvirt, (MethodInfo)method); - } - - // Handle the return. - if (emitNew) - { - Type returnType = method.DeclaringType!; - if (returnType.IsValueType) - { - il.Emit(OpCodes.Box, returnType); - } - } - else - { - RuntimeType returnType; - if (method is RuntimeMethodInfo rmi) - { - returnType = (RuntimeType)rmi.ReturnType; - } - else - { - Debug.Assert(method is DynamicMethod); - returnType = (RuntimeType)((DynamicMethod)method).ReturnType; - } - - if (returnType == typeof(void)) - { - il.Emit(OpCodes.Ldnull); - } - else if (returnType.IsValueType) - { - il.Emit(OpCodes.Box, returnType); - } - else if (returnType.IsPointer) - { - il.Emit(OpCodes.Ldtoken, returnType); - il.Emit(OpCodes.Call, Methods.Type_GetTypeFromHandle()); - il.Emit(OpCodes.Call, Methods.Pointer_Box()); - } - else if (returnType.IsByRef) - { - // Check for null ref return. - Type elementType = returnType.GetElementType()!; - Label retValueOk = il.DefineLabel(); - il.Emit(OpCodes.Dup); - il.Emit(OpCodes.Brtrue_S, retValueOk); - il.Emit(OpCodes.Call, Methods.ThrowHelper_Throw_NullReference_InvokeNullRefReturned()); - il.MarkLabel(retValueOk); - - // Handle per-type differences. - if (elementType.IsValueType) - { - il.Emit(OpCodes.Ldobj, elementType); - il.Emit(OpCodes.Box, elementType); - } - else if (elementType.IsPointer) - { - il.Emit(OpCodes.Ldind_Ref); - il.Emit(OpCodes.Conv_U); - il.Emit(OpCodes.Ldtoken, elementType); - il.Emit(OpCodes.Call, Methods.Type_GetTypeFromHandle()); - il.Emit(OpCodes.Call, Methods.Pointer_Box()); - } - else - { - il.Emit(OpCodes.Ldobj, elementType); - } - } - } - - il.Emit(OpCodes.Ret); - - // Create the delegate; it is also compiled at this point due to restrictedSkipVisibility=true. - return (InvokeFunc)dm.CreateDelegate(typeof(InvokeFunc), target: null); - } - - private static class ThrowHelper - { - public static void Throw_NullReference_InvokeNullRefReturned() - { - throw new NullReferenceException(SR.NullReference_InvokeNullRefReturned); - } - } - - private static class Methods - { - private static MethodInfo? s_ByReferenceOfByte_Value; - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(ByReference<>))] - public static MethodInfo ByReferenceOfByte_Value() => - s_ByReferenceOfByte_Value ?? - (s_ByReferenceOfByte_Value = typeof(ByReference).GetMethod("get_Value")!); - - private static MethodInfo? s_ThrowHelper_Throw_NullReference_InvokeNullRefReturned; - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(ThrowHelper))] - public static MethodInfo ThrowHelper_Throw_NullReference_InvokeNullRefReturned() => - s_ThrowHelper_Throw_NullReference_InvokeNullRefReturned ?? - (s_ThrowHelper_Throw_NullReference_InvokeNullRefReturned = typeof(ThrowHelper).GetMethod(nameof(ThrowHelper.Throw_NullReference_InvokeNullRefReturned))!); - - private static MethodInfo? s_Pointer_Box; - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(Pointer))] - public static MethodInfo Pointer_Box() => - s_Pointer_Box ?? - (s_Pointer_Box = typeof(Pointer).GetMethod(nameof(Pointer.Box), new[] { typeof(void*), typeof(Type) })!); - - private static MethodInfo? s_Type_GetTypeFromHandle; - public static MethodInfo Type_GetTypeFromHandle() => - s_Type_GetTypeFromHandle ?? - (s_Type_GetTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle), new[] { typeof(RuntimeTypeHandle) })!); - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs index 3c93886aa4fd75..3053aab008a491 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs @@ -143,7 +143,7 @@ private protected void ValidateInvokeTarget(object? target) private protected unsafe void CheckArguments( Span copyOfParameters, IntPtr* byrefParameters, - Span shouldCopyBack, + Span shouldCopyBack, ReadOnlySpan parameters, RuntimeType[] sigTypes, Binder? binder, @@ -156,45 +156,35 @@ BindingFlags invokeAttr ParameterInfo[]? paramInfos = null; for (int i = 0; i < parameters.Length; i++) { - ParameterCopyBackAction copyBackArg = default; - bool isValueType = false; + bool copyBackArg = false; + bool isValueType; object? arg = parameters[i]; RuntimeType sigType = sigTypes[i]; if (arg is null) { - // Fast path for null reference types. + // Fast path that avoids calling CheckValue() for reference types. isValueType = RuntimeTypeHandle.IsValueType(sigType); if (isValueType || RuntimeTypeHandle.IsByRef(sigType)) { isValueType = sigType.CheckValue(ref arg, ref copyBackArg, binder, culture, invokeAttr); } } + else if (ReferenceEquals(arg.GetType(), sigType)) + { + // Fast path that avoids calling CheckValue() when argument value matches the signature type. + isValueType = RuntimeTypeHandle.IsValueType(sigType); + } else { - RuntimeType argType = (RuntimeType)arg.GetType(); - - if (ReferenceEquals(argType, sigType)) + paramInfos ??= GetParametersNoCopy(); + ParameterInfo paramInfo = paramInfos[i]; + if (!ReferenceEquals(arg, Type.Missing)) { - // Fast path when the value's type matches the signature type. - isValueType = RuntimeTypeHandle.IsValueType(argType); - } - else if (sigType.TryByRefFastPath(ref arg, ref isValueType)) - { - // Fast path when the value's type matches the signature type of a byref parameter. - copyBackArg = ParameterCopyBackAction.Copy; - } - else if (!ReferenceEquals(arg, Type.Missing)) - { - // Slow path that supports type conversions. isValueType = sigType.CheckValue(ref arg, ref copyBackArg, binder, culture, invokeAttr); } else { - // Convert Type.Missing to the default value. - paramInfos ??= GetParametersNoCopy(); - ParameterInfo paramInfo = paramInfos[i]; - if (paramInfo.DefaultValue == DBNull.Value) { throw new ArgumentException(SR.Arg_VarMissNull, nameof(parameters)); @@ -203,7 +193,6 @@ BindingFlags invokeAttr arg = paramInfo.DefaultValue; if (ReferenceEquals(arg?.GetType(), sigType)) { - // Fast path when the default value's type matches the signature type. isValueType = RuntimeTypeHandle.IsValueType(sigType); } else @@ -220,20 +209,16 @@ BindingFlags invokeAttr // 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. // This separate array is also used to hold default values when 'null' is specified for value - // types, and also used to hold the results from conversions such as from Int16 to Int32. For - // compat, these default values and conversions are not be applied to the incoming arguments. + // types, and also used to hold the results from conversions such as from Int16 to Int32; these + // default values and conversions should not be applied to the incoming arguments. shouldCopyBack[i] = copyBackArg; copyOfParameters[i] = arg; if (isValueType) { -#if !MONO // Temporary until Mono is updated. - Debug.Assert(arg != null); - Debug.Assert( - arg.GetType() == sigType || - (sigType.IsPointer && arg.GetType() == typeof(IntPtr)) || - (sigType.IsByRef && arg.GetType() == RuntimeTypeHandle.GetElementType(sigType)) || - ((sigType.IsEnum || arg.GetType().IsEnum) && RuntimeType.GetUnderlyingType((RuntimeType)arg.GetType()) == RuntimeType.GetUnderlyingType(sigType))); +#if DEBUG + // Once Mono has managed conversion logic, VerifyValueType() can be lifted here as Asserts. + sigType.VerifyValueType(arg); #endif ByReference valueTypeRef = new(ref copyOfParameters[i]!.GetRawData()); *(ByReference*)(byrefParameters + i) = valueTypeRef; @@ -262,11 +247,11 @@ private protected ref struct StackAllocedArguments private object? _arg2; private object? _arg3; #pragma warning restore CA1823, CS0169, IDE0051 - internal ParameterCopyBackAction _copyBack0; + internal bool _copyBack0; #pragma warning disable CA1823, CS0169, IDE0051 // accessed via 'CheckArguments' ref arithmetic - private ParameterCopyBackAction _copyBack1; - private ParameterCopyBackAction _copyBack2; - private ParameterCopyBackAction _copyBack3; + private bool _copyBack1; + private bool _copyBack2; + private bool _copyBack3; #pragma warning restore CA1823, CS0169, IDE0051 } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs index 3adbad39eb21cc..65c4e2bac2e746 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs @@ -2,84 +2,25 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Runtime.CompilerServices; namespace System.Reflection { internal sealed partial class MethodInvoker { - private readonly MethodBase _method; + internal InvocationFlags _invocationFlags; + private readonly RuntimeMethodInfo _methodInfo; - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#if MONO // Temporary until Mono is updated. - public unsafe object? InlinedInvoke(object? obj, Span args, BindingFlags invokeAttr) => InterpretedInvoke(obj, args, invokeAttr); -#else - public unsafe object? InlinedInvoke(object? obj, IntPtr* args, BindingFlags invokeAttr) + public MethodInvoker(RuntimeMethodInfo methodInfo) { - if (_invokeFunc != null && (invokeAttr & BindingFlags.DoNotWrapExceptions) != 0) - { - return _invokeFunc(obj, args); - } - return Invoke(obj, args, invokeAttr); + _methodInfo = methodInfo; } -#endif - -#if !MONO // Temporary until Mono is updated. - private bool _invoked; - private bool _strategyDetermined; - private InvokerEmitUtil.InvokeFunc? _invokeFunc; [DebuggerStepThrough] [DebuggerHidden] - private unsafe object? Invoke(object? obj, IntPtr* args, BindingFlags invokeAttr) + public unsafe object? InvokeUnsafe(object? obj, IntPtr* args, Span argsForTemporaryMonoSupport, BindingFlags invokeAttr) { - if (!_strategyDetermined) - { - if (!_invoked) - { - // The first time, ignoring race conditions, use the slow path. - _invoked = true; - } - else - { - if (RuntimeFeature.IsDynamicCodeCompiled) - { - _invokeFunc = InvokerEmitUtil.CreateInvokeDelegate(_method); - } - _strategyDetermined = true; - } - } - - object? ret; - if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0) - { - try - { - if (_invokeFunc != null) - { - ret = _invokeFunc(obj, args); - } - else - { - ret = InterpretedInvoke(obj, args); - } - } - catch (Exception e) - { - throw new TargetInvocationException(e); - } - } - else if (_invokeFunc != null) - { - ret = _invokeFunc(obj, args); - } - else - { - ret = InterpretedInvoke(obj, args); - } - - return ret; + // Todo: add strategy for calling IL Emit-based version + return _methodInfo.InvokeNonEmitUnsafe(obj, args, argsForTemporaryMonoSupport, invokeAttr); } -#endif } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/ParameterCopyBackAction.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/ParameterCopyBackAction.cs deleted file mode 100644 index e7a9692a7449a2..00000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/ParameterCopyBackAction.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Reflection -{ - /// - /// Determines how an invoke parameter needs to be copied back to the caller's object[] parameters. - /// - internal enum ParameterCopyBackAction : byte - { - None = 0, - Copy = 1, - CopyNullable = 2 - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs index 0ad353cfd38e9f..046979aac68ef5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs @@ -130,7 +130,7 @@ internal void ThrowNoInvokeException() { if (argCount == 0) { - Invoker.InlinedInvoke(obj, args: default, invokeAttr); + Invoker.InvokeUnsafe(obj, args: default, argsForTemporaryMonoSupport: default, invokeAttr); } else if (argCount > MaxStackAllocArgCount) { @@ -141,8 +141,8 @@ internal void ThrowNoInvokeException() { Debug.Assert(parameters != null); StackAllocedArguments argStorage = default; - Span copyOfParameters = new(ref argStorage._arg0, argCount); - Span shouldCopyBackParameters = new(ref argStorage._copyBack0, argCount); + Span copyOfParameters = new Span(ref argStorage._arg0, argCount); + Span shouldCopyBackParameters = new Span(ref argStorage._copyBack0, argCount); StackAllocatedByRefs byrefStorage = default; IntPtr* pByRefStorage = (IntPtr*)&byrefStorage; @@ -157,29 +157,14 @@ internal void ThrowNoInvokeException() culture, invokeAttr); -#if MONO // Temporary until Mono is updated. - Invoker.InlinedInvoke(obj, copyOfParameters, invokeAttr); -#else - Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr); -#endif + Invoker.InvokeUnsafe(obj, pByRefStorage, copyOfParameters, invokeAttr); // Copy modified values out. This should be done only with ByRef or Type.Missing parameters. for (int i = 0; i < argCount; i++) { - ParameterCopyBackAction action = shouldCopyBackParameters[i]; - if (action != ParameterCopyBackAction.None) + if (shouldCopyBackParameters[i]) { - if (action == ParameterCopyBackAction.Copy) - { - parameters[i] = copyOfParameters[i]; - } - else - { - Debug.Assert(action == ParameterCopyBackAction.CopyNullable); - Debug.Assert(copyOfParameters[i] != null); - Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT); - parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]); - } + parameters[i] = copyOfParameters[i]; } } } @@ -202,15 +187,15 @@ private static unsafe void InvokeWithManyArguments( CultureInfo? culture) { object[] objHolder = new object[argCount]; - Span copyOfParameters = new(objHolder, 0, argCount); + Span copyOfParameters = new Span(objHolder, 0, argCount); // We don't check a max stack size since we are invoking a method which // naturally requires a stack size that is dependent on the arg count\size. IntPtr* pByRefStorage = stackalloc IntPtr[argCount]; Buffer.ZeroMemory((byte*)pByRefStorage, (uint)(argCount * sizeof(IntPtr))); - ParameterCopyBackAction* copyBackActions = stackalloc ParameterCopyBackAction[argCount]; - Span shouldCopyBackParameters = new(copyBackActions, argCount); + bool* boolHolder = stackalloc bool[argCount]; + Span shouldCopyBackParameters = new Span(boolHolder, argCount); GCFrameRegistration reg = new(pByRefStorage, (uint)argCount, areByRefs: true); @@ -227,11 +212,7 @@ private static unsafe void InvokeWithManyArguments( culture, invokeAttr); -#if MONO // Temporary until Mono is updated. - ci.Invoker.InlinedInvoke(obj, copyOfParameters, invokeAttr); -#else - ci.Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr); -#endif + ci.Invoker.InvokeUnsafe(obj, pByRefStorage, copyOfParameters, invokeAttr); } finally { @@ -241,20 +222,9 @@ private static unsafe void InvokeWithManyArguments( // Copy modified values out. This should be done only with ByRef or Type.Missing parameters. for (int i = 0; i < argCount; i++) { - ParameterCopyBackAction action = shouldCopyBackParameters[i]; - if (action != ParameterCopyBackAction.None) + if (shouldCopyBackParameters[i]) { - if (action == ParameterCopyBackAction.Copy) - { - parameters[i] = copyOfParameters[i]; - } - else - { - Debug.Assert(action == ParameterCopyBackAction.CopyNullable); - Debug.Assert(copyOfParameters[i] != null); - Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT); - parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]); - } + parameters[i] = copyOfParameters[i]; } } } @@ -284,7 +254,7 @@ public override object Invoke(BindingFlags invokeAttr, Binder? binder, object?[] { if (argCount == 0) { - retValue = Invoker.InlinedInvoke(obj: null, args: default, invokeAttr); + retValue = Invoker.InvokeUnsafe(obj: null, args: default, argsForTemporaryMonoSupport: default, invokeAttr); } else if (argCount > MaxStackAllocArgCount) { @@ -294,8 +264,8 @@ public override object Invoke(BindingFlags invokeAttr, Binder? binder, object?[] { Debug.Assert(parameters != null); StackAllocedArguments argStorage = default; - Span copyOfParameters = new(ref argStorage._arg0, argCount); - Span shouldCopyBackParameters = new(ref argStorage._copyBack0, argCount); + Span copyOfParameters = new Span(ref argStorage._arg0, argCount); + Span shouldCopyBackParameters = new Span(ref argStorage._copyBack0, argCount); StackAllocatedByRefs byrefStorage = default; IntPtr* pByRefStorage = (IntPtr*)&byrefStorage; @@ -310,29 +280,14 @@ public override object Invoke(BindingFlags invokeAttr, Binder? binder, object?[] culture, invokeAttr); -#if MONO // Temporary until Mono is updated. - retValue = Invoker.InlinedInvoke(obj: null, copyOfParameters, invokeAttr); -#else - retValue = Invoker.InlinedInvoke(obj: null, pByRefStorage, invokeAttr); -#endif + retValue = Invoker.InvokeUnsafe(obj: null, pByRefStorage, copyOfParameters, invokeAttr); // Copy modified values out. This should be done only with ByRef or Type.Missing parameters. for (int i = 0; i < argCount; i++) { - ParameterCopyBackAction action = shouldCopyBackParameters[i]; - if (action != ParameterCopyBackAction.None) + if (shouldCopyBackParameters[i]) { - if (action == ParameterCopyBackAction.Copy) - { - parameters[i] = copyOfParameters[i]; - } - else - { - Debug.Assert(action == ParameterCopyBackAction.CopyNullable); - Debug.Assert(copyOfParameters[i] != null); - Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT); - parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]); - } + parameters[i] = copyOfParameters[i]; } } } @@ -357,15 +312,15 @@ public override object Invoke(BindingFlags invokeAttr, Binder? binder, object?[] Debug.Assert(parameters != null); object[] objHolder = new object[argCount]; - Span copyOfParameters = new(objHolder, 0, argCount); + Span copyOfParameters = new Span(objHolder, 0, argCount); // We don't check a max stack size since we are invoking a method which // naturally requires a stack size that is dependent on the arg count\size. IntPtr* pByRefStorage = stackalloc IntPtr[argCount]; Buffer.ZeroMemory((byte*)pByRefStorage, (uint)(argCount * sizeof(IntPtr))); - ParameterCopyBackAction* copyBackActions = stackalloc ParameterCopyBackAction[argCount]; - Span shouldCopyBackParameters = new(copyBackActions, argCount); + bool* boolHolder = stackalloc bool[argCount]; + Span shouldCopyBackParameters = new Span(boolHolder, argCount); GCFrameRegistration reg = new(pByRefStorage, (uint)argCount, areByRefs: true); @@ -383,11 +338,7 @@ public override object Invoke(BindingFlags invokeAttr, Binder? binder, object?[] culture, invokeAttr); -#if MONO // Temporary until Mono is updated. - retValue = ci.Invoker.InlinedInvoke(obj: null, copyOfParameters, invokeAttr); -#else - retValue = ci.Invoker.InlinedInvoke(obj: null, pByRefStorage, invokeAttr); -#endif + retValue = ci.Invoker.InvokeUnsafe(obj: null, pByRefStorage, copyOfParameters, invokeAttr); } finally { @@ -397,20 +348,9 @@ public override object Invoke(BindingFlags invokeAttr, Binder? binder, object?[] // Copy modified values out. This should be done only with ByRef or Type.Missing parameters. for (int i = 0; i < argCount; i++) { - ParameterCopyBackAction action = shouldCopyBackParameters[i]; - if (action != ParameterCopyBackAction.None) + if (shouldCopyBackParameters[i]) { - if (action == ParameterCopyBackAction.Copy) - { - parameters[i] = copyOfParameters[i]; - } - else - { - Debug.Assert(action == ParameterCopyBackAction.CopyNullable); - Debug.Assert(copyOfParameters[i] != null); - Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT); - parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]); - } + parameters[i] = copyOfParameters[i]; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs index 14b7372605d7dc..87404f4723de1d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs @@ -126,7 +126,7 @@ private void ThrowNoInvokeException() { if (argCount == 0) { - retValue = Invoker.InlinedInvoke(obj, args: default, invokeAttr); + retValue = Invoker.InvokeUnsafe(obj, args: default, argsForTemporaryMonoSupport: default, invokeAttr); } else if (argCount > MaxStackAllocArgCount) { @@ -137,8 +137,8 @@ private void ThrowNoInvokeException() { Debug.Assert(parameters != null); StackAllocedArguments argStorage = default; - Span copyOfParameters = new(ref argStorage._arg0, argCount); - Span shouldCopyBackParameters = new(ref argStorage._copyBack0, argCount); + Span copyOfParameters = new Span(ref argStorage._arg0, argCount); + Span shouldCopyBackParameters = new Span(ref argStorage._copyBack0, argCount); StackAllocatedByRefs byrefStorage = default; IntPtr* pByRefStorage = (IntPtr*)&byrefStorage; @@ -153,29 +153,14 @@ private void ThrowNoInvokeException() culture, invokeAttr); -#if MONO // Temporary until Mono is updated. - retValue = Invoker.InlinedInvoke(obj, copyOfParameters, invokeAttr); -#else - retValue = Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr); -#endif + retValue = Invoker.InvokeUnsafe(obj, pByRefStorage, copyOfParameters, invokeAttr); // Copy modified values out. This should be done only with ByRef or Type.Missing parameters. for (int i = 0; i < argCount; i++) { - ParameterCopyBackAction action = shouldCopyBackParameters[i]; - if (action != ParameterCopyBackAction.None) + if (shouldCopyBackParameters[i]) { - if (action == ParameterCopyBackAction.Copy) - { - parameters[i] = copyOfParameters[i]; - } - else - { - Debug.Assert(action == ParameterCopyBackAction.CopyNullable); - Debug.Assert(copyOfParameters[i] != null); - Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT); - parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]); - } + parameters[i] = copyOfParameters[i]; } } } @@ -198,15 +183,15 @@ private void ThrowNoInvokeException() CultureInfo? culture) { object[] objHolder = new object[argCount]; - Span copyOfParameters = new(objHolder, 0, argCount); + Span copyOfParameters = new Span(objHolder, 0, argCount); // We don't check a max stack size since we are invoking a method which // naturally requires a stack size that is dependent on the arg count\size. IntPtr* pByRefStorage = stackalloc IntPtr[argCount]; Buffer.ZeroMemory((byte*)pByRefStorage, (uint)(argCount * sizeof(IntPtr))); - ParameterCopyBackAction* copyBackActions = stackalloc ParameterCopyBackAction[argCount]; - Span shouldCopyBackParameters = new(copyBackActions, argCount); + bool* boolHolder = stackalloc bool[argCount]; + Span shouldCopyBackParameters = new Span(boolHolder, argCount); GCFrameRegistration reg = new(pByRefStorage, (uint)argCount, areByRefs: true); @@ -224,11 +209,7 @@ private void ThrowNoInvokeException() culture, invokeAttr); -#if MONO // Temporary until Mono is updated. - retValue = mi.Invoker.InlinedInvoke(obj, copyOfParameters, invokeAttr); -#else - retValue = mi.Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr); -#endif + retValue = mi.Invoker.InvokeUnsafe(obj, pByRefStorage, copyOfParameters, invokeAttr); } finally { @@ -238,20 +219,9 @@ private void ThrowNoInvokeException() // Copy modified values out. This should be done only with ByRef or Type.Missing parameters. for (int i = 0; i < argCount; i++) { - ParameterCopyBackAction action = shouldCopyBackParameters[i]; - if (action != ParameterCopyBackAction.None) + if (shouldCopyBackParameters[i]) { - if (action == ParameterCopyBackAction.Copy) - { - parameters[i] = copyOfParameters[i]; - } - else - { - Debug.Assert(action == ParameterCopyBackAction.CopyNullable); - Debug.Assert(copyOfParameters[i] != null); - Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT); - parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]); - } + parameters[i] = copyOfParameters[i]; } } diff --git a/src/libraries/System.Reflection/tests/MethodInfoTests.cs b/src/libraries/System.Reflection/tests/MethodInfoTests.cs index 678560ecb003f5..47d1bad96d6a40 100644 --- a/src/libraries/System.Reflection/tests/MethodInfoTests.cs +++ b/src/libraries/System.Reflection/tests/MethodInfoTests.cs @@ -709,68 +709,6 @@ static MethodInfo GetMethod(string name) => typeof(EnumMethods).GetMethod( name, BindingFlags.Public | BindingFlags.Static)!; } - [Fact] - public void ValueTypeMembers_WithOverrides() - { - ValueTypeWithOverrides obj = new() { Id = 1 }; - - // ToString is overridden. - Assert.Equal("Hello", (string)GetMethod(typeof(ValueTypeWithOverrides), nameof(ValueTypeWithOverrides.ToString)). - Invoke(obj, null)); - - // Ensure a normal method works. - Assert.Equal(1, (int)GetMethod(typeof(ValueTypeWithOverrides), nameof(ValueTypeWithOverrides.GetId)). - Invoke(obj, null)); - } - - [Fact] - public void ValueTypeMembers_WithoutOverrides() - { - ValueTypeWithoutOverrides obj = new() { Id = 1 }; - - // ToString is not overridden. - Assert.Equal(typeof(ValueTypeWithoutOverrides).ToString(), (string)GetMethod(typeof(ValueTypeWithoutOverrides), nameof(ValueTypeWithoutOverrides.ToString)). - Invoke(obj, null)); - - // Ensure a normal method works. - Assert.Equal(1, (int)GetMethod(typeof(ValueTypeWithoutOverrides), nameof(ValueTypeWithoutOverrides.GetId)). - Invoke(obj, null)); - } - - [Fact] - public void NullableOfTMembers() - { - // Ensure calling a method on Nullable works. - MethodInfo mi = GetMethod(typeof(int?), nameof(Nullable.GetValueOrDefault)); - Assert.Equal(42, mi.Invoke(42, null)); - } - - [Fact] - public void CopyBackWithByRefArgs() - { - object i = 42; - object[] args = new object[] { i }; - GetMethod(typeof(CopyBackMethods), nameof(CopyBackMethods.IncrementByRef)).Invoke(null, args); - Assert.Equal(43, (int)args[0]); - Assert.NotSame(i, args[0]); // A copy should be made; a boxed instance should never be directly updated. - - i = 42; - args = new object[] { i }; - GetMethod(typeof(CopyBackMethods), nameof(CopyBackMethods.IncrementByNullableRef)).Invoke(null, args); - Assert.Equal(43, (int)args[0]); - Assert.NotSame(i, args[0]); - - object o = null; - args = new object[] { o }; - GetMethod(typeof(CopyBackMethods), nameof(CopyBackMethods.SetToNonNullByRef)).Invoke(null, args); - Assert.NotNull(args[0]); - - o = new object(); - args = new object[] { o }; - GetMethod(typeof(CopyBackMethods), nameof(CopyBackMethods.SetToNullByRef)).Invoke(null, args); - Assert.Null(args[0]); - } - //Methods for Reflection Metadata private void DummyMethod1(string str, int iValue, long lValue) { @@ -1063,29 +1001,6 @@ public static bool ValueToNullBoxed(ref int? i, int expected) } } - public static class CopyBackMethods - { - public static void IncrementByRef(ref int i) - { - i++; - } - - public static void IncrementByNullableRef(ref int? i) - { - i++; - } - - public static void SetToNullByRef(ref object o) - { - o = null; - } - - public static void SetToNonNullByRef(ref object o) - { - o = new object(); - } - } - public enum ColorsInt : int { Red = 1 @@ -1101,19 +1016,6 @@ public enum OtherColorsInt : int Red = 1 } - public struct ValueTypeWithOverrides - { - public int Id; - public override string ToString() => "Hello"; - public int GetId() => Id; - } - - public struct ValueTypeWithoutOverrides - { - public int Id; - public int GetId() => Id; - } - public static class EnumMethods { public static bool PassColorsInt(ColorsInt color) diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs b/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs index 250a84819b82e0..9a7ed450afe045 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs @@ -566,23 +566,10 @@ private static void CheckObjectTypeIntegrity(ISerializable serializable) private static void SanityCheckBlob(object obj, TypeSerializableValue[] blobs) { // These types are unstable during serialization and produce different blobs. - string name = obj.GetType().FullName; if (obj is WeakReference || obj is Collections.Specialized.HybridDictionary || obj is Color || - name == "System.Collections.SortedList+SyncSortedList" || - // Due to non-deterministic field ordering the types below will fail when using IL Emit-based Invoke. - // The types above may also be failing for the same reason. - // Remove these cases once https://github.com/dotnet/runtime/issues/46272 is fixed. - name == "System.Collections.Comparer" || - name == "System.Collections.Hashtable" || - name == "System.Collections.SortedList" || - name == "System.Collections.Specialized.ListDictionary" || - name == "System.CultureAwareComparer" || - name == "System.Globalization.CompareInfo" || - name == "System.Net.Cookie" || - name == "System.Net.CookieCollection" || - name == "System.Net.CookieContainer") + obj.GetType().FullName == "System.Collections.SortedList+SyncSortedList") { return; } diff --git a/src/libraries/System.Runtime/tests/System/Reflection/PointerTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/PointerTests.cs index f89f19ec5d19ef..cfe5f5b20ace88 100644 --- a/src/libraries/System.Runtime/tests/System/Reflection/PointerTests.cs +++ b/src/libraries/System.Runtime/tests/System/Reflection/PointerTests.cs @@ -16,27 +16,15 @@ public void Method(byte* ptr, int expected) Assert.Equal(expected, unchecked((int)ptr)); } - public void MethodWithSystemPointer(Pointer ptr, int expected) - { - Assert.Equal(expected, unchecked((int)Pointer.Unbox(ptr))); - } - public bool* Return(int expected) { return unchecked((bool*)expected); } - - public object ReturnWithSystemPointer(int expected) - { - return Pointer.Box((byte*)expected, typeof(byte*)); - } } unsafe delegate void MethodDelegate(byte* ptr, int expected); - unsafe delegate void MethodDelegateWithSystemPointer(Pointer ptr, int expected); unsafe delegate bool* ReturnDelegate(int expected); - unsafe delegate object ReturnDelegateWithSystemPointer(int expected); public unsafe class PointerTests { @@ -254,15 +242,6 @@ public void PointerMethodDelegateParameter(int value) d.DynamicInvoke(Pointer.Box(unchecked((void*)value), typeof(byte*)), value); } - [Theory] - [MemberData(nameof(Pointers))] - public void MethodDelegateParameter_SystemPointer(int value) - { - var obj = new PointerHolder(); - MethodDelegateWithSystemPointer d = obj.MethodWithSystemPointer; - d.DynamicInvoke(Pointer.Box(unchecked((void*)value), typeof(byte*)), value); - } - [Fact] public void PointerNullMethodDelegateParameter() { @@ -271,24 +250,6 @@ public void PointerNullMethodDelegateParameter() d.DynamicInvoke(null, 0); } - [Fact] - public void PointerNullMethodDelegateParameter_InvalidType_SystemPointer() - { - // An null is not converted to a System.Pointer. - var obj = new PointerHolder(); - MethodDelegateWithSystemPointer d = obj.MethodWithSystemPointer; - try - { - d.DynamicInvoke(null, 0); - } - catch (TargetInvocationException e) when (e.InnerException is ArgumentException) - { - return; - } - - Assert.Fail("Inner exception should be ArgumentException."); - } - [Theory] [MemberData(nameof(Pointers))] public void IntPtrMethodDelegateParameter(int value) @@ -298,19 +259,6 @@ public void IntPtrMethodDelegateParameter(int value) d.DynamicInvoke((IntPtr)value, value); } - [Theory] - [MemberData(nameof(Pointers))] - public void IntPtrMethodDelegateParameter_InvalidType_SystemPointer(int value) - { - // An IntPtr is not converted to a System.Pointer. - var obj = new PointerHolder(); - MethodDelegateWithSystemPointer d = obj.MethodWithSystemPointer; - AssertExtensions.Throws(null, () => - { - d.DynamicInvoke((IntPtr)value, value); - }); - } - [Theory] [MemberData(nameof(Pointers))] public void PointerMethodDelegateParameter_InvalidType(int value) @@ -323,16 +271,6 @@ public void PointerMethodDelegateParameter_InvalidType(int value) }); } - [Theory] - [MemberData(nameof(Pointers))] - public void PointerMethodDelegateParameter_InvalidType_SystemPointer(int value) - { - // Although the type boxed doesn't match, when unboxing void* is returned. - var obj = new PointerHolder(); - MethodDelegateWithSystemPointer d = obj.MethodWithSystemPointer; - d.DynamicInvoke(Pointer.Box(unchecked((void*)value), typeof(long*)), value); - } - [Theory] [MemberData(nameof(Pointers))] public void PointerMethodDelegateReturn(int value) @@ -344,17 +282,5 @@ public void PointerMethodDelegateReturn(int value) void* actualPointer = Pointer.Unbox(actualValue); Assert.Equal(value, unchecked((int)actualPointer)); } - - [Theory] - [MemberData(nameof(Pointers))] - public void PointerMethodDelegateReturn_SystemPointer(int value) - { - var obj = new PointerHolder(); - ReturnDelegateWithSystemPointer d = obj.ReturnWithSystemPointer; - object actualValue = d.DynamicInvoke(value); - Assert.IsType(actualValue); - void* actualPointer = Pointer.Unbox(actualValue); - Assert.Equal(value, unchecked((int)actualPointer)); - } } } diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 51f1b835032e0a..18ec40cc3ca0b2 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -1,4 +1,4 @@ - + false true @@ -200,12 +200,10 @@ - - diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.Mono.cs deleted file mode 100644 index 49b13afc66511a..00000000000000 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.Mono.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System.Reflection -{ - internal partial class ConstructorInvoker - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe object? InterpretedInvoke(object? obj, Span args, BindingFlags invokeAttr) - { - Exception exc; - object? o; - - if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0) - { - try - { - o = _method.InternalInvoke(obj, args, out exc); - } - catch (MethodAccessException) - { - throw; - } - catch (OverflowException) - { - throw; - } - catch (Exception e) - { - throw new TargetInvocationException(e); - } - } - else - { - o = _method.InternalInvoke(obj, args, out exc); - } - - if (exc != null) - throw exc; - - return obj == null ? o : null; - } - } -} diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/MethodInvoker.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/MethodInvoker.Mono.cs deleted file mode 100644 index 2ea61086c7a4f0..00000000000000 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/MethodInvoker.Mono.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System.Reflection -{ - internal partial class MethodInvoker - { - public MethodInvoker(MethodBase method) - { - _method = method; - -#if USE_NATIVE_INVOKE - // Always use the native invoke; useful for testing. - _strategyDetermined = true; -#elif USE_EMIT_INVOKE - // Always use emit invoke (if IsDynamicCodeCompiled == true); useful for testing. - _invoked = true; -#endif - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe object? InterpretedInvoke(object? obj, Span args, BindingFlags invokeAttr) - { - Exception? exc; - object? o; - - if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0) - { - try - { - o = ((RuntimeMethodInfo)_method).InternalInvoke(obj, args, out exc); - } - catch (Mono.NullByRefReturnException) - { - throw new NullReferenceException(); - } - catch (OverflowException) - { - throw; - } - catch (Exception e) - { - throw new TargetInvocationException(e); - } - } - else - { - try - { - o = ((RuntimeMethodInfo)_method).InternalInvoke(obj, args, out exc); - } - catch (Mono.NullByRefReturnException) - { - throw new NullReferenceException(); - } - } - - if (exc != null) - throw exc; - - return o; - } - } -} diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs index 73aa4744e8f562..8d6c46f153ca2d 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs @@ -278,7 +278,7 @@ public override void SetValue(object? obj, object? val, BindingFlags invokeAttr, if (val != null) { RuntimeType fieldType = (RuntimeType)FieldType; - ParameterCopyBackAction _ = default; + bool _ = false; if (!ReferenceEquals(val.GetType(), fieldType)) { diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.Mono.cs index 146739163199be..208640b790de5e 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.Mono.cs @@ -384,6 +384,49 @@ internal RuntimeType[] ArgumentTypes [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern object? InternalInvoke(object? obj, in Span parameters, out Exception? exc); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal unsafe object? InvokeNonEmitUnsafe(object? obj, IntPtr* byrefParameters, Span argsForTemporaryMonoSupport, BindingFlags invokeAttr) + { + Exception? exc; + object? o; + + if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0) + { + try + { + o = InternalInvoke(obj, argsForTemporaryMonoSupport, out exc); + } + catch (Mono.NullByRefReturnException) + { + throw new NullReferenceException(); + } + catch (OverflowException) + { + throw; + } + catch (Exception e) + { + throw new TargetInvocationException(e); + } + } + else + { + try + { + o = InternalInvoke(obj, argsForTemporaryMonoSupport, out exc); + } + catch (Mono.NullByRefReturnException) + { + throw new NullReferenceException(); + } + } + + if (exc != null) + throw exc; + + return o; + } + public override RuntimeMethodHandle MethodHandle { get @@ -815,6 +858,43 @@ private static void InvokeClassConstructor() [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern object InternalInvoke(object? obj, in Span parameters, out Exception exc); + [DebuggerHidden] + [DebuggerStepThrough] + internal unsafe object? InvokeNonEmitUnsafe(object? obj, IntPtr* byrefParameters, Span argsForTemporaryMonoSupport, BindingFlags invokeAttr) + { + Exception exc; + object? o; + + if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0) + { + try + { + o = InternalInvoke(obj, argsForTemporaryMonoSupport, out exc); + } + catch (MethodAccessException) + { + throw; + } + catch (OverflowException) + { + throw; + } + catch (Exception e) + { + throw new TargetInvocationException(e); + } + } + else + { + o = InternalInvoke(obj, argsForTemporaryMonoSupport, out exc); + } + + if (exc != null) + throw exc; + + return obj == null ? o : null; + } + public override RuntimeMethodHandle MethodHandle { get diff --git a/src/mono/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs b/src/mono/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs index d57633d1a52a8e..2f0e1b726082f1 100644 --- a/src/mono/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs +++ b/src/mono/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs @@ -85,8 +85,5 @@ internal bool IsNullHandle() { return value == IntPtr.Zero; } - - // Temporary placeholder until Mono adds support for supporting boxing true Nullables. - internal static object? ReboxFromNullable(object? src) => src; } } diff --git a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs index 91ba5db495c051..607f9b462a75f2 100644 --- a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs @@ -1635,20 +1635,29 @@ internal override FieldInfo GetField(FieldInfo fromNoninstanciated) unsafe { - return ctor.Invoker.InlinedInvoke( + return ctor.Invoker.InvokeUnsafe( obj: null, args: default, + argsForTemporaryMonoSupport: default, wrapExceptions ? BindingFlags.Default : BindingFlags.DoNotWrapExceptions); } } + // Once Mono has managed conversion logic, this method can be removed and the Core + // implementation of this method moved to RuntimeMethod.Invoke(). +#if DEBUG +#pragma warning disable CA1822 + internal void VerifyValueType(object? value) { } +#pragma warning restore CA1822 +#endif + /// /// Verify and optionally convert the value for special cases. /// /// Not yet implemented in Mono: True if the value should be considered a value type, False otherwise internal bool CheckValue( ref object? value, - ref ParameterCopyBackAction copyBack, + ref bool copyBack, Binder? binder, CultureInfo? culture, BindingFlags invokeAttr) @@ -1656,7 +1665,7 @@ internal bool CheckValue( // Already fast-pathed by the caller. Debug.Assert(!ReferenceEquals(value?.GetType(), this)); - copyBack = ParameterCopyBackAction.Copy; + copyBack = true; CheckValueStatus status = TryConvertToType(ref value); if (status == CheckValueStatus.Success) @@ -1760,11 +1769,6 @@ private CheckValueStatus TryConvertToType(ref object? value) return CheckValueStatus.ArgumentException; } - // Stub method to allow for shared code with CoreClr. -#pragma warning disable CA1822 - internal bool TryByRefFastPath(ref object arg, ref bool isValueType) => false; -#pragma warning restore CA1822 - // Binder uses some incompatible conversion rules. For example // int value cannot be used with decimal parameter but in other // ways it's more flexible than normal convertor, for example @@ -2025,7 +2029,7 @@ internal static object CreateInstanceForAnotherGenericParameter( unsafe { - return ctor.Invoker.InlinedInvoke(obj: null, args: default, BindingFlags.Default)!; + return ctor.Invoker.InvokeUnsafe(obj: null, args: default, argsForTemporaryMonoSupport: default, BindingFlags.Default)!; } } @@ -2335,8 +2339,6 @@ public override string? FullName public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore(other); - internal bool IsNullableOfT => Nullable.GetUnderlyingType(this) != null; - public override bool IsSZArray { get diff --git a/src/tests/Loader/binding/tracing/BinderTracingTest.EventHandlers.cs b/src/tests/Loader/binding/tracing/BinderTracingTest.EventHandlers.cs index 21561ec3ff151e..9596b84510f507 100644 --- a/src/tests/Loader/binding/tracing/BinderTracingTest.EventHandlers.cs +++ b/src/tests/Loader/binding/tracing/BinderTracingTest.EventHandlers.cs @@ -275,9 +275,7 @@ public static BindOperation AssemblyLoadFromResolveHandler_LoadDependency() }; } - [BinderTest(isolate: true, - additionalLoadsToTrack: new string[] { "AssemblyToLoadDependency" }, - activeIssue: "https://github.com/dotnet/runtime/issues/68521")] // Emit-based Invoke causes an extra load. + [BinderTest(isolate: true, additionalLoadsToTrack: new string[] { "AssemblyToLoadDependency" })] public static BindOperation AssemblyLoadFromResolveHandler_MissingDependency() { string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); diff --git a/src/tests/Loader/binding/tracing/BinderTracingTest.ResolutionFlow.cs b/src/tests/Loader/binding/tracing/BinderTracingTest.ResolutionFlow.cs index 08efd201316367..780162821f5081 100644 --- a/src/tests/Loader/binding/tracing/BinderTracingTest.ResolutionFlow.cs +++ b/src/tests/Loader/binding/tracing/BinderTracingTest.ResolutionFlow.cs @@ -73,8 +73,7 @@ public static BindOperation FindInLoadContext_DefaultALC() // ResolutionAttempted : DefaultAssemblyLoadContextFallback (CustomALC) [AssemblyNotFound] // ResolutionAttempted : AssemblyLoadContextResolvingEvent (CustomALC) [AssemblyNotFound] // ResolutionAttempted : AppDomainAssemblyResolveEvent (CustomALC) [AssemblyNotFound] - [BinderTest(isolate: true, testSetup: nameof(LoadSubdirectoryAssembly_InstanceALC), - activeIssue: "https://github.com/dotnet/runtime/issues/68521")] // Emit-based Invoke causes an extra load. + [BinderTest(isolate: true, testSetup: nameof(LoadSubdirectoryAssembly_InstanceALC))] public static BindOperation FindInLoadContext_CustomALC_IncompatibleVersion() { var assemblyName = new AssemblyName($"{SubdirectoryAssemblyName}, Version=4.3.2.1"); @@ -181,9 +180,7 @@ public static BindOperation ApplicationAssemblies_IncompatibleVersion() // ResolutionAttempted : ApplicationAssemblies (DefaultALC) [MismatchedAssemblyName] // ResolutionAttempted : AssemblyLoadContextResolvingEvent (DefaultALC) [AssemblyNotFound] // ResolutionAttempted : AppDomainAssemblyResolveEvent (DefaultALC) [AssemblyNotFound] - [BinderTest(isolate: true, - additionalLoadsToTrack: new string[] { DependentAssemblyName + "_Copy" }, - activeIssue: "https://github.com/dotnet/runtime/issues/68521")] // Emit-based Invoke causes AssemblyNotFound instead of MismatchedAssemblyName. + [BinderTest(isolate: true, additionalLoadsToTrack: new string[] { DependentAssemblyName + "_Copy" } )] public static BindOperation ApplicationAssemblies_MismatchedAssemblyName() { var assemblyName = new AssemblyName($"{DependentAssemblyName}_Copy, Culture=neutral, PublicKeyToken=null"); @@ -339,8 +336,7 @@ public static BindOperation ResolveSatelliteAssembly() // ResolutionAttempted : ApplicationAssemblies (DefaultALC) [AssemblyNotFound] // ResolutionAttempted : DefaultAssemblyLoadContextFallback (CustomALC) [AssemblyNotFound] // ResolutionAttempted : AssemblyLoadContextResolvingEvent (CustomALC) [Success] - [BinderTest(isolate: true, - activeIssue: "https://github.com/dotnet/runtime/issues/68521")] // Emit-based Invoke causes an extra load. + [BinderTest(isolate: true)] public static BindOperation AssemblyLoadContextResolvingEvent_CustomALC() { var assemblyName = new AssemblyName(SubdirectoryAssemblyName); @@ -409,8 +405,7 @@ public static BindOperation AssemblyLoadContextResolvingEvent_DefaultALC() // ResolutionAttempted : ApplicationAssemblies (DefaultALC) [AssemblyNotFound] // ResolutionAttempted : DefaultAssemblyLoadContextFallback (CustomALC) [AssemblyNotFound] // ResolutionAttempted : AssemblyLoadContextResolvingEvent (CustomALC) [Exception] - [BinderTest(isolate: true, - activeIssue: "https://github.com/dotnet/runtime/issues/68521")] // Emit-based Invoke causes an extra load. + [BinderTest(isolate: true)] public static BindOperation AssemblyLoadContextResolvingEvent_CustomALC_Exception() { var assemblyName = new AssemblyName(SubdirectoryAssemblyName); @@ -476,8 +471,7 @@ public static BindOperation AssemblyLoadContextResolvingEvent_DefaultALC_Excepti // ResolutionAttempted : DefaultAssemblyLoadContextFallback (CustomALC) [AssemblyNotFound] // ResolutionAttempted : AssemblyLoadContextResolvingEvent (CustomALC) [AssemblyNotFound] // ResolutionAttempted : AppDomainAssemblyResolveEvent (CustomALC) [Success] - [BinderTest(isolate: true, - activeIssue: "https://github.com/dotnet/runtime/issues/68521")] // Emit-based Invoke causes an extra load. + [BinderTest(isolate: true)] public static BindOperation AppDomainAssemblyResolveEvent_CustomALC() { var assemblyName = new AssemblyName(SubdirectoryAssemblyName); @@ -550,8 +544,7 @@ public static BindOperation AppDomainAssemblyResolveEvent_DefaultALC() // ResolutionAttempted : DefaultAssemblyLoadContextFallback (CustomALC) [AssemblyNotFound] // ResolutionAttempted : AssemblyLoadContextResolvingEvent (CustomALC) [AssemblyNotFound] // ResolutionAttempted : AppDomainAssemblyResolveEvent (CustomALC) [Exception] - [BinderTest(isolate: true, - activeIssue: "https://github.com/dotnet/runtime/issues/68521")] // Emit-based Invoke causes an extra load. + [BinderTest(isolate: true)] public static BindOperation AppDomainAssemblyResolveEvent_Exception() { var assemblyName = new AssemblyName(SubdirectoryAssemblyName); diff --git a/src/tests/Loader/binding/tracing/BinderTracingTest.cs b/src/tests/Loader/binding/tracing/BinderTracingTest.cs index bbafdf07db1f12..d3f1fd8eee3db7 100644 --- a/src/tests/Loader/binding/tracing/BinderTracingTest.cs +++ b/src/tests/Loader/binding/tracing/BinderTracingTest.cs @@ -18,15 +18,13 @@ namespace BinderTracingTests class BinderTestAttribute : Attribute { public bool Isolate { get; private set; } - public string ActiveIssue { get; private set; } public string TestSetup { get; private set; } public string[] AdditionalLoadsToTrack { get; private set; } - public BinderTestAttribute(bool isolate = false, string testSetup = null, string[] additionalLoadsToTrack = null, string activeIssue = null) + public BinderTestAttribute(bool isolate = false, string testSetup = null, string[] additionalLoadsToTrack = null) { Isolate = isolate; TestSetup = testSetup; AdditionalLoadsToTrack = additionalLoadsToTrack; - ActiveIssue = activeIssue; } } @@ -77,9 +75,7 @@ public static bool RunAllTests() { MethodInfo[] methods = typeof(BinderTracingTest) .GetMethods(BindingFlags.Public | BindingFlags.Static) - .Where(m => m.GetCustomAttribute() != null && - m.ReturnType == typeof(BindOperation) && - m.GetCustomAttribute().ActiveIssue == null) + .Where(m => m.GetCustomAttribute() != null && m.ReturnType == typeof(BindOperation)) .ToArray(); foreach (var method in methods) @@ -114,10 +110,7 @@ public static int Main(string[] args) // Run specific test - first argument should be the test method name MethodInfo method = typeof(BinderTracingTest) .GetMethod(args[0], BindingFlags.Public | BindingFlags.Static); - Assert.True(method != null && - method.GetCustomAttribute() != null && - method.ReturnType == typeof(BindOperation) && - method.GetCustomAttribute().ActiveIssue == null); + Assert.True(method != null && method.GetCustomAttribute() != null && method.ReturnType == typeof(BindOperation)); success = RunSingleTest(method); } } diff --git a/src/tests/issues.targets b/src/tests/issues.targets index a12139bd64117c..9f29f7072b7a98 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -666,18 +666,6 @@ https://github.com/dotnet/runtime/issues/57856 - - https://github.com/dotnet/runtime/issues/68837 - - - https://github.com/dotnet/runtime/issues/68837 - - - https://github.com/dotnet/runtime/issues/68837 - - - https://github.com/dotnet/runtime/issues/68837 - https://github.com/dotnet/runtime/issues/57875