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 index a5027d6a8da32a..bc93d60174553e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.CoreCLR.cs @@ -1,24 +1,35 @@ // 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.CodeAnalysis; +using System.Reflection.Emit; +using static System.Reflection.InvokerEmitUtil; namespace System.Reflection { public partial class ConstructorInvoker { - private readonly Signature? _signature; - internal unsafe ConstructorInvoker(RuntimeConstructorInfo constructor) : this(constructor, constructor.Signature.Arguments) { - _signature = constructor.Signature; _invokeFunc_RefArgs = InterpretedInvoke; } - [RequiresUnsafe] private unsafe object? InterpretedInvoke(object? obj, IntPtr* args) { - return RuntimeMethodHandle.InvokeMethod(obj, (void**)args, _signature!, isConstructor: obj is null); + InvokeFunc_RefArgs emitDelegate; + using (AssemblyBuilder.ForceAllowDynamicCode()) + { + emitDelegate = CreateInvokeDelegate_RefArgs(_method); + } + + // Don't cache for collectible assemblies: the DynamicMethod holds token + // references to types that would prevent the assembly from being unloaded. + Type? declaringType = _method.DeclaringType; + if (declaringType is null || !declaringType.Assembly.IsCollectible) + { + _invokeFunc_RefArgs = emitDelegate; + } + + return emitDelegate(obj, args); } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.CoreCLR.cs index 48babc3ebc4a8e..70a740c7bce15d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.CoreCLR.cs @@ -1,41 +1,49 @@ // 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.CodeAnalysis; using System.Reflection.Emit; +using static System.Reflection.InvokerEmitUtil; +using static System.Reflection.MethodBase; namespace System.Reflection { internal partial class MethodBaseInvoker { - private readonly Signature? _signature; - internal unsafe MethodBaseInvoker(RuntimeMethodInfo method) : this(method, method.Signature.Arguments) { - _signature = method.Signature; _invocationFlags = method.ComputeAndUpdateInvocationFlags(); _invokeFunc_RefArgs = InterpretedInvoke_Method; } internal unsafe MethodBaseInvoker(RuntimeConstructorInfo constructor) : this(constructor, constructor.Signature.Arguments) { - _signature = constructor.Signature; _invocationFlags = constructor.ComputeAndUpdateInvocationFlags(); - _invokeFunc_RefArgs = InterpretedInvoke_Constructor; + _invokeFunc_RefArgs = InterpretedInvoke_Method; } internal unsafe MethodBaseInvoker(DynamicMethod method, Signature signature) : this(method, signature.Arguments) { - _signature = signature; _invokeFunc_RefArgs = InterpretedInvoke_Method; } - [RequiresUnsafe] - private unsafe object? InterpretedInvoke_Constructor(object? obj, IntPtr* args) => - RuntimeMethodHandle.InvokeMethod(obj, (void**)args, _signature!, isConstructor: obj is null); + private unsafe object? InterpretedInvoke_Method(object? obj, IntPtr* args) + { + InvokeFunc_RefArgs emitDelegate; + using (AssemblyBuilder.ForceAllowDynamicCode()) + { + emitDelegate = CreateInvokeDelegate_RefArgs(_method); + } + + // Don't cache for collectible assemblies: the DynamicMethod holds token + // references to types that would prevent the assembly from being unloaded. + Type? declaringType = _method.DeclaringType; + if (declaringType is null || !declaringType.Assembly.IsCollectible) + { + _invokeFunc_RefArgs = emitDelegate; + } + + return emitDelegate(obj, args); + } - [RequiresUnsafe] - private unsafe object? InterpretedInvoke_Method(object? obj, IntPtr* args) => - RuntimeMethodHandle.InvokeMethod(obj, (void**)args, _signature!, isConstructor: false); } } 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 index ed74df63ac261b..96cc47ff09571c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodInvoker.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodInvoker.CoreCLR.cs @@ -1,42 +1,47 @@ // 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.CodeAnalysis; using System.Reflection.Emit; +using static System.Reflection.InvokerEmitUtil; namespace System.Reflection { public partial class MethodInvoker { - private readonly Signature? _signature; - private unsafe MethodInvoker(RuntimeMethodInfo method) : this(method, method.Signature.Arguments) { - _signature = method.Signature; - _invokeFunc_RefArgs = InterpretedInvoke_Method; _invocationFlags = method.ComputeAndUpdateInvocationFlags(); + _invokeFunc_RefArgs = InterpretedInvoke_Method; } private unsafe MethodInvoker(DynamicMethod method) : this(method, method.Signature.Arguments) { - _signature = method.Signature; _invokeFunc_RefArgs = InterpretedInvoke_Method; // No _invocationFlags for DynamicMethod. } private unsafe MethodInvoker(RuntimeConstructorInfo constructor) : this(constructor, constructor.Signature.Arguments) { - _signature = constructor.Signature; - _invokeFunc_RefArgs = InterpretedInvoke_Constructor; _invocationFlags = constructor.ComputeAndUpdateInvocationFlags(); + _invokeFunc_RefArgs = InterpretedInvoke_Method; } - [RequiresUnsafe] - private unsafe object? InterpretedInvoke_Method(object? obj, IntPtr* args) => - RuntimeMethodHandle.InvokeMethod(obj, (void**)args, _signature!, isConstructor: false); + private unsafe object? InterpretedInvoke_Method(object? obj, IntPtr* args) + { + InvokeFunc_RefArgs emitDelegate; + using (AssemblyBuilder.ForceAllowDynamicCode()) + { + emitDelegate = CreateInvokeDelegate_RefArgs(_method); + } + + Type? declaringType = _method.DeclaringType; + if (declaringType is null || !declaringType.Assembly.IsCollectible) + { + _invokeFunc_RefArgs = emitDelegate; + } + + return emitDelegate(obj, args); + } - [RequiresUnsafe] - private unsafe object? InterpretedInvoke_Constructor(object? obj, IntPtr* args) => - RuntimeMethodHandle.InvokeMethod(obj, (void**)args, _signature!, isConstructor: obj 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 0c5de0c21006a1..fa2450ec464dc3 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 @@ -41,7 +41,7 @@ internal InvocationFlags InvocationFlags } } - private MethodBaseInvoker Invoker + internal MethodBaseInvoker Invoker { [MethodImpl(MethodImplOptions.AggressiveInlining)] get diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs index b0a37eb1997d05..b7080c6546d69a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs @@ -302,16 +302,16 @@ private RuntimeCustomAttributeData(RuntimeModule scope, MetadataToken caCtorToke #region Pseudo Custom Attribute Constructor internal RuntimeCustomAttributeData(Attribute attribute) { - if (attribute is DllImportAttribute dllImportAttribute) - Init(dllImportAttribute); - else if (attribute is FieldOffsetAttribute fieldOffsetAttribute) - Init(fieldOffsetAttribute); - else if (attribute is MarshalAsAttribute marshalAsAttribute) - Init(marshalAsAttribute); - else if (attribute is TypeForwardedToAttribute typeForwardedToAttribute) - Init(typeForwardedToAttribute); - else - Init(attribute); + if (attribute is DllImportAttribute dllImportAttribute) + Init(dllImportAttribute); + else if (attribute is FieldOffsetAttribute fieldOffsetAttribute) + Init(fieldOffsetAttribute); + else if (attribute is MarshalAsAttribute marshalAsAttribute) + Init(marshalAsAttribute); + else if (attribute is TypeForwardedToAttribute typeForwardedToAttribute) + Init(typeForwardedToAttribute); + else + Init(attribute); } private void Init(DllImportAttribute dllImport) { @@ -849,24 +849,24 @@ private static CustomAttributeEncodedArgument ParseCustomAttributeValue( arg.StringValue = parser.GetString(); break; case CustomAttributeEncoding.Array: - { - arg.ArrayValue = null; - int len = parser.GetI4(); - if (len != -1) // indicates array is null - ECMA-335 II.23.3. { - attributeType = new CustomAttributeType( - attributeType.EncodedArrayType, - CustomAttributeEncoding.Undefined, // Array type - attributeType.EncodedEnumType, - attributeType.EnumType); - arg.ArrayValue = new CustomAttributeEncodedArgument[len]; - for (int i = 0; i < len; ++i) + arg.ArrayValue = null; + int len = parser.GetI4(); + if (len != -1) // indicates array is null - ECMA-335 II.23.3. { - arg.ArrayValue[i] = ParseCustomAttributeValue(ref parser, attributeType, module); + attributeType = new CustomAttributeType( + attributeType.EncodedArrayType, + CustomAttributeEncoding.Undefined, // Array type + attributeType.EncodedEnumType, + attributeType.EnumType); + arg.ArrayValue = new CustomAttributeEncodedArgument[len]; + for (int i = 0; i < len; ++i) + { + arg.ArrayValue[i] = ParseCustomAttributeValue(ref parser, attributeType, module); + } } + break; } - break; - } default: throw new BadImageFormatException(); } @@ -1859,6 +1859,88 @@ private static bool ParseAttributeUsageAttribute( return result != 0; } + [UnmanagedCallersOnly] + [RequiresUnsafe] + private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* pContract, object* pResult, Exception* pException) + { + try + { + object?[]? parameters = *pContract->CtorArgs; + object? ctorObject = *pContract->CtorMethod; + + int argCount = pContract->ArgCount; + if (argCount < 0 || parameters is null || parameters.Length != argCount) + { + throw new TargetParameterCountException(SR.Arg_ParmCnt); + } + + RuntimeConstructorInfo? ctor = ctorObject as RuntimeConstructorInfo; + if (ctor is null) + { + if (ctorObject is not System.IRuntimeMethodInfo methodInfo || + RuntimeType.GetMethodBase(methodInfo) is not RuntimeConstructorInfo resolvedCtor) + { + throw new InvalidOperationException("Invalid custom attribute constructor."); + } + + ctor = resolvedCtor; + } + + ReadOnlySpan ctorParameters = default; + bool needCtorParameters = false; + + for (int i = 0; i < argCount; i++) + { + object? arg = parameters[i]; + + if (ReferenceEquals(arg, DBNull.Value) || ReferenceEquals(arg, Missing.Value)) + { + parameters[i] = null; + continue; + } + + if (arg is not null && arg.GetType() == typeof(object)) + { + if (!needCtorParameters) + { + ctorParameters = ctor.GetParametersAsSpan(); + needCtorParameters = true; + } + + if ((uint)i < (uint)ctorParameters.Length && ctorParameters[i].ParameterType == typeof(object)) + { + // Native custom-attribute parsing can represent object-typed null as a plain object instance. + parameters[i] = null; + } + } + } + + MethodBaseInvoker invoker = ctor.Invoker; + object? result = argCount switch + { + 0 => invoker.InvokeWithNoArgs(obj: null, BindingFlags.DoNotWrapExceptions), + 1 => invoker.InvokeWithOneArg(obj: null, BindingFlags.DoNotWrapExceptions, binder: null, parameters, culture: null), + 2 or 3 or 4 => invoker.InvokeWithFewArgs(obj: null, BindingFlags.DoNotWrapExceptions, binder: null, parameters, culture: null), + _ => invoker.InvokeWithManyArgs(obj: null, BindingFlags.DoNotWrapExceptions, binder: null, parameters, culture: null), + }; + + *pResult = result!; + } + catch (Exception ex) + { + *pException = ex; + } + } + + [StructLayout(LayoutKind.Sequential)] + private unsafe struct NativeCtorInvokeContract + { + public object[]* CtorArgs; + public int ArgCount; + public object* CtorMethod; + public object* CtorDeclaringType; + } + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CustomAttribute_CreateCustomAttributeInstance")] private static partial void CreateCustomAttributeInstance( QCallModule pModule, diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 7e9d558c00dd57..4cda65080846d0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -1192,27 +1192,6 @@ internal static MdUtf8String GetUtf8Name(RuntimeMethodHandleInternal method) return new MdUtf8String(name); } - [DebuggerStepThrough] - [DebuggerHidden] - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeMethodHandle_InvokeMethod")] - [RequiresUnsafe] - private static partial void InvokeMethod(ObjectHandleOnStack target, void** arguments, ObjectHandleOnStack sig, Interop.BOOL isConstructor, ObjectHandleOnStack result); - - [DebuggerStepThrough] - [DebuggerHidden] - [RequiresUnsafe] - internal static object? InvokeMethod(object? target, void** arguments, Signature sig, bool isConstructor) - { - object? result = null; - InvokeMethod( - ObjectHandleOnStack.Create(ref target), - arguments, - ObjectHandleOnStack.Create(ref sig), - isConstructor ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, - ObjectHandleOnStack.Create(ref result)); - return result; - } - /// /// For a true boxed Nullable{T}, re-box to a boxed {T} or null, otherwise just return the input. /// diff --git a/src/coreclr/vm/callhelpers.cpp b/src/coreclr/vm/callhelpers.cpp index 2568a1483d5714..3934d6dbc0f20e 100644 --- a/src/coreclr/vm/callhelpers.cpp +++ b/src/coreclr/vm/callhelpers.cpp @@ -139,68 +139,6 @@ void CopyReturnedFpStructFromRegisters(void* dest, UINT64 returnRegs[2], FpStruc } #endif // TARGET_RISCV64 || TARGET_LOONGARCH64 -// Helper for VM->managed calls with simple signatures. -void* DispatchCallSimple( - SIZE_T *pSrc, - DWORD numStackSlotsToCopy, - PCODE pTargetAddress, - BOOL fCriticalCall) -{ - CONTRACTL - { - GC_TRIGGERS; - THROWS; - MODE_COOPERATIVE; - } - CONTRACTL_END; - -#ifdef DEBUGGING_SUPPORTED - if (CORDebuggerTraceCall()) - g_pDebugInterface->TraceCall((const BYTE *)pTargetAddress); -#endif // DEBUGGING_SUPPORTED - - CallDescrData callDescrData; - -#ifdef CALLDESCR_ARGREGS - callDescrData.pSrc = pSrc + NUM_ARGUMENT_REGISTERS; - callDescrData.numStackSlots = numStackSlotsToCopy; - callDescrData.pArgumentRegisters = (ArgumentRegisters *)pSrc; -#else - callDescrData.pSrc = pSrc; - callDescrData.numStackSlots = numStackSlotsToCopy; -#endif - -#ifdef CALLDESCR_RETBUFFARGREG - UINT64 retBuffArgPlaceholder = 0; - callDescrData.pRetBuffArg = &retBuffArgPlaceholder; -#endif - -#ifdef CALLDESCR_FPARGREGS - callDescrData.pFloatArgumentRegisters = NULL; -#endif -#ifdef CALLDESCR_REGTYPEMAP - callDescrData.dwRegTypeMap = 0; -#endif - callDescrData.fpReturnSize = 0; - callDescrData.pTarget = pTargetAddress; - -#ifdef TARGET_WASM - static_assert(2*sizeof(ARGHOLDER_TYPE) == INTERP_STACK_SLOT_SIZE); - callDescrData.nArgsSize = numStackSlotsToCopy * sizeof(ARGHOLDER_TYPE)*2; - callDescrData.hasRetBuff = false; - LPVOID pOrigSrc = callDescrData.pSrc; - callDescrData.pSrc = (LPVOID)_alloca(callDescrData.nArgsSize); - for (int i = 0; i < numStackSlotsToCopy; i++) - { - ((ARGHOLDER_TYPE*)callDescrData.pSrc)[i*2] = ((ARGHOLDER_TYPE*)pOrigSrc)[i]; - } -#endif // TARGET_WASM - - CallDescrWorkerWithHandler(&callDescrData, fCriticalCall); - - return *(void **)(&callDescrData.returnValue); -} - #ifdef CALLDESCR_REGTYPEMAP //******************************************************************************* void FillInRegTypeMap(int argOffset, CorElementType typ, BYTE * pMap) @@ -536,34 +474,3 @@ void MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT * #endif // !defined(HOST_64BIT) && BIGENDIAN } } - -void CallDefaultConstructor(OBJECTREF ref) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - MethodTable *pMT = ref->GetMethodTable(); - - _ASSERTE(pMT != NULL); - - if (!pMT->HasDefaultConstructor()) - { - SString ctorMethodName(SString::Utf8, COR_CTOR_METHOD_NAME); - COMPlusThrowNonLocalized(kMissingMethodException, ctorMethodName.GetUnicode()); - } - - GCPROTECT_BEGIN (ref); - - MethodDesc *pMD = pMT->GetDefaultConstructor(); - - UnmanagedCallersOnlyCaller defaultCtorInvoker{METHOD__RUNTIME_HELPERS__CALL_DEFAULT_CONSTRUCTOR}; - - defaultCtorInvoker.InvokeThrowing(&ref, pMD->GetSingleCallableAddrOfCode()); - - GCPROTECT_END (); -} diff --git a/src/coreclr/vm/callhelpers.h b/src/coreclr/vm/callhelpers.h index d853823023a073..a6cee69b008113 100644 --- a/src/coreclr/vm/callhelpers.h +++ b/src/coreclr/vm/callhelpers.h @@ -69,13 +69,6 @@ void CallDescrWorkerWithHandler( CallDescrData * pCallDescrData, BOOL fCriticalCall = FALSE); -// Helper for VM->managed calls with simple signatures. -void* DispatchCallSimple( - SIZE_T *pSrc, - DWORD numStackSlotsToCopy, - PCODE pTargetAddress, - BOOL fCriticalCall); - #if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) // Copy structs returned according to floating-point calling convention from 'returnRegs' containing struct fields // (each returned in one register) as they are filled by CallDescrWorkerInternal, to the final destination in memory @@ -495,14 +488,8 @@ enum EEToManagedCallFlags #define END_CALL_TO_MANAGED() \ } -/***********************************************************************/ -/* Macros that provide abstraction to the usage of DispatchCallSimple */ -/***********************************************************************/ - #define ARGHOLDER_TYPE LPVOID -void CallDefaultConstructor(OBJECTREF ref); - // // Helper types for calling managed methods marked with [UnmanagedCallersOnly] // from native code. diff --git a/src/coreclr/vm/clrex.cpp b/src/coreclr/vm/clrex.cpp index 565f8ec633b68e..73ea3b79cff66e 100644 --- a/src/coreclr/vm/clrex.cpp +++ b/src/coreclr/vm/clrex.cpp @@ -993,7 +993,11 @@ OBJECTREF EEException::CreateThrowable() { ThreadPreventAsyncHolder preventAbort(m_kind == kThreadAbortException || m_kind == kThreadInterruptedException); - CallDefaultConstructor(throwable); + + _ASSERTE(pMT->HasDefaultConstructor()); + MethodDesc *pMD = pMT->GetDefaultConstructor(); + UnmanagedCallersOnlyCaller defaultCtorInvoker{METHOD__RUNTIME_HELPERS__CALL_DEFAULT_CONSTRUCTOR}; + defaultCtorInvoker.InvokeThrowing(&throwable, pMD->GetSingleCallableAddrOfCode()); } HRESULT hr = GetHR(); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index f75fdcce0c832d..2e62223723a369 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -515,6 +515,9 @@ DEFINE_CLASS(MEMBER, Reflection, MemberInfo) DEFINE_CLASS(METHODBASEINVOKER, Reflection, MethodBaseInvoker) +DEFINE_CLASS(CUSTOMATTRIBUTE, Reflection, CustomAttribute) +DEFINE_METHOD(CUSTOMATTRIBUTE, INVOKE_CUSTOM_ATTRIBUTE_CTOR, InvokeCustomAttributeCtor, NoSig) + DEFINE_CLASS(INSTANCE_CALLI_HELPER, Reflection, InstanceCalliHelper) DEFINE_CLASS_U(Reflection, RuntimeMethodInfo, NoClass) diff --git a/src/coreclr/vm/customattribute.cpp b/src/coreclr/vm/customattribute.cpp index caf204b84cee19..e15bcfa2194042 100644 --- a/src/coreclr/vm/customattribute.cpp +++ b/src/coreclr/vm/customattribute.cpp @@ -15,6 +15,7 @@ #include "reflectioninvocation.h" #include "runtimehandles.h" #include "typestring.h" +#include "callhelpers.h" static TypeHandle GetTypeForEnum(LPCUTF8 szEnumName, COUNT_T cbEnumName, Assembly* pAssembly) { @@ -915,23 +916,29 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( MethodDesc* pCtorMD = ((REFLECTMETHODREF)pMethod.Get())->GetMethod(); TypeHandle th = ((REFLECTCLASSBASEREF)pCaType.Get())->GetType(); - MethodDescCallSite ctorCallSite(pCtorMD, th); - MetaSig* pSig = ctorCallSite.GetMetaSig(); + MetaSig ctorSig(pCtorMD, th); + MetaSig* pSig = &ctorSig; BYTE* pBlob = *ppBlob; - // get the number of arguments and allocate an array for the args - ARG_SLOT *args = NULL; - UINT cArgs = pSig->NumFixedArgs() + 1; // make room for the this pointer - UINT i = 1; // used to flag that we actually get the right number of arg from the blob + UINT cArgs = pSig->NumFixedArgs(); + UINT i = 0; - args = (ARG_SLOT*)_alloca(cArgs * sizeof(ARG_SLOT)); - memset((void*)args, 0, cArgs * sizeof(ARG_SLOT)); - - OBJECTREF *argToProtect = (OBJECTREF*)_alloca(cArgs * sizeof(OBJECTREF)); - memset((void*)argToProtect, 0, cArgs * sizeof(OBJECTREF)); - - // load the this pointer - argToProtect[0] = th.GetMethodTable()->Allocate(); // this is the value to return after the ctor invocation + struct + { + PTRARRAYREF ctorArgs; + OBJECTREF ctorMethod; + OBJECTREF ctorDeclaringType; + OBJECTREF ctorResult; + } gc; + gc.ctorArgs = NULL; + gc.ctorMethod = NULL; + gc.ctorDeclaringType = NULL; + gc.ctorResult = NULL; + GCPROTECT_BEGIN(gc); + + gc.ctorArgs = (PTRARRAYREF)AllocateObjectArray(cArgs, g_pObjectClass); + gc.ctorMethod = pMethod.Get(); + gc.ctorDeclaringType = pCaType.Get(); if (pBlob) { @@ -947,44 +954,66 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( pBlob += 2; } - if (cArgs > 1) + if (cArgs > 0) { - GCPROTECT_ARRAY_BEGIN(*argToProtect, cArgs); + // loop through the args + for (i = 0; i < cArgs; i++) { - // loop through the args - for (i = 1; i < cArgs; i++) { - CorElementType type = pSig->NextArg(); - if (type == ELEMENT_TYPE_END) - break; - BOOL bObjectCreated = FALSE; - TypeHandle th = pSig->GetLastTypeHandleThrowing(); - if (th.IsArray()) - // get the array element - th = th.GetArrayElementTypeHandle(); - ARG_SLOT data = GetDataFromBlob(pCtorMD->GetAssembly(), (CorSerializationType)type, th, &pBlob, pEndBlob, pModule, &bObjectCreated); - if (bObjectCreated) - argToProtect[i] = ArgSlotToObj(data); + CorElementType type = pSig->NextArg(); + if (type == ELEMENT_TYPE_END) + break; + + BOOL bObjectCreated = FALSE; + TypeHandle paramType = pSig->GetLastTypeHandleThrowing(); + TypeHandle argTypeForParse = paramType; + if (argTypeForParse.IsArray()) + argTypeForParse = argTypeForParse.GetArrayElementTypeHandle(); + + ARG_SLOT data = GetDataFromBlob( + pCtorMD->GetAssembly(), + (CorSerializationType)type, + argTypeForParse, + &pBlob, + pEndBlob, + pModule, + &bObjectCreated); + + OBJECTREF argObj = NULL; + if (bObjectCreated) + { + argObj = ArgSlotToObj(data); + } + else + { + if (type == ELEMENT_TYPE_CLASS || + type == ELEMENT_TYPE_STRING || + type == ELEMENT_TYPE_OBJECT || + type == ELEMENT_TYPE_SZARRAY || + type == ELEMENT_TYPE_ARRAY) + { + // Reference-like custom-attribute ctor arguments are already represented as OBJECTREF + // when non-null. If no object was created, this must be the null case. + _ASSERTE(data == (ARG_SLOT)0); + argObj = NULL; + } else - args[i] = data; + { + MethodTable* pMTValue = (type == ELEMENT_TYPE_VALUETYPE) + ? argTypeForParse.GetMethodTable() + : CoreLibBinder::GetElementType(type); + + _ASSERTE(pMTValue != NULL); + argObj = pMTValue->Box((void*)ArgSlotEndiannessFixup(&data, pMTValue->GetNumInstanceFieldBytes())); + } } + + gc.ctorArgs->SetAt(i, argObj); } - GCPROTECT_END(); - // We have borrowed the signature from MethodDescCallSite. We have to put it back into the initial position - // because of that's where MethodDescCallSite expects to find it below. + // Reset signature enumeration before we leave argument processing. pSig->Reset(); - - for (i = 1; i < cArgs; i++) - { - if (argToProtect[i] != NULL) - { - _ASSERTE(args[i] == (ARG_SLOT)NULL); - args[i] = ObjToArgSlot(argToProtect[i]); - } - } } } - args[0] = ObjToArgSlot(argToProtect[0]); if (i != cArgs) COMPlusThrow(kCustomAttributeFormatException); @@ -1009,12 +1038,25 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( if (*pcNamedArgs == 0 && pBlob != pEndBlob) COMPlusThrow(kCustomAttributeFormatException); - // make the invocation to the ctor - result.Set(ArgSlotToObj(args[0])); - if (pCtorMD->GetMethodTable()->IsValueType()) - args[0] = PtrToArgSlot(OBJECTREFToObject(result.Get())->UnBox()); + struct NativeCtorInvokeContract + { + PTRARRAYREF* ctorArgs; + INT32 argCount; + OBJECTREF* ctorMethod; + OBJECTREF* ctorDeclaringType; + } contract; - ctorCallSite.CallWithValueTypes(args); + contract.ctorArgs = &gc.ctorArgs; + contract.argCount = (INT32)cArgs; + contract.ctorMethod = &gc.ctorMethod; + contract.ctorDeclaringType = &gc.ctorDeclaringType; + + UnmanagedCallersOnlyCaller invokeCustomAttributeCtor(METHOD__CUSTOMATTRIBUTE__INVOKE_CUSTOM_ATTRIBUTE_CTOR); + invokeCustomAttributeCtor.InvokeThrowing(&contract, &gc.ctorResult); + + result.Set(gc.ctorResult); + + GCPROTECT_END(); END_QCALL; } diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index b7633063e87fab..584cba13d1fc8e 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -167,7 +167,6 @@ static const Entry s_QCall[] = DllImportEntry(RuntimeTypeHandle_AllocateTypeAssociatedMemoryAligned) DllImportEntry(RuntimeTypeHandle_RegisterCollectibleTypeDependency) DllImportEntry(MethodBase_GetCurrentMethod) - DllImportEntry(RuntimeMethodHandle_InvokeMethod) DllImportEntry(RuntimeMethodHandle_ConstructInstantiation) DllImportEntry(RuntimeMethodHandle_GetFunctionPointer) DllImportEntry(RuntimeMethodHandle_GetMethodInstantiation) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 37343b4fcb9cc2..da61b1d8fc9d8c 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -109,7 +109,11 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_CreateInstanceForAnotherGenericParam OBJECTREF newObj = instantiatedType.GetMethodTable()->Allocate(); GCPROTECT_BEGIN(newObj); - CallDefaultConstructor(newObj); + + MethodDesc *pMD = pVMT->GetDefaultConstructor(); + UnmanagedCallersOnlyCaller defaultCtorInvoker{METHOD__RUNTIME_HELPERS__CALL_DEFAULT_CONSTRUCTOR}; + defaultCtorInvoker.InvokeThrowing(&newObj, pMD->GetSingleCallableAddrOfCode()); + GCPROTECT_END(); pInstantiatedObject.Set(newObj); @@ -148,564 +152,6 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_InternalAllocNoChecks(MethodTable* p END_QCALL; } -static OBJECTREF InvokeArrayConstructor(TypeHandle th, PVOID* args, int argCnt) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - // Validate the argCnt an the Rank. Also allow nested SZARRAY's. - _ASSERTE(argCnt == (int) th.GetRank() || argCnt == (int) th.GetRank() * 2 || - th.GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY); - - // Validate all of the parameters. These all typed as integers - int allocSize = 0; - if (!ClrSafeInt::multiply(sizeof(INT32), argCnt, allocSize)) - COMPlusThrow(kArgumentException, IDS_EE_SIGTOOCOMPLEX); - - INT32* indexes = (INT32*) _alloca((size_t)allocSize); - ZeroMemory(indexes, allocSize); - MethodTable* pMT = CoreLibBinder::GetElementType(ELEMENT_TYPE_I4); - - for (DWORD i=0; i<(DWORD)argCnt; i++) - { - _ASSERTE(args[i] != NULL); - - INT32 size = *(INT32*)args[i]; - ARG_SLOT value = size; - memcpyNoGCRefs(indexes + i, ArgSlotEndiannessFixup(&value, sizeof(INT32)), sizeof(INT32)); - } - - return AllocateArrayEx(th, indexes, argCnt); -} - -static BOOL IsActivationNeededForMethodInvoke(MethodDesc * pMD) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - // The activation for non-generic instance methods is covered by non-null "this pointer" - if (!pMD->IsStatic() && !pMD->HasMethodInstantiation() && !pMD->IsInterface()) - return FALSE; - - // We need to activate the instance at least once - pMD->EnsureActive(); - return FALSE; -} - -class ArgIteratorBaseForMethodInvoke -{ -protected: - SIGNATURENATIVEREF * m_ppNativeSig; - bool m_fHasThis; - - FORCEINLINE CorElementType GetReturnType(TypeHandle * pthValueType) - { - WRAPPER_NO_CONTRACT; - return (*pthValueType = (*m_ppNativeSig)->GetReturnTypeHandle()).GetInternalCorElementType(); - } - - FORCEINLINE CorElementType GetNextArgumentType(DWORD iArg, TypeHandle * pthValueType) - { - WRAPPER_NO_CONTRACT; - return (*pthValueType = (*m_ppNativeSig)->GetArgumentAt(iArg)).GetInternalCorElementType(); - } - - FORCEINLINE void Reset() - { - LIMITED_METHOD_CONTRACT; - } - - FORCEINLINE BOOL IsRegPassedStruct(TypeHandle th) - { - return th.AsMethodTable()->IsRegPassedStruct(); - } - -#if defined(UNIX_AMD64_ABI) - FORCEINLINE SystemVEightByteRegistersInfo GetEightByteRegistersInfo(TypeHandle th) - { - return th.AsMethodTable()->GetClass()->GetEightByteRegistersInfo(); - } -#endif // defined(UNIX_AMD64_ABI) - -public: - FORCEINLINE BOOL IsRetBuffPassedAsFirstArg() - { - return ::IsRetBuffPassedAsFirstArg(); - } - - BOOL HasThis() - { - LIMITED_METHOD_CONTRACT; - return m_fHasThis; - } - - BOOL HasParamType() - { - LIMITED_METHOD_CONTRACT; - // param type methods are not supported for reflection invoke, so HasParamType is always false for them - return FALSE; - } - - BOOL HasAsyncContinuation() - { - LIMITED_METHOD_CONTRACT; - // async calls are also not supported for reflection invoke - return FALSE; - } - - BOOL IsVarArg() - { - LIMITED_METHOD_CONTRACT; - // vararg methods are not supported for reflection invoke, so IsVarArg is always false for them - return FALSE; - } - - DWORD NumFixedArgs() - { - LIMITED_METHOD_CONTRACT; - return (*m_ppNativeSig)->NumFixedArgs(); - } -}; - -class ArgIteratorForMethodInvoke : public ArgIteratorTemplate -{ -public: - ArgIteratorForMethodInvoke(SIGNATURENATIVEREF * ppNativeSig, BOOL fCtorOfVariableSizedObject) - { - m_ppNativeSig = ppNativeSig; - - m_fHasThis = (*m_ppNativeSig)->HasThis() && !fCtorOfVariableSizedObject; - - DWORD dwFlags = (*m_ppNativeSig)->GetArgIteratorFlags(); - - // Use the cached values if they are available - if (dwFlags & SIZE_OF_ARG_STACK_COMPUTED) - { - m_dwFlags = dwFlags; - m_nSizeOfArgStack = (*m_ppNativeSig)->GetSizeOfArgStack(); - return; - } - - // - // Compute flags and stack argument size, and cache them for next invocation - // - - ForceSigWalk(); - - if (IsActivationNeededForMethodInvoke((*m_ppNativeSig)->GetMethod())) - { - m_dwFlags |= METHOD_INVOKE_NEEDS_ACTIVATION; - } - - (*m_ppNativeSig)->SetSizeOfArgStack(m_nSizeOfArgStack); - _ASSERTE((*m_ppNativeSig)->GetSizeOfArgStack() == m_nSizeOfArgStack); - - // This has to be last - (*m_ppNativeSig)->SetArgIteratorFlags(m_dwFlags); - _ASSERTE((*m_ppNativeSig)->GetArgIteratorFlags() == m_dwFlags); - } - - BOOL IsActivationNeeded() - { - LIMITED_METHOD_CONTRACT; - return (m_dwFlags & METHOD_INVOKE_NEEDS_ACTIVATION) != 0; - } -}; - -extern "C" void QCALLTYPE RuntimeMethodHandle_InvokeMethod( - QCall::ObjectHandleOnStack target, - PVOID* args, // An array of byrefs - QCall::ObjectHandleOnStack pSig, - BOOL fConstructor, - QCall::ObjectHandleOnStack result) -{ - QCALL_CONTRACT; - - BEGIN_QCALL; - - GCX_COOP(); - - struct - { - OBJECTREF target; - SIGNATURENATIVEREF pSig; - OBJECTREF retVal; - } gc; - gc.target = NULL; - gc.pSig = NULL; - gc.retVal = NULL; - GCPROTECT_BEGIN(gc); - gc.target = target.Get(); - gc.pSig = (SIGNATURENATIVEREF)pSig.Get(); - - MethodDesc* pMeth = gc.pSig->GetMethod(); - TypeHandle ownerType = gc.pSig->GetDeclaringType(); - - if (ownerType.IsSharedByGenericInstantiations()) - { - COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); - } - -#ifdef _DEBUG - if (g_pConfig->ShouldInvokeHalt(pMeth)) - { - _ASSERTE(!"InvokeHalt"); - } -#endif - - BOOL fCtorOfVariableSizedObject = FALSE; - - if (fConstructor) - { - // If we are invoking a constructor on an array then we must - // handle this specially. - if (ownerType.IsArray()) { - gc.retVal = InvokeArrayConstructor(ownerType, - args, - gc.pSig->NumFixedArgs()); - goto Done; - } - - // Variable sized objects, like String instances, allocate themselves - // so they are a special case. - MethodTable * pMT = ownerType.AsMethodTable(); - fCtorOfVariableSizedObject = pMT->HasComponentSize(); - if (!fCtorOfVariableSizedObject) - gc.retVal = pMT->Allocate(); - } - - { - ArgIteratorForMethodInvoke argit(&gc.pSig, fCtorOfVariableSizedObject); - - if (argit.IsActivationNeeded()) - pMeth->EnsureActive(); - CONSISTENCY_CHECK(pMeth->CheckActivated()); - - UINT nStackBytes = argit.SizeOfFrameArgumentArray(); - - // Note that SizeOfFrameArgumentArray does overflow checks with sufficient margin to prevent overflows here - SIZE_T nAllocaSize = TransitionBlock::GetNegSpaceSize() + sizeof(TransitionBlock) + nStackBytes; - - Thread * pThread = GET_THREAD(); - - LPBYTE pAlloc = (LPBYTE)_alloca(nAllocaSize); - - LPBYTE pTransitionBlock = pAlloc + TransitionBlock::GetNegSpaceSize(); - - CallDescrData callDescrData; - - callDescrData.pSrc = pTransitionBlock + sizeof(TransitionBlock); - callDescrData.numStackSlots = ALIGN_UP(nStackBytes, TARGET_REGISTER_SIZE) / TARGET_REGISTER_SIZE; -#ifdef CALLDESCR_ARGREGS - callDescrData.pArgumentRegisters = (ArgumentRegisters*)(pTransitionBlock + TransitionBlock::GetOffsetOfArgumentRegisters()); -#endif -#ifdef CALLDESCR_RETBUFFARGREG - callDescrData.pRetBuffArg = (UINT64*)(pTransitionBlock + TransitionBlock::GetOffsetOfRetBuffArgReg()); -#endif -#ifdef CALLDESCR_FPARGREGS - callDescrData.pFloatArgumentRegisters = NULL; -#endif -#ifdef CALLDESCR_REGTYPEMAP - callDescrData.dwRegTypeMap = 0; -#endif - callDescrData.fpReturnSize = argit.GetFPReturnSize(); -#ifdef TARGET_WASM - // WASM-TODO: this is now called from the interpreter, so the arguments layout is OK. reconsider with codegen - callDescrData.nArgsSize = nStackBytes; - callDescrData.hasThis = argit.HasThis(); - callDescrData.hasRetBuff = argit.HasRetBuffArg(); -#endif // TARGET_WASM - - // This is duplicated logic from MethodDesc::GetCallTarget - PCODE pTarget; - if (pMeth->IsVtableMethod()) - { - pTarget = pMeth->GetSingleCallableAddrOfVirtualizedCode(&gc.target, ownerType); - } - else - { - pTarget = pMeth->GetSingleCallableAddrOfCode(); - } - callDescrData.pTarget = pTarget; - - // Build the arguments on the stack - - GCStress::MaybeTrigger(); - - ProtectValueClassFrame *pProtectValueClassFrame = NULL; - ValueClassInfo *pValueClasses = NULL; - - // if we have the magic Value Class return, we need to allocate that class - // and place a pointer to it on the stack. - - BOOL hasRefReturnAndNeedsBoxing = FALSE; // Indicates that the method has a BYREF return type and the target type needs to be copied into a preallocated boxed object. - - TypeHandle retTH = gc.pSig->GetReturnTypeHandle(); - - TypeHandle refReturnTargetTH; // Valid only if retType == ELEMENT_TYPE_BYREF. Caches the TypeHandle of the byref target. - BOOL fHasRetBuffArg = argit.HasRetBuffArg(); - CorElementType retType = retTH.GetSignatureCorElementType(); - BOOL hasValueTypeReturn = retTH.IsValueType() && retType != ELEMENT_TYPE_VOID; - _ASSERTE(hasValueTypeReturn || !fHasRetBuffArg); // only valuetypes are returned via a return buffer. - if (hasValueTypeReturn) { - gc.retVal = retTH.GetMethodTable()->Allocate(); - } - else if (retType == ELEMENT_TYPE_BYREF) - { - refReturnTargetTH = retTH.AsTypeDesc()->GetTypeParam(); - - // If the target of the byref is a value type, we need to preallocate a boxed object to hold the managed return value. - if (refReturnTargetTH.IsValueType()) - { - _ASSERTE(refReturnTargetTH.GetSignatureCorElementType() != ELEMENT_TYPE_VOID); // Managed Reflection layer has a bouncer for "ref void" returns. - hasRefReturnAndNeedsBoxing = TRUE; - gc.retVal = refReturnTargetTH.GetMethodTable()->Allocate(); - } - } - - // Copy "this" pointer - if (!pMeth->IsStatic() && !fCtorOfVariableSizedObject) { - PVOID pThisPtr; - - if (fConstructor) - { - // Copy "this" pointer: only unbox if type is value type and method is not unboxing stub - if (ownerType.IsValueType() && !pMeth->IsUnboxingStub()) { - // Note that we create a true boxed nullabe and then convert it to a T below - pThisPtr = gc.retVal->GetData(); - } - else - pThisPtr = OBJECTREFToObject(gc.retVal); - } - else if (!pMeth->GetMethodTable()->IsValueType()) - pThisPtr = OBJECTREFToObject(gc.target); - else { - if (pMeth->IsUnboxingStub()) - pThisPtr = OBJECTREFToObject(gc.target); - else { - // Create a true boxed Nullable and use that as the 'this' pointer. - // since what is passed in is just a boxed T - MethodTable* pMT = pMeth->GetMethodTable(); - if (Nullable::IsNullableType(pMT)) { - OBJECTREF bufferObj = pMT->Allocate(); - void* buffer = bufferObj->GetData(); - Nullable::UnBox(buffer, gc.target, pMT); - pThisPtr = buffer; - } - else - pThisPtr = gc.target->UnBox(); - } - } - - *((LPVOID*) (pTransitionBlock + argit.GetThisOffset())) = pThisPtr; - } - - // NO GC AFTER THIS POINT. The object references in the method frame are not protected. - // - // We have already copied "this" pointer so we do not want GC to happen even sooner. Unfortunately, - // we may allocate in the process of copying this pointer that makes it hard to express using contracts. - // - // If an exception occurs a gc may happen but we are going to dump the stack anyway and we do - // not need to protect anything. - - // Allocate a local buffer for the return buffer if necessary - PVOID pLocalRetBuf = nullptr; - - { - BEGINFORBIDGC(); -#ifdef _DEBUG - GCForbidLoaderUseHolder forbidLoaderUse; -#endif - - // Take care of any return arguments - if (fHasRetBuffArg) - { - _ASSERT(hasValueTypeReturn); - PTR_MethodTable pMT = retTH.GetMethodTable(); - size_t localRetBufSize = retTH.GetSize(); - - // Allocate a local buffer. The invoked method will write the return value to this - // buffer which will be copied to gc.retVal later. - pLocalRetBuf = _alloca(localRetBufSize); - ZeroMemory(pLocalRetBuf, localRetBufSize); - *((LPVOID*) (pTransitionBlock + argit.GetRetBuffArgOffset())) = pLocalRetBuf; - if (pMT->ContainsGCPointers()) - { - pValueClasses = new (_alloca(sizeof(ValueClassInfo))) ValueClassInfo(pLocalRetBuf, pMT, pValueClasses); - } - } - - // copy args - UINT nNumArgs = gc.pSig->NumFixedArgs(); - for (UINT i = 0 ; i < nNumArgs; i++) { - TypeHandle th = gc.pSig->GetArgumentAt(i); - - int ofs = argit.GetNextOffset(); - _ASSERTE(ofs != TransitionBlock::InvalidOffset); - -#ifdef CALLDESCR_REGTYPEMAP - FillInRegTypeMap(ofs, argit.GetArgType(), (BYTE *)&callDescrData.dwRegTypeMap); -#endif - -#ifdef CALLDESCR_FPARGREGS - // Under CALLDESCR_FPARGREGS -ve offsets indicate arguments in floating point registers. If we have at - // least one such argument we point the call worker at the floating point area of the frame (we leave - // it null otherwise since the worker can perform a useful optimization if it knows no floating point - // registers need to be set up). - - if (TransitionBlock::HasFloatRegister(ofs, argit.GetArgLocDescForStructInRegs()) && - (callDescrData.pFloatArgumentRegisters == NULL)) - { - callDescrData.pFloatArgumentRegisters = (FloatArgumentRegisters*) (pTransitionBlock + - TransitionBlock::GetOffsetOfFloatArgumentRegisters()); - } -#endif - - UINT structSize = argit.GetArgSize(); - - ArgDestination argDest(pTransitionBlock, ofs, argit.GetArgLocDescForStructInRegs()); - -#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE - if (argit.IsArgPassedByRef()) - { - MethodTable* pMT = th.GetMethodTable(); - _ASSERTE(pMT && pMT->IsValueType()); - - PVOID pArgDst = argDest.GetDestinationAddress(); - - PVOID pStackCopy = _alloca(structSize); - *(PVOID *)pArgDst = pStackCopy; - - // save the info into ValueClassInfo - if (pMT->ContainsGCPointers()) - { - pValueClasses = new (_alloca(sizeof(ValueClassInfo))) ValueClassInfo(pStackCopy, pMT, pValueClasses); - } - - // We need a new ArgDestination that points to the stack copy - argDest = ArgDestination(pStackCopy, 0, NULL); - } -#endif - - InvokeUtil::CopyArg(th, args[i], &argDest); - } - - ENDFORBIDGC(); - } - - if (pValueClasses != NULL) - { - pProtectValueClassFrame = new (_alloca (sizeof (ProtectValueClassFrame))) - ProtectValueClassFrame(pThread, pValueClasses); - } - - // Call the method - CallDescrWorkerWithHandler(&callDescrData); - - if (fHasRetBuffArg) - { - // Copy the return value from the return buffer to the object - if (retTH.GetMethodTable()->ContainsGCPointers()) - { - memmoveGCRefs(gc.retVal->GetData(), pLocalRetBuf, retTH.GetSize()); - } - else - { - memcpyNoGCRefs(gc.retVal->GetData(), pLocalRetBuf, retTH.GetSize()); - } - } - - // It is still illegal to do a GC here. The return type might have/contain GC pointers. - if (fConstructor) - { - // We have a special case for Strings...The object is returned... - if (fCtorOfVariableSizedObject) { - PVOID pReturnValue = &callDescrData.returnValue; - gc.retVal = ObjectToOBJECTREF(*(Object**)pReturnValue); - } - - // If it is a Nullable, box it using Nullable conventions. - // TODO: this double allocates on constructions which is wasteful - gc.retVal = Nullable::NormalizeBox(gc.retVal); - } - else - if (hasValueTypeReturn || hasRefReturnAndNeedsBoxing) - { - _ASSERTE(gc.retVal != NULL); - - if (hasRefReturnAndNeedsBoxing) - { - // Method has BYREF return and the target type is one that needs boxing. We need to copy into the boxed object we have allocated for this purpose. - LPVOID pReturnedReference = *(LPVOID*)&callDescrData.returnValue; - if (pReturnedReference == NULL) - { - COMPlusThrow(kNullReferenceException, W("NullReference_InvokeNullRefReturned")); - } - CopyValueClass(gc.retVal->GetData(), pReturnedReference, gc.retVal->GetMethodTable()); - } - // if the structure is returned by value, then we need to copy in the boxed object - // we have allocated for this purpose. - else if (!fHasRetBuffArg) - { -#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) - if (callDescrData.fpReturnSize != FpStruct::UseIntCallConv) - { - FpStructInRegistersInfo info = argit.GetReturnFpStructInRegistersInfo(); - bool hasPointers = gc.retVal->GetMethodTable()->ContainsGCPointers(); - CopyReturnedFpStructFromRegisters(gc.retVal->GetData(), callDescrData.returnValue, info, hasPointers); - } - else -#endif // defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) - { - CopyValueClass(gc.retVal->GetData(), &callDescrData.returnValue, gc.retVal->GetMethodTable()); - } - } - // From here on out, it is OK to have GCs since the return object (which may have had - // GC pointers has been put into a GC object and thus protected. - - // TODO this creates two objects which is inefficient - // If the return type is a Nullable box it into the correct form - gc.retVal = Nullable::NormalizeBox(gc.retVal); - } - else if (retType == ELEMENT_TYPE_BYREF) - { - // WARNING: pReturnedReference is an unprotected inner reference so we must not trigger a GC until the referenced value has been safely captured. - LPVOID pReturnedReference = *(LPVOID*)&callDescrData.returnValue; - if (pReturnedReference == NULL) - { - COMPlusThrow(kNullReferenceException, W("NullReference_InvokeNullRefReturned")); - } - - gc.retVal = InvokeUtil::CreateObjectAfterInvoke(refReturnTargetTH, pReturnedReference); - } - else - { - gc.retVal = InvokeUtil::CreateObjectAfterInvoke(retTH, &callDescrData.returnValue); - } - - if (pProtectValueClassFrame != NULL) - pProtectValueClassFrame->Pop(pThread); - - } - -Done: - result.Set(gc.retVal); - - GCPROTECT_END(); - - END_QCALL; -} - struct SkipStruct { SkipStruct(StackCrawlMark* mark, PTR_Thread thread) : pStackMark(mark) diff --git a/src/coreclr/vm/runtimecallablewrapper.cpp b/src/coreclr/vm/runtimecallablewrapper.cpp index f705d67b5c98da..67374c0b053079 100644 --- a/src/coreclr/vm/runtimecallablewrapper.cpp +++ b/src/coreclr/vm/runtimecallablewrapper.cpp @@ -1673,7 +1673,13 @@ void RCW::CreateDuplicateWrapper(MethodTable *pNewMT, RCWHolder* pNewRCW) // Run the class constructor if it has not run yet. pNewMT->CheckRunClassInitThrowing(); - CallDefaultConstructor(ObjectToOBJECTREF(NewWrapperObj)); + { + OBJECTREF wrapperRef = ObjectToOBJECTREF(NewWrapperObj); + _ASSERTE(pNewMT->HasDefaultConstructor()); + MethodDesc *pMD = pNewMT->GetDefaultConstructor(); + UnmanagedCallersOnlyCaller defaultCtorInvoker{METHOD__RUNTIME_HELPERS__CALL_DEFAULT_CONSTRUCTOR}; + defaultCtorInvoker.InvokeThrowing(&wrapperRef, pMD->GetSingleCallableAddrOfCode()); + } pNewRCW->InitNoCheck(NewWrapperObj); diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index de19a93f279f55..7fac6cce410ec6 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -232,13 +232,6 @@ extern "C" BOOL QCALLTYPE RuntimeMethodHandle_IsCAVisibleFromDecoratedType( extern "C" void QCALLTYPE RuntimeMethodHandle_GetMethodInstantiation(MethodDesc * pMethod, QCall::ObjectHandleOnStack retTypes, BOOL fAsRuntimeTypeArray); -extern "C" void QCALLTYPE RuntimeMethodHandle_InvokeMethod( - QCall::ObjectHandleOnStack target, - PVOID* args, - QCall::ObjectHandleOnStack pSigUNSAFE, - BOOL fConstructor, - QCall::ObjectHandleOnStack result); - extern "C" void QCALLTYPE RuntimeMethodHandle_ConstructInstantiation(MethodDesc * pMethod, DWORD format, QCall::StringHandleOnStack retString); extern "C" void* QCALLTYPE RuntimeMethodHandle_GetFunctionPointer(MethodDesc * pMethod); extern "C" BOOL QCALLTYPE RuntimeMethodHandle_GetIsCollectible(MethodDesc * pMethod); diff --git a/src/coreclr/vm/wasm/callhelpers-reverse.cpp b/src/coreclr/vm/wasm/callhelpers-reverse.cpp index 0d20019d2f13c1..271100434e0d70 100644 --- a/src/coreclr/vm/wasm/callhelpers-reverse.cpp +++ b/src/coreclr/vm/wasm/callhelpers-reverse.cpp @@ -610,6 +610,19 @@ static void Call_System_Private_CoreLib_System_Threading_Lock_InitializeForMonit ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Threading_Lock_InitializeForMonitor_I32_I32_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Threading_Lock_InitializeForMonitor_I32_I32_I32_I32_RetVoid); } +static MethodDesc* MD_System_Private_CoreLib_System_Reflection_CustomAttribute_InvokeCustomAttributeCtor_I32_I32_I32_RetVoid = nullptr; +static void Call_System_Private_CoreLib_System_Reflection_CustomAttribute_InvokeCustomAttributeCtor_I32_I32_I32_RetVoid(void * arg0, void * arg1, void * arg2) +{ + int64_t args[3] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2 }; + + // Lazy lookup of MethodDesc for the function export scenario. + if (!MD_System_Private_CoreLib_System_Reflection_CustomAttribute_InvokeCustomAttributeCtor_I32_I32_I32_RetVoid) + { + LookupUnmanagedCallersOnlyMethodByName("System.Reflection.CustomAttribute, System.Private.CoreLib", "InvokeCustomAttributeCtor", &MD_System_Private_CoreLib_System_Reflection_CustomAttribute_InvokeCustomAttributeCtor_I32_I32_I32_RetVoid); + } + ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Reflection_CustomAttribute_InvokeCustomAttributeCtor_I32_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Reflection_CustomAttribute_InvokeCustomAttributeCtor_I32_I32_I32_RetVoid); +} + static MethodDesc* MD_System_Private_CoreLib_System_Exception_InternalPreserveStackTrace_I32_I32_RetVoid = nullptr; static void Call_System_Private_CoreLib_System_Exception_InternalPreserveStackTrace_I32_I32_RetVoid(void * arg0, void * arg1) { @@ -1161,6 +1174,7 @@ const ReverseThunkMapEntry g_ReverseThunks[] = { 513042204, "InitializeDefaultEventSources#1:System.Private.CoreLib:System.Diagnostics.Tracing:EventSource", { &MD_System_Private_CoreLib_System_Diagnostics_Tracing_EventSource_InitializeDefaultEventSources_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Diagnostics_Tracing_EventSource_InitializeDefaultEventSources_I32_RetVoid } }, { 266659693, "InitializeForMonitor#4:System.Private.CoreLib:System.Threading:Lock", { &MD_System_Private_CoreLib_System_Threading_Lock_InitializeForMonitor_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Threading_Lock_InitializeForMonitor_I32_I32_I32_I32_RetVoid } }, { 288803216, "InternalPreserveStackTrace#2:System.Private.CoreLib:System:Exception", { &MD_System_Private_CoreLib_System_Exception_InternalPreserveStackTrace_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Exception_InternalPreserveStackTrace_I32_I32_RetVoid } }, + { 1442849229, "InvokeCustomAttributeCtor#3:System.Private.CoreLib:System.Reflection:CustomAttribute", { &MD_System_Private_CoreLib_System_Reflection_CustomAttribute_InvokeCustomAttributeCtor_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Reflection_CustomAttribute_InvokeCustomAttributeCtor_I32_I32_I32_RetVoid } }, { 3290644746, "IsInterfaceImplemented#5:System.Private.CoreLib:System.Runtime.InteropServices:DynamicInterfaceCastableHelpers", { &MD_System_Private_CoreLib_System_Runtime_InteropServices_DynamicInterfaceCastableHelpers_IsInterfaceImplemented_I32_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Runtime_InteropServices_DynamicInterfaceCastableHelpers_IsInterfaceImplemented_I32_I32_I32_I32_I32_RetVoid } }, { 3422156547, "LoadAssembly#3:System.Private.CoreLib:Internal.Runtime.InteropServices:ComponentActivator", { &MD_System_Private_CoreLib_Internal_Runtime_InteropServices_ComponentActivator_LoadAssembly_I32_I32_I32_RetI32, (void*)&Call_System_Private_CoreLib_Internal_Runtime_InteropServices_ComponentActivator_LoadAssembly_I32_I32_I32_RetI32 } }, { 542185314, "LoadAssemblyAndGetFunctionPointer#6:System.Private.CoreLib:Internal.Runtime.InteropServices:ComponentActivator", { &MD_System_Private_CoreLib_Internal_Runtime_InteropServices_ComponentActivator_LoadAssemblyAndGetFunctionPointer_I32_I32_I32_I32_I32_I32_RetI32, (void*)&Call_System_Private_CoreLib_Internal_Runtime_InteropServices_ComponentActivator_LoadAssemblyAndGetFunctionPointer_I32_I32_I32_I32_I32_I32_RetI32 } }, 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 63a22060a16696..80ac4fd0462190 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs @@ -184,7 +184,11 @@ private object InvokeImpl(object? arg1, object? arg2, object? arg3, object? arg4 if ((_strategy & InvokerStrategy.StrategyDetermined_Obj4Args) == 0) { +#if MONO DetermineStrategy_Obj4Args(ref _strategy, ref _invokeFunc_Obj4Args, _method, _needsByRefStrategy, backwardsCompat: false); +#else + DetermineStrategy_Obj4Args(ref _strategy, ref _invokeFunc_Obj4Args, _method, _needsByRefStrategy); +#endif if (_invokeFunc_Obj4Args is not null) { return _invokeFunc_Obj4Args(obj: null, arg1, arg2, arg3, arg4)!; @@ -264,7 +268,11 @@ internal object InvokeWithFewArgs(Span arguments) if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { +#if MONO DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: false); +#else + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy); +#endif if (_invokeFunc_ObjSpanArgs is not null) { return _invokeFunc_ObjSpanArgs(obj: null, copyOfArgs)!; @@ -286,7 +294,11 @@ internal unsafe object InvokeDirectByRefWithFewArgs(Span copyOfArgs) { if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { +#if MONO DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: false); +#else + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method); +#endif } StackAllocatedByRefs byrefs = default; @@ -310,7 +322,11 @@ internal unsafe object InvokeWithManyArgs(Span arguments) if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { +#if MONO DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: false); +#else + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy); +#endif } if (_invokeFunc_ObjSpanArgs is not null) @@ -343,7 +359,11 @@ internal unsafe object InvokeWithManyArgs(Span arguments) { if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { +#if MONO DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: false); +#else + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method); +#endif } IntPtr* pStorage = stackalloc IntPtr[2 * _argCount]; diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs index 05c6ee32132359..916317d7f95bb9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs @@ -16,7 +16,11 @@ internal static class InvokerEmitUtil internal delegate object? InvokeFunc_ObjSpanArgs(object? obj, Span arguments); internal delegate object? InvokeFunc_Obj4Args(object? obj, object? arg1, object? arg2, object? arg3, object? arg4); +#if MONO public static InvokeFunc_Obj4Args CreateInvokeDelegate_Obj4Args(MethodBase method, bool backwardsCompat) +#else + public static InvokeFunc_Obj4Args CreateInvokeDelegate_Obj4Args(MethodBase method) +#endif { Debug.Assert(!method.ContainsGenericParameters); @@ -77,13 +81,21 @@ public static InvokeFunc_Obj4Args CreateInvokeDelegate_Obj4Args(MethodBase metho } } +#if MONO EmitCallAndReturnHandling(il, method, emitNew, backwardsCompat); +#else + EmitCallAndReturnHandling(il, method, emitNew); +#endif // Create the delegate; it is also compiled at this point due to restrictedSkipVisibility=true. return (InvokeFunc_Obj4Args)dm.CreateDelegate(typeof(InvokeFunc_Obj4Args), target: null); } +#if MONO public static InvokeFunc_ObjSpanArgs CreateInvokeDelegate_ObjSpanArgs(MethodBase method, bool backwardsCompat) +#else + public static InvokeFunc_ObjSpanArgs CreateInvokeDelegate_ObjSpanArgs(MethodBase method) +#endif { Debug.Assert(!method.ContainsGenericParameters); @@ -134,13 +146,21 @@ public static InvokeFunc_ObjSpanArgs CreateInvokeDelegate_ObjSpanArgs(MethodBase } } +#if MONO EmitCallAndReturnHandling(il, method, emitNew, backwardsCompat); +#else + EmitCallAndReturnHandling(il, method, emitNew); +#endif // Create the delegate; it is also compiled at this point due to restrictedSkipVisibility=true. return (InvokeFunc_ObjSpanArgs)dm.CreateDelegate(typeof(InvokeFunc_ObjSpanArgs), target: null); } +#if MONO public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method, bool backwardsCompat) +#else + public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method) +#endif { Debug.Assert(!method.ContainsGenericParameters); @@ -170,6 +190,81 @@ public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method, } } +#if !MONO + if (emitNew) + { + Debug.Assert(method is RuntimeConstructorInfo); + + // Constructor invoke has two behaviors: + // 1) obj == null: allocate and run ctor (normal constructor invoke) + // 2) obj != null: run ctor on existing instance and return null + Label allocateAndInvoke = il.DefineLabel(); + Label done = il.DefineLabel(); + + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Brfalse, allocateAndInvoke); + + il.Emit(OpCodes.Ldarg_1); + if (method.DeclaringType!.IsValueType) + { + il.Emit(OpCodes.Unbox, method.DeclaringType); + } + + // Push the arguments from byref storage. + ReadOnlySpan ctorParameters = method.GetParametersAsSpan(); + for (int i = 0; i < ctorParameters.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.Ldfld, Methods.ByReferenceOfByte_Value()); + + RuntimeType parameterType = (RuntimeType)ctorParameters[i].ParameterType; + if (!parameterType.IsByRef) + { + il.Emit(OpCodes.Ldobj, parameterType.IsPointer || parameterType.IsFunctionPointer ? typeof(IntPtr) : parameterType); + } + } + + il.Emit(OpCodes.Call, (ConstructorInfo)method); + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Br, done); + + il.MarkLabel(allocateAndInvoke); + + // Push the arguments from byref storage. + for (int i = 0; i < ctorParameters.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.Ldfld, Methods.ByReferenceOfByte_Value()); + + RuntimeType parameterType = (RuntimeType)ctorParameters[i].ParameterType; + if (!parameterType.IsByRef) + { + il.Emit(OpCodes.Ldobj, parameterType.IsPointer || parameterType.IsFunctionPointer ? typeof(IntPtr) : parameterType); + } + } + + EmitCallAndReturnHandling(il, method, emitNew); + + il.MarkLabel(done); + il.Emit(OpCodes.Ret); + + // Create the delegate; it is also compiled at this point due to restrictedSkipVisibility=true. + return (InvokeFunc_RefArgs)dm.CreateDelegate(typeof(InvokeFunc_RefArgs), target: null); + } +#endif + // Push the arguments. ReadOnlySpan parameters = method.GetParametersAsSpan(); for (int i = 0; i < parameters.Length; i++) @@ -190,7 +285,11 @@ public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method, } } +#if MONO EmitCallAndReturnHandling(il, method, emitNew, backwardsCompat); +#else + EmitCallAndReturnHandling(il, method, emitNew); +#endif // Create the delegate; it is also compiled at this point due to restrictedSkipVisibility=true. return (InvokeFunc_RefArgs)dm.CreateDelegate(typeof(InvokeFunc_RefArgs), target: null); @@ -205,19 +304,26 @@ private static void Unbox(ILGenerator il, Type parameterType) il.Emit(OpCodes.Ldobj, parameterType); } +#if MONO private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method, bool emitNew, bool backwardsCompat) +#else + private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method, bool emitNew) +#endif { +#if MONO // For CallStack reasons, don't inline target method. // Mono interpreter does not support\need this. if (backwardsCompat && RuntimeFeature.IsDynamicCodeCompiled) { -#if MONO il.Emit(OpCodes.Call, Methods.DisableInline()); + } #else + if (RuntimeFeature.IsDynamicCodeCompiled) + { il.Emit(OpCodes.Call, Methods.NextCallReturnAddress()); il.Emit(OpCodes.Pop); -#endif } +#endif // Invoke the method. if (emitNew) diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.Constructor.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.Constructor.cs index 61ce60ff5bd4ec..05a8c35763d3f4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.Constructor.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.Constructor.cs @@ -48,7 +48,11 @@ internal sealed partial class MethodBaseInvoker try { // Use the interpreted version to avoid having to generate a new method that doesn't allocate. +#if MONO ret = InterpretedInvoke_Constructor(obj, pByRefStorage); +#else + ret = InterpretedInvoke_Method(obj, pByRefStorage); +#endif } catch (Exception e) when (wrapInTargetInvocationException) { @@ -70,7 +74,11 @@ internal sealed partial class MethodBaseInvoker try { // Use the interpreted version to avoid having to generate a new method that doesn't allocate. +#if MONO return InterpretedInvoke_Constructor(obj, null); +#else + return InterpretedInvoke_Method(obj, null); +#endif } catch (Exception e) when (wrapInTargetInvocationException) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.cs index dd46f83e78dc87..5addd345c76365 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.cs @@ -49,7 +49,11 @@ internal static void ThrowTargetParameterCountException() if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { +#if MONO DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: true); +#else + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method); +#endif } try @@ -83,7 +87,11 @@ internal static void ThrowTargetParameterCountException() object? ret; if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { +#if MONO DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: true); +#else + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy); +#endif } CheckArguments(parametersSpan, copyOfArgs, shouldCopyBack, binder, culture, invokeAttr); @@ -124,7 +132,11 @@ internal static void ThrowTargetParameterCountException() object? ret; if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { +#if MONO DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: true); +#else + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy); +#endif } CheckArguments(parameters, copyOfArgs, shouldCopyBack, binder, culture, invokeAttr); @@ -156,7 +168,11 @@ internal static void ThrowTargetParameterCountException() if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { +#if MONO DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: true); +#else + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method); +#endif } StackAllocatedByRefs byrefs = default; @@ -195,7 +211,11 @@ internal static void ThrowTargetParameterCountException() if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { +#if MONO DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: true); +#else + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy); +#endif } if (_invokeFunc_ObjSpanArgs is not null) @@ -232,7 +252,11 @@ internal static void ThrowTargetParameterCountException() { if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { +#if MONO DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: true); +#else + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method); +#endif } IntPtr* pStorage = stackalloc IntPtr[3 * _argCount]; @@ -312,7 +336,11 @@ internal void InvokePropertySetter( if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { // Initialize for next time. +#if MONO DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: true); +#else + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy); +#endif } InvokeDirectByRefWithFewArgs(obj, copyOfArgs, invokeAttr); 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 f03b1d420858a8..33aebbd33e4821 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs @@ -186,6 +186,20 @@ private MethodInvoker(MethodBase method, RuntimeType[] argumentTypes) private object? InvokeImpl(object? obj, object? arg1, object? arg2, object? arg3, object? arg4) { + if (_method is RuntimeConstructorInfo constructor && obj is not null) + { + object?[] parameters = _argCount switch + { + 0 => [], + 1 => [arg1], + 2 => [arg1, arg2], + 3 => [arg1, arg2, arg3], + _ => [arg1, arg2, arg3, arg4], + }; + + return constructor.Invoke(obj, BindingFlags.DoNotWrapExceptions, binder: null, parameters, culture: null); + } + if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0) { ThrowForBadInvocationFlags(); @@ -220,7 +234,11 @@ private MethodInvoker(MethodBase method, RuntimeType[] argumentTypes) if ((_strategy & InvokerStrategy.StrategyDetermined_Obj4Args) == 0) { +#if MONO DetermineStrategy_Obj4Args(ref _strategy, ref _invokeFunc_Obj4Args, _method, _needsByRefStrategy, backwardsCompat: false); +#else + DetermineStrategy_Obj4Args(ref _strategy, ref _invokeFunc_Obj4Args, _method, _needsByRefStrategy); +#endif if (_invokeFunc_Obj4Args is not null) { return _invokeFunc_Obj4Args(obj, arg1, arg2, arg3, arg4); @@ -244,6 +262,14 @@ private MethodInvoker(MethodBase method, RuntimeType[] argumentTypes) MethodBaseInvoker.ThrowTargetParameterCountException(); } + if (_method is RuntimeConstructorInfo constructor && obj is not null) + { + object?[] parameters = arguments.ToArray(); + object? ret = constructor.Invoke(obj, BindingFlags.DoNotWrapExceptions, binder: null, parameters, culture: null); + parameters.AsSpan().CopyTo(arguments); + return ret; + } + if (!_needsByRefStrategy) { // Switch to fast path if possible. @@ -317,7 +343,11 @@ private void ThrowForBadInvocationFlags() if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { +#if MONO DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: false); +#else + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy); +#endif if (_invokeFunc_ObjSpanArgs is not null) { return _invokeFunc_ObjSpanArgs(obj, copyOfArgs); @@ -339,7 +369,11 @@ private void ThrowForBadInvocationFlags() { if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { +#if MONO DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: false); +#else + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method); +#endif } StackAllocatedByRefs byrefs = default; @@ -363,7 +397,11 @@ private void ThrowForBadInvocationFlags() if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { +#if MONO DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: false); +#else + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy); +#endif } if (_invokeFunc_ObjSpanArgs is not null) @@ -396,7 +434,11 @@ private void ThrowForBadInvocationFlags() { if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { +#if MONO DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: false); +#else + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method); +#endif } IntPtr* pStorage = stackalloc IntPtr[2 * _argCount]; diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs index 3e8bdb5778970b..d28074fc4a779e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs @@ -105,8 +105,12 @@ internal static void DetermineStrategy_ObjSpanArgs( ref InvokeFunc_ObjSpanArgs? invokeFunc_ObjSpanArgs, MethodBase method, +#if MONO bool needsByRefStrategy, bool backwardsCompat) +#else + bool needsByRefStrategy) +#endif { if (needsByRefStrategy) { @@ -123,7 +127,11 @@ ref InvokeFunc_ObjSpanArgs? { if (RuntimeFeature.IsDynamicCodeSupported) { +#if MONO invokeFunc_ObjSpanArgs = CreateInvokeDelegate_ObjSpanArgs(method, backwardsCompat); +#else + invokeFunc_ObjSpanArgs = CreateInvokeDelegate_ObjSpanArgs(method); +#endif } strategy |= InvokerStrategy.StrategyDetermined_ObjSpanArgs; @@ -134,8 +142,12 @@ internal static void DetermineStrategy_Obj4Args( ref InvokerStrategy strategy, ref InvokeFunc_Obj4Args? invokeFunc_Obj4Args, MethodBase method, +#if MONO bool needsByRefStrategy, bool backwardsCompat) +#else + bool needsByRefStrategy) +#endif { if (needsByRefStrategy) { @@ -152,7 +164,11 @@ internal static void DetermineStrategy_Obj4Args( { if (RuntimeFeature.IsDynamicCodeSupported) { +#if MONO invokeFunc_Obj4Args = CreateInvokeDelegate_Obj4Args(method, backwardsCompat); +#else + invokeFunc_Obj4Args = CreateInvokeDelegate_Obj4Args(method); +#endif } strategy |= InvokerStrategy.StrategyDetermined_Obj4Args; @@ -162,9 +178,46 @@ internal static void DetermineStrategy_Obj4Args( internal static void DetermineStrategy_RefArgs( ref InvokerStrategy strategy, ref InvokeFunc_RefArgs? invokeFunc_RefArgs, +#if MONO MethodBase method, bool backwardsCompat) +#else + MethodBase method) +#endif { +#if !MONO + if (((strategy & InvokerStrategy.HasBeenInvoked_RefArgs) == 0) && !Debugger.IsAttached) + { + // The first time, ignoring race conditions, use the interpreted path (already assigned + // in the constructor). This avoids the cost of emit+JIT for single-use invocations. + strategy |= InvokerStrategy.HasBeenInvoked_RefArgs; + } + else + { + // Skip caching for collectible assemblies: the DynamicMethod holds token + // references to types that would prevent the assembly from being unloaded. + bool isCollectible = method.DeclaringType is { Assembly.IsCollectible: true }; + if (RuntimeFeature.IsDynamicCodeSupported && !isCollectible) + { + invokeFunc_RefArgs = CreateInvokeDelegate_RefArgs(method); + } + + strategy |= InvokerStrategy.StrategyDetermined_RefArgs; + } + + return; +#else + if (!backwardsCompat) + { + if (RuntimeFeature.IsDynamicCodeSupported) + { + invokeFunc_RefArgs = CreateInvokeDelegate_RefArgs(method, backwardsCompat); + } + + strategy |= InvokerStrategy.StrategyDetermined_RefArgs; + return; + } + if (((strategy & InvokerStrategy.HasBeenInvoked_RefArgs) == 0) && !Debugger.IsAttached) { // The first time, ignoring race conditions, use the slow path, except for the case when running under a debugger. @@ -175,11 +228,16 @@ internal static void DetermineStrategy_RefArgs( { if (RuntimeFeature.IsDynamicCodeSupported) { +#if MONO invokeFunc_RefArgs = CreateInvokeDelegate_RefArgs(method, backwardsCompat); +#else + invokeFunc_RefArgs = CreateInvokeDelegate_RefArgs(method); +#endif } strategy |= InvokerStrategy.StrategyDetermined_RefArgs; } +#endif } } }