From 7a3eaba859d7c3923aaff14e444c38818f56e4db Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Sun, 5 Apr 2026 01:17:16 +0300 Subject: [PATCH 01/16] Convert COMCustomAttribute from MDCS to UCO --- .../Reflection/RuntimeCustomAttributeData.cs | 224 ++++++++++++-- src/coreclr/vm/corelib.h | 3 + src/coreclr/vm/customattribute.cpp | 274 ++++++++++++++---- 3 files changed, 428 insertions(+), 73 deletions(-) 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..2ab5cf3b7b7ac9 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,182 @@ 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; + byte[]? argIsValueType = *pContract->ArgIsValueType; + object? target = *pContract->CtorTarget; + + int argCount = pContract->ArgCount; + if (argCount < 0 || parameters is null || argIsValueType is null || parameters.Length != argCount || argIsValueType.Length != argCount) + { + throw new TargetParameterCountException(SR.Arg_ParmCnt); + } + + if (target is null) + { + throw new InvalidOperationException("Invalid constructor target."); + } + + object ctorTarget = target; + object?[] ctorArgs = parameters; + byte[] argIsValueTypeFlags = argIsValueType; + + if (pContract->CtorEntryPoint == IntPtr.Zero) + { + throw new InvalidOperationException("Invalid constructor entry point."); + } + + if (argCount == 0) + { + ((delegate*)pContract->CtorEntryPoint)(ctorTarget); + *pResult = ctorTarget; + return; + } + + if (argCount == 1) + { + object? arg0 = ctorArgs[0]; + if (argIsValueTypeFlags[0] == 0) + { + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, arg0); + } + else + { + if (arg0 is null) + { + throw new InvalidOperationException("Value-type custom attribute ctor argument cannot be null."); + } + + Type argType = arg0.GetType(); + if (argType.IsEnum) + { + argType = Enum.GetUnderlyingType(argType); + } + + switch (Type.GetTypeCode(argType)) + { + case TypeCode.Boolean: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (bool)arg0); + break; + case TypeCode.Char: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (char)arg0); + break; + case TypeCode.SByte: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (sbyte)arg0); + break; + case TypeCode.Byte: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (byte)arg0); + break; + case TypeCode.Int16: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (short)arg0); + break; + case TypeCode.UInt16: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (ushort)arg0); + break; + case TypeCode.Int32: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (int)arg0); + break; + case TypeCode.UInt32: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (uint)arg0); + break; + case TypeCode.Int64: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (long)arg0); + break; + case TypeCode.UInt64: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (ulong)arg0); + break; + case TypeCode.Single: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (float)arg0); + break; + case TypeCode.Double: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (double)arg0); + break; + default: + throw new InvalidOperationException("Unsupported value-type custom attribute ctor argument."); + } + } + + *pResult = ctorTarget; + return; + } + + if (argCount <= 6) + { + bool hasValueTypeArg = false; + for (int i = 0; i < argCount; i++) + { + if (argIsValueTypeFlags[i] != 0) + { + hasValueTypeArg = true; + } + } + + if (hasValueTypeArg) + { + if (pContract->CtorInvokeStubEntryPoint == IntPtr.Zero) + { + throw new InvalidOperationException("Invalid constructor invoke stub entry point."); + } + + ((delegate*)pContract->CtorInvokeStubEntryPoint)(ctorTarget, ctorArgs, pContract->CtorEntryPoint); + *pResult = ctorTarget; + return; + } + + switch (argCount) + { + case 2: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1]); + break; + case 3: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2]); + break; + case 4: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3]); + break; + case 5: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3], ctorArgs[4]); + break; + case 6: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3], ctorArgs[4], ctorArgs[5]); + break; + } + + *pResult = ctorTarget; + return; + } + + if (pContract->CtorInvokeStubEntryPoint == IntPtr.Zero) + { + throw new InvalidOperationException("Invalid constructor invoke stub entry point."); + } + + ((delegate*)pContract->CtorInvokeStubEntryPoint)(ctorTarget, ctorArgs, pContract->CtorEntryPoint); + *pResult = ctorTarget; + return; + } + catch (Exception ex) + { + *pException = ex; + } + } + + [StructLayout(LayoutKind.Sequential)] + private unsafe struct NativeCtorInvokeContract + { + public object[]* CtorArgs; + public byte[]* ArgIsValueType; + public int ArgCount; + public object* CtorTarget; + public IntPtr CtorEntryPoint; + public IntPtr CtorInvokeStubEntryPoint; + } + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CustomAttribute_CreateCustomAttributeInstance")] private static partial void CreateCustomAttributeInstance( QCallModule pModule, diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index dcab7536023135..77299269abe634 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..0f830545344e5b 100644 --- a/src/coreclr/vm/customattribute.cpp +++ b/src/coreclr/vm/customattribute.cpp @@ -15,6 +15,8 @@ #include "reflectioninvocation.h" #include "runtimehandles.h" #include "typestring.h" +#include "callhelpers.h" +#include "dllimport.h" static TypeHandle GetTypeForEnum(LPCUTF8 szEnumName, COUNT_T cbEnumName, Assembly* pAssembly) { @@ -897,6 +899,126 @@ extern "C" BOOL QCALLTYPE CustomAttribute_ParseAttributeUsageAttribute( return TRUE; } +struct CustomAttributeCtorInvokeHashBlob : ILStubHashBlobBase +{ + MethodDesc* pCtorMD; + MethodTable* pAttributeMT; +}; + +static Signature BuildCustomAttributeCtorInvokeStubSignature(LoaderAllocator* pLoaderAllocator) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(pLoaderAllocator)); + } + CONTRACTL_END; + + SigBuilder sigBuilder; + sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT); + sigBuilder.AppendData(3); + sigBuilder.AppendElementType(ELEMENT_TYPE_VOID); + sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); + sigBuilder.AppendElementType(ELEMENT_TYPE_SZARRAY); + sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); + sigBuilder.AppendElementType(ELEMENT_TYPE_I); + + DWORD cbSig = 0; + PVOID pSigRaw = sigBuilder.GetSignature(&cbSig); + AllocMemHolder pSig(pLoaderAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbSig))); + memcpy(pSig, pSigRaw, cbSig); + + Signature signature(pSig, cbSig); + pSig.SuppressRelease(); + return signature; +} + +static PCODE GetOrCreateCustomAttributeCtorInvokeStub(MethodDesc* pCtorMD, TypeHandle attributeType) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(pCtorMD)); + } + CONTRACTL_END; + + Module* pLoaderModule = pCtorMD->GetLoaderModule(); + + CustomAttributeCtorInvokeHashBlob hashBlob; + hashBlob.m_cbSizeOfBlob = sizeof(hashBlob); + hashBlob.pCtorMD = pCtorMD; + hashBlob.pAttributeMT = attributeType.GetMethodTable(); + + ILStubHashBlob* pHashBlob = reinterpret_cast(&hashBlob); + MethodDesc* pStubMD = pLoaderModule->GetILStubCache()->LookupStubMethodDesc(pHashBlob); + if (pStubMD == NULL) + { + LoaderAllocator* pLoaderAllocator = pCtorMD->GetLoaderAllocator(); + AllocMemTracker amTracker; + Signature stubSig = BuildCustomAttributeCtorInvokeStubSignature(pLoaderAllocator); + + SigTypeContext typeContext(pCtorMD); + ILStubLinker sl( + pCtorMD->GetModule(), + stubSig, + &typeContext, + pCtorMD, + ILSTUB_LINKER_FLAG_NONE); + + ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch); + + MetaSig ctorSig(pCtorMD, attributeType); + UINT argCount = ctorSig.NumFixedArgs(); + + pCode->EmitLDARG(0); + + for (UINT i = 0; i < argCount; i++) + { + CorElementType type = ctorSig.NextArg(); + _ASSERTE(type != ELEMENT_TYPE_END); + TypeHandle argType = ctorSig.GetLastTypeHandleThrowing(); + + pCode->EmitLDARG(1); + pCode->EmitLDC(i); + pCode->EmitLDELEM_REF(); + pCode->EmitUNBOX_ANY(pCode->GetToken(argType)); + } + + pCode->EmitLDARG(2); + pCode->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, argCount + 1, 0); + pCode->EmitRET(); + + pStubMD = ILStubCache::CreateAndLinkNewILStubMethodDesc( + pLoaderAllocator, + pLoaderModule->GetILStubCache()->GetOrCreateStubMethodTable(pLoaderModule), + ILSTUB_DELEGATE_SHUFFLE_THUNK, + pCtorMD->GetModule(), + stubSig.GetRawSig(), + stubSig.GetRawSigLen(), + &typeContext, + &sl); + + MetaSig targetSigMeta(pCtorMD, attributeType); + SigBuilder targetSigBuilder; + MethodDesc::CreateDerivedTargetSig(targetSigMeta, &targetSigBuilder); + + DWORD cbTargetSig = 0; + PCCOR_SIGNATURE pTargetSig = reinterpret_cast(targetSigBuilder.GetSignature(&cbTargetSig)); + + ILStubResolver* pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver(); + pResolver->SetStubTargetMethodSig(pTargetSig, cbTargetSig); + pResolver->SetStubTargetMethodDesc(pCtorMD); + + pStubMD = pLoaderModule->GetILStubCache()->InsertStubMethodDesc(pStubMD, pHashBlob); + } + + return pStubMD->GetSingleCallableAddrOfCode(); +} + extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( QCall::ModuleHandle pModule, QCall::ObjectHandleOnStack pCaType, @@ -915,23 +1037,33 @@ 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 + { + OBJECTREF ctorArgs; + OBJECTREF ctorArgIsValueTypeFlags; + OBJECTREF ctorArgsObj; + OBJECTREF ctorTarget; + OBJECTREF ctorResult; + } gc; + gc.ctorArgs = NULL; + gc.ctorArgIsValueTypeFlags = NULL; + gc.ctorArgsObj = NULL; + gc.ctorTarget = NULL; + gc.ctorResult = NULL; + GCPROTECT_BEGIN(gc); + + gc.ctorArgs = (OBJECTREF)AllocateObjectArray(cArgs, g_pObjectClass); + gc.ctorArgIsValueTypeFlags = (OBJECTREF)(U1ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_U1, cArgs); + gc.ctorTarget = th.GetMethodTable()->Allocate(); + + BYTE* argIsValueTypeFlags = ((U1ARRAYREF)gc.ctorArgIsValueTypeFlags)->GetDataPtr(); if (pBlob) { @@ -947,44 +1079,54 @@ 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); - else - args[i] = 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(); + + argIsValueTypeFlags[i] = paramType.IsValueType() ? 1 : 0; + + ARG_SLOT data = GetDataFromBlob( + pCtorMD->GetAssembly(), + (CorSerializationType)type, + argTypeForParse, + &pBlob, + pEndBlob, + pModule, + &bObjectCreated); + + OBJECTREF argObj = NULL; + if (bObjectCreated) + { + argObj = ArgSlotToObj(data); } - } - 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. - pSig->Reset(); - - for (i = 1; i < cArgs; i++) - { - if (argToProtect[i] != NULL) + else { - _ASSERTE(args[i] == (ARG_SLOT)NULL); - args[i] = ObjToArgSlot(argToProtect[i]); + MethodTable* pMTValue = (type == ELEMENT_TYPE_VALUETYPE || type == ELEMENT_TYPE_CLASS) + ? argTypeForParse.GetMethodTable() + : CoreLibBinder::GetElementType(type); + + _ASSERTE(pMTValue != NULL); + argObj = pMTValue->Box((void*)ArgSlotEndiannessFixup(&data, pMTValue->GetNumInstanceFieldBytes())); } + + ((PTRARRAYREF)gc.ctorArgs)->SetAt(i, argObj); } + + // Reset signature enumeration before we leave argument processing. + pSig->Reset(); } } - args[0] = ObjToArgSlot(argToProtect[0]); if (i != cArgs) COMPlusThrow(kCustomAttributeFormatException); @@ -1009,12 +1151,46 @@ 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()); + bool needsCtorInvokeStub = (cArgs > 6); + if (!needsCtorInvokeStub && cArgs > 1) + { + for (UINT argIndex = 0; argIndex < cArgs; argIndex++) + { + if (argIsValueTypeFlags[argIndex] != 0) + { + needsCtorInvokeStub = true; + break; + } + } + } + + gc.ctorArgsObj = gc.ctorArgs; + PCODE ctorEntryPoint = pCtorMD->GetSingleCallableAddrOfCode(); + PCODE ctorInvokeStubEntryPoint = needsCtorInvokeStub ? GetOrCreateCustomAttributeCtorInvokeStub(pCtorMD, th) : (PCODE)NULL; + + struct NativeCtorInvokeContract + { + OBJECTREF* ctorArgs; + OBJECTREF* argIsValueType; + INT32 argCount; + OBJECTREF* ctorTarget; + PCODE ctorEntryPoint; + PCODE ctorInvokeStubEntryPoint; + } contract; + + contract.ctorArgs = &gc.ctorArgsObj; + contract.argIsValueType = &gc.ctorArgIsValueTypeFlags; + contract.argCount = (INT32)cArgs; + contract.ctorTarget = &gc.ctorTarget; + contract.ctorEntryPoint = ctorEntryPoint; + contract.ctorInvokeStubEntryPoint = ctorInvokeStubEntryPoint; + + UnmanagedCallersOnlyCaller invokeCustomAttributeCtor(METHOD__CUSTOMATTRIBUTE__INVOKE_CUSTOM_ATTRIBUTE_CTOR); + invokeCustomAttributeCtor.InvokeThrowing(&contract, &gc.ctorResult); + + result.Set(gc.ctorResult); - ctorCallSite.CallWithValueTypes(args); + GCPROTECT_END(); END_QCALL; } From f3e9b8205522643d6c3053c9bb658181bcb91825 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Sun, 5 Apr 2026 14:38:13 +0300 Subject: [PATCH 02/16] Address Aaron's feedback --- .../Reflection/RuntimeCustomAttributeData.cs | 24 +++++++++---------- src/coreclr/vm/customattribute.cpp | 23 ++++++++---------- 2 files changed, 22 insertions(+), 25 deletions(-) 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 2ab5cf3b7b7ac9..d04b2209b0c747 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs @@ -1866,7 +1866,7 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p try { object?[]? parameters = *pContract->CtorArgs; - byte[]? argIsValueType = *pContract->ArgIsValueType; + bool[]? argIsValueType = *pContract->ArgIsValueType; object? target = *pContract->CtorTarget; int argCount = pContract->ArgCount; @@ -1882,9 +1882,9 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p object ctorTarget = target; object?[] ctorArgs = parameters; - byte[] argIsValueTypeFlags = argIsValueType; + bool[] argIsValueTypeFlags = argIsValueType; - if (pContract->CtorEntryPoint == IntPtr.Zero) + if (pContract->CtorEntryPoint == null) { throw new InvalidOperationException("Invalid constructor entry point."); } @@ -1899,7 +1899,7 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p if (argCount == 1) { object? arg0 = ctorArgs[0]; - if (argIsValueTypeFlags[0] == 0) + if (!argIsValueTypeFlags[0]) { InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, arg0); } @@ -1968,7 +1968,7 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p bool hasValueTypeArg = false; for (int i = 0; i < argCount; i++) { - if (argIsValueTypeFlags[i] != 0) + if (argIsValueTypeFlags[i]) { hasValueTypeArg = true; } @@ -1976,12 +1976,12 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p if (hasValueTypeArg) { - if (pContract->CtorInvokeStubEntryPoint == IntPtr.Zero) + if (pContract->CtorInvokeStubEntryPoint == null) { throw new InvalidOperationException("Invalid constructor invoke stub entry point."); } - ((delegate*)pContract->CtorInvokeStubEntryPoint)(ctorTarget, ctorArgs, pContract->CtorEntryPoint); + ((delegate*)pContract->CtorInvokeStubEntryPoint)(ctorTarget, ctorArgs, pContract->CtorEntryPoint); *pResult = ctorTarget; return; } @@ -2009,12 +2009,12 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p return; } - if (pContract->CtorInvokeStubEntryPoint == IntPtr.Zero) + if (pContract->CtorInvokeStubEntryPoint == null) { throw new InvalidOperationException("Invalid constructor invoke stub entry point."); } - ((delegate*)pContract->CtorInvokeStubEntryPoint)(ctorTarget, ctorArgs, pContract->CtorEntryPoint); + ((delegate*)pContract->CtorInvokeStubEntryPoint)(ctorTarget, ctorArgs, pContract->CtorEntryPoint); *pResult = ctorTarget; return; } @@ -2028,11 +2028,11 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p private unsafe struct NativeCtorInvokeContract { public object[]* CtorArgs; - public byte[]* ArgIsValueType; + public bool[]* ArgIsValueType; public int ArgCount; public object* CtorTarget; - public IntPtr CtorEntryPoint; - public IntPtr CtorInvokeStubEntryPoint; + public void* CtorEntryPoint; + public void* CtorInvokeStubEntryPoint; } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CustomAttribute_CreateCustomAttributeInstance")] diff --git a/src/coreclr/vm/customattribute.cpp b/src/coreclr/vm/customattribute.cpp index 0f830545344e5b..9db020f10b8cdd 100644 --- a/src/coreclr/vm/customattribute.cpp +++ b/src/coreclr/vm/customattribute.cpp @@ -1046,24 +1046,22 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( struct { - OBJECTREF ctorArgs; - OBJECTREF ctorArgIsValueTypeFlags; - OBJECTREF ctorArgsObj; + PTRARRAYREF ctorArgs; + BOOLARRAYREF ctorArgIsValueTypeFlags; OBJECTREF ctorTarget; OBJECTREF ctorResult; } gc; gc.ctorArgs = NULL; gc.ctorArgIsValueTypeFlags = NULL; - gc.ctorArgsObj = NULL; gc.ctorTarget = NULL; gc.ctorResult = NULL; GCPROTECT_BEGIN(gc); - gc.ctorArgs = (OBJECTREF)AllocateObjectArray(cArgs, g_pObjectClass); - gc.ctorArgIsValueTypeFlags = (OBJECTREF)(U1ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_U1, cArgs); + gc.ctorArgs = (PTRARRAYREF)AllocateObjectArray(cArgs, g_pObjectClass); + gc.ctorArgIsValueTypeFlags = (BOOLARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_BOOLEAN, cArgs); gc.ctorTarget = th.GetMethodTable()->Allocate(); - BYTE* argIsValueTypeFlags = ((U1ARRAYREF)gc.ctorArgIsValueTypeFlags)->GetDataPtr(); + CLR_BOOL* argIsValueTypeFlags = reinterpret_cast(static_cast(gc.ctorArgIsValueTypeFlags->GetDataPtr())); if (pBlob) { @@ -1094,7 +1092,7 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( if (argTypeForParse.IsArray()) argTypeForParse = argTypeForParse.GetArrayElementTypeHandle(); - argIsValueTypeFlags[i] = paramType.IsValueType() ? 1 : 0; + argIsValueTypeFlags[i] = CLR_BOOL_ARG(paramType.IsValueType()); ARG_SLOT data = GetDataFromBlob( pCtorMD->GetAssembly(), @@ -1120,7 +1118,7 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( argObj = pMTValue->Box((void*)ArgSlotEndiannessFixup(&data, pMTValue->GetNumInstanceFieldBytes())); } - ((PTRARRAYREF)gc.ctorArgs)->SetAt(i, argObj); + gc.ctorArgs->SetAt(i, argObj); } // Reset signature enumeration before we leave argument processing. @@ -1164,21 +1162,20 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( } } - gc.ctorArgsObj = gc.ctorArgs; PCODE ctorEntryPoint = pCtorMD->GetSingleCallableAddrOfCode(); PCODE ctorInvokeStubEntryPoint = needsCtorInvokeStub ? GetOrCreateCustomAttributeCtorInvokeStub(pCtorMD, th) : (PCODE)NULL; struct NativeCtorInvokeContract { - OBJECTREF* ctorArgs; - OBJECTREF* argIsValueType; + PTRARRAYREF* ctorArgs; + BOOLARRAYREF* argIsValueType; INT32 argCount; OBJECTREF* ctorTarget; PCODE ctorEntryPoint; PCODE ctorInvokeStubEntryPoint; } contract; - contract.ctorArgs = &gc.ctorArgsObj; + contract.ctorArgs = &gc.ctorArgs; contract.argIsValueType = &gc.ctorArgIsValueTypeFlags; contract.argCount = (INT32)cArgs; contract.ctorTarget = &gc.ctorTarget; From 3e6efe5703f39d39934caeea5d123fd7e927bb5c Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Sun, 5 Apr 2026 14:38:32 +0300 Subject: [PATCH 03/16] Address Jan's feedback --- .../Reflection/RuntimeCustomAttributeData.cs | 158 +++-------------- src/coreclr/vm/customattribute.cpp | 165 ++---------------- 2 files changed, 30 insertions(+), 293 deletions(-) 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 d04b2209b0c747..d64b253e4b426e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs @@ -1866,157 +1866,41 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p try { object?[]? parameters = *pContract->CtorArgs; - bool[]? argIsValueType = *pContract->ArgIsValueType; - object? target = *pContract->CtorTarget; + object? ctorObject = *pContract->CtorMethod; + object? ctorDeclaringTypeObject = *pContract->CtorDeclaringType; int argCount = pContract->ArgCount; - if (argCount < 0 || parameters is null || argIsValueType is null || parameters.Length != argCount || argIsValueType.Length != argCount) + if (argCount < 0 || parameters is null || parameters.Length != argCount) { throw new TargetParameterCountException(SR.Arg_ParmCnt); } - if (target is null) + if (ctorObject is not System.IRuntimeMethodInfo methodInfo) { - throw new InvalidOperationException("Invalid constructor target."); + throw new InvalidOperationException("Invalid custom attribute constructor."); } - object ctorTarget = target; - object?[] ctorArgs = parameters; - bool[] argIsValueTypeFlags = argIsValueType; - - if (pContract->CtorEntryPoint == null) + if (ctorDeclaringTypeObject is not RuntimeType ctorDeclaringType) { - throw new InvalidOperationException("Invalid constructor entry point."); + throw new InvalidOperationException("Invalid custom attribute constructor."); } - if (argCount == 0) + RuntimeMethodHandle methodHandle = new(methodInfo); + if (MethodBase.GetMethodFromHandle(methodHandle, ctorDeclaringType.TypeHandle) is not RuntimeConstructorInfo ctor) { - ((delegate*)pContract->CtorEntryPoint)(ctorTarget); - *pResult = ctorTarget; - return; + throw new InvalidOperationException("Invalid custom attribute constructor."); } - if (argCount == 1) + MethodBaseInvoker invoker = new(ctor); + object? result = argCount switch { - object? arg0 = ctorArgs[0]; - if (!argIsValueTypeFlags[0]) - { - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, arg0); - } - else - { - if (arg0 is null) - { - throw new InvalidOperationException("Value-type custom attribute ctor argument cannot be null."); - } - - Type argType = arg0.GetType(); - if (argType.IsEnum) - { - argType = Enum.GetUnderlyingType(argType); - } + 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), + }; - switch (Type.GetTypeCode(argType)) - { - case TypeCode.Boolean: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (bool)arg0); - break; - case TypeCode.Char: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (char)arg0); - break; - case TypeCode.SByte: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (sbyte)arg0); - break; - case TypeCode.Byte: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (byte)arg0); - break; - case TypeCode.Int16: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (short)arg0); - break; - case TypeCode.UInt16: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (ushort)arg0); - break; - case TypeCode.Int32: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (int)arg0); - break; - case TypeCode.UInt32: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (uint)arg0); - break; - case TypeCode.Int64: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (long)arg0); - break; - case TypeCode.UInt64: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (ulong)arg0); - break; - case TypeCode.Single: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (float)arg0); - break; - case TypeCode.Double: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (double)arg0); - break; - default: - throw new InvalidOperationException("Unsupported value-type custom attribute ctor argument."); - } - } - - *pResult = ctorTarget; - return; - } - - if (argCount <= 6) - { - bool hasValueTypeArg = false; - for (int i = 0; i < argCount; i++) - { - if (argIsValueTypeFlags[i]) - { - hasValueTypeArg = true; - } - } - - if (hasValueTypeArg) - { - if (pContract->CtorInvokeStubEntryPoint == null) - { - throw new InvalidOperationException("Invalid constructor invoke stub entry point."); - } - - ((delegate*)pContract->CtorInvokeStubEntryPoint)(ctorTarget, ctorArgs, pContract->CtorEntryPoint); - *pResult = ctorTarget; - return; - } - - switch (argCount) - { - case 2: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1]); - break; - case 3: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2]); - break; - case 4: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3]); - break; - case 5: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3], ctorArgs[4]); - break; - case 6: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3], ctorArgs[4], ctorArgs[5]); - break; - } - - *pResult = ctorTarget; - return; - } - - if (pContract->CtorInvokeStubEntryPoint == null) - { - throw new InvalidOperationException("Invalid constructor invoke stub entry point."); - } - - ((delegate*)pContract->CtorInvokeStubEntryPoint)(ctorTarget, ctorArgs, pContract->CtorEntryPoint); - *pResult = ctorTarget; - return; + *pResult = result!; } catch (Exception ex) { @@ -2028,11 +1912,9 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p private unsafe struct NativeCtorInvokeContract { public object[]* CtorArgs; - public bool[]* ArgIsValueType; public int ArgCount; - public object* CtorTarget; - public void* CtorEntryPoint; - public void* CtorInvokeStubEntryPoint; + public object* CtorMethod; + public object* CtorDeclaringType; } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CustomAttribute_CreateCustomAttributeInstance")] diff --git a/src/coreclr/vm/customattribute.cpp b/src/coreclr/vm/customattribute.cpp index 9db020f10b8cdd..70723a672f27a3 100644 --- a/src/coreclr/vm/customattribute.cpp +++ b/src/coreclr/vm/customattribute.cpp @@ -16,7 +16,6 @@ #include "runtimehandles.h" #include "typestring.h" #include "callhelpers.h" -#include "dllimport.h" static TypeHandle GetTypeForEnum(LPCUTF8 szEnumName, COUNT_T cbEnumName, Assembly* pAssembly) { @@ -899,126 +898,6 @@ extern "C" BOOL QCALLTYPE CustomAttribute_ParseAttributeUsageAttribute( return TRUE; } -struct CustomAttributeCtorInvokeHashBlob : ILStubHashBlobBase -{ - MethodDesc* pCtorMD; - MethodTable* pAttributeMT; -}; - -static Signature BuildCustomAttributeCtorInvokeStubSignature(LoaderAllocator* pLoaderAllocator) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(CheckPointer(pLoaderAllocator)); - } - CONTRACTL_END; - - SigBuilder sigBuilder; - sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT); - sigBuilder.AppendData(3); - sigBuilder.AppendElementType(ELEMENT_TYPE_VOID); - sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); - sigBuilder.AppendElementType(ELEMENT_TYPE_SZARRAY); - sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); - sigBuilder.AppendElementType(ELEMENT_TYPE_I); - - DWORD cbSig = 0; - PVOID pSigRaw = sigBuilder.GetSignature(&cbSig); - AllocMemHolder pSig(pLoaderAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbSig))); - memcpy(pSig, pSigRaw, cbSig); - - Signature signature(pSig, cbSig); - pSig.SuppressRelease(); - return signature; -} - -static PCODE GetOrCreateCustomAttributeCtorInvokeStub(MethodDesc* pCtorMD, TypeHandle attributeType) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(CheckPointer(pCtorMD)); - } - CONTRACTL_END; - - Module* pLoaderModule = pCtorMD->GetLoaderModule(); - - CustomAttributeCtorInvokeHashBlob hashBlob; - hashBlob.m_cbSizeOfBlob = sizeof(hashBlob); - hashBlob.pCtorMD = pCtorMD; - hashBlob.pAttributeMT = attributeType.GetMethodTable(); - - ILStubHashBlob* pHashBlob = reinterpret_cast(&hashBlob); - MethodDesc* pStubMD = pLoaderModule->GetILStubCache()->LookupStubMethodDesc(pHashBlob); - if (pStubMD == NULL) - { - LoaderAllocator* pLoaderAllocator = pCtorMD->GetLoaderAllocator(); - AllocMemTracker amTracker; - Signature stubSig = BuildCustomAttributeCtorInvokeStubSignature(pLoaderAllocator); - - SigTypeContext typeContext(pCtorMD); - ILStubLinker sl( - pCtorMD->GetModule(), - stubSig, - &typeContext, - pCtorMD, - ILSTUB_LINKER_FLAG_NONE); - - ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch); - - MetaSig ctorSig(pCtorMD, attributeType); - UINT argCount = ctorSig.NumFixedArgs(); - - pCode->EmitLDARG(0); - - for (UINT i = 0; i < argCount; i++) - { - CorElementType type = ctorSig.NextArg(); - _ASSERTE(type != ELEMENT_TYPE_END); - TypeHandle argType = ctorSig.GetLastTypeHandleThrowing(); - - pCode->EmitLDARG(1); - pCode->EmitLDC(i); - pCode->EmitLDELEM_REF(); - pCode->EmitUNBOX_ANY(pCode->GetToken(argType)); - } - - pCode->EmitLDARG(2); - pCode->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, argCount + 1, 0); - pCode->EmitRET(); - - pStubMD = ILStubCache::CreateAndLinkNewILStubMethodDesc( - pLoaderAllocator, - pLoaderModule->GetILStubCache()->GetOrCreateStubMethodTable(pLoaderModule), - ILSTUB_DELEGATE_SHUFFLE_THUNK, - pCtorMD->GetModule(), - stubSig.GetRawSig(), - stubSig.GetRawSigLen(), - &typeContext, - &sl); - - MetaSig targetSigMeta(pCtorMD, attributeType); - SigBuilder targetSigBuilder; - MethodDesc::CreateDerivedTargetSig(targetSigMeta, &targetSigBuilder); - - DWORD cbTargetSig = 0; - PCCOR_SIGNATURE pTargetSig = reinterpret_cast(targetSigBuilder.GetSignature(&cbTargetSig)); - - ILStubResolver* pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver(); - pResolver->SetStubTargetMethodSig(pTargetSig, cbTargetSig); - pResolver->SetStubTargetMethodDesc(pCtorMD); - - pStubMD = pLoaderModule->GetILStubCache()->InsertStubMethodDesc(pStubMD, pHashBlob); - } - - return pStubMD->GetSingleCallableAddrOfCode(); -} - extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( QCall::ModuleHandle pModule, QCall::ObjectHandleOnStack pCaType, @@ -1047,21 +926,19 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( struct { PTRARRAYREF ctorArgs; - BOOLARRAYREF ctorArgIsValueTypeFlags; - OBJECTREF ctorTarget; + OBJECTREF ctorMethod; + OBJECTREF ctorDeclaringType; OBJECTREF ctorResult; } gc; gc.ctorArgs = NULL; - gc.ctorArgIsValueTypeFlags = NULL; - gc.ctorTarget = NULL; + gc.ctorMethod = NULL; + gc.ctorDeclaringType = NULL; gc.ctorResult = NULL; GCPROTECT_BEGIN(gc); gc.ctorArgs = (PTRARRAYREF)AllocateObjectArray(cArgs, g_pObjectClass); - gc.ctorArgIsValueTypeFlags = (BOOLARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_BOOLEAN, cArgs); - gc.ctorTarget = th.GetMethodTable()->Allocate(); - - CLR_BOOL* argIsValueTypeFlags = reinterpret_cast(static_cast(gc.ctorArgIsValueTypeFlags->GetDataPtr())); + gc.ctorMethod = pMethod.Get(); + gc.ctorDeclaringType = pCaType.Get(); if (pBlob) { @@ -1092,8 +969,6 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( if (argTypeForParse.IsArray()) argTypeForParse = argTypeForParse.GetArrayElementTypeHandle(); - argIsValueTypeFlags[i] = CLR_BOOL_ARG(paramType.IsValueType()); - ARG_SLOT data = GetDataFromBlob( pCtorMD->GetAssembly(), (CorSerializationType)type, @@ -1149,38 +1024,18 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( if (*pcNamedArgs == 0 && pBlob != pEndBlob) COMPlusThrow(kCustomAttributeFormatException); - bool needsCtorInvokeStub = (cArgs > 6); - if (!needsCtorInvokeStub && cArgs > 1) - { - for (UINT argIndex = 0; argIndex < cArgs; argIndex++) - { - if (argIsValueTypeFlags[argIndex] != 0) - { - needsCtorInvokeStub = true; - break; - } - } - } - - PCODE ctorEntryPoint = pCtorMD->GetSingleCallableAddrOfCode(); - PCODE ctorInvokeStubEntryPoint = needsCtorInvokeStub ? GetOrCreateCustomAttributeCtorInvokeStub(pCtorMD, th) : (PCODE)NULL; - struct NativeCtorInvokeContract { PTRARRAYREF* ctorArgs; - BOOLARRAYREF* argIsValueType; INT32 argCount; - OBJECTREF* ctorTarget; - PCODE ctorEntryPoint; - PCODE ctorInvokeStubEntryPoint; + OBJECTREF* ctorMethod; + OBJECTREF* ctorDeclaringType; } contract; contract.ctorArgs = &gc.ctorArgs; - contract.argIsValueType = &gc.ctorArgIsValueTypeFlags; contract.argCount = (INT32)cArgs; - contract.ctorTarget = &gc.ctorTarget; - contract.ctorEntryPoint = ctorEntryPoint; - contract.ctorInvokeStubEntryPoint = ctorInvokeStubEntryPoint; + contract.ctorMethod = &gc.ctorMethod; + contract.ctorDeclaringType = &gc.ctorDeclaringType; UnmanagedCallersOnlyCaller invokeCustomAttributeCtor(METHOD__CUSTOMATTRIBUTE__INVOKE_CUSTOM_ATTRIBUTE_CTOR); invokeCustomAttributeCtor.InvokeThrowing(&contract, &gc.ctorResult); From d7f8b33797743affb9e1c1f3053ab8749da82513 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Sun, 5 Apr 2026 21:47:58 +0300 Subject: [PATCH 04/16] Use emitted thunks in RuntimeMethodHandle.InvokeMethod --- .../src/System/RuntimeHandles.cs | 39 +++++++++++++++---- .../src/System/Reflection/InvokerEmitUtil.cs | 19 +++++++-- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 7e9d558c00dd57..e9cf0b9a3322b2 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -1203,16 +1203,37 @@ internal static MdUtf8String GetUtf8Name(RuntimeMethodHandleInternal method) [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; + if (!RuntimeFeature.IsDynamicCodeSupported) + { + 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; + } + + MethodBase? method = RuntimeType.GetMethodBase(sig.DeclaringType, sig.MethodHandle); + if (method is null) + { + throw new InvalidOperationException(SR.Arg_InvalidHandle); + } + + Reflection.InvokerEmitUtil.InvokeFunc_RefArgs thunk = method is Reflection.RuntimeConstructorInfo && !isConstructor + ? s_invokeMethodCtorNoAllocCache.GetValue(method, static m => Reflection.InvokerEmitUtil.CreateInvokeDelegate_RefArgs(m, backwardsCompat: true, constructorWithoutAllocation: true)) + : s_invokeMethodCache.GetValue(method, static m => Reflection.InvokerEmitUtil.CreateInvokeDelegate_RefArgs(m, backwardsCompat: true)); + + return thunk(target, (IntPtr*)arguments); } + private static readonly ConditionalWeakTable s_invokeMethodCache = + new(); + + private static readonly ConditionalWeakTable s_invokeMethodCtorNoAllocCache = + new(); + /// /// For a true boxed Nullable{T}, re-box to a boxed {T} or null, otherwise just return the input. /// @@ -2146,6 +2167,8 @@ public Signature(void* pCorSig, int cCorSig, RuntimeType declaringType) #region Internal Members internal CallingConventions CallingConvention => (CallingConventions)(_managedCallingConventionAndArgIteratorFlags & 0xff); + internal RuntimeMethodHandleInternal MethodHandle => _pMethod; + internal RuntimeType DeclaringType => _declaringType; internal RuntimeType[] Arguments { get 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..d4bddc0767bf2e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs @@ -141,10 +141,14 @@ public static InvokeFunc_ObjSpanArgs CreateInvokeDelegate_ObjSpanArgs(MethodBase } public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method, bool backwardsCompat) + => CreateInvokeDelegate_RefArgs(method, backwardsCompat, constructorWithoutAllocation: false); + + public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method, bool backwardsCompat, bool constructorWithoutAllocation) { Debug.Assert(!method.ContainsGenericParameters); + Debug.Assert(!constructorWithoutAllocation || method is RuntimeConstructorInfo); - bool emitNew = method is RuntimeConstructorInfo; + bool emitNew = method is RuntimeConstructorInfo && !constructorWithoutAllocation; 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. @@ -190,7 +194,7 @@ public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method, } } - EmitCallAndReturnHandling(il, method, emitNew, backwardsCompat); + EmitCallAndReturnHandling(il, method, emitNew, backwardsCompat, constructorWithoutAllocation); // 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,7 +209,7 @@ private static void Unbox(ILGenerator il, Type parameterType) il.Emit(OpCodes.Ldobj, parameterType); } - private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method, bool emitNew, bool backwardsCompat) + private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method, bool emitNew, bool backwardsCompat, bool constructorWithoutAllocation = false) { // For CallStack reasons, don't inline target method. // Mono interpreter does not support\need this. @@ -224,6 +228,11 @@ private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method, { il.Emit(OpCodes.Newobj, (ConstructorInfo)method); } + else if (method is RuntimeConstructorInfo) + { + // Constructor invocation on an existing target object. + il.Emit(OpCodes.Call, (ConstructorInfo)method); + } else if (method.IsStatic || method.DeclaringType!.IsValueType) { il.Emit(OpCodes.Call, (MethodInfo)method); @@ -242,6 +251,10 @@ private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method, il.Emit(OpCodes.Box, returnType); } } + else if (constructorWithoutAllocation) + { + il.Emit(OpCodes.Ldnull); + } else { RuntimeType returnType; From be9dddcbf533f38b160d403d9d061b1865f87823 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Sun, 5 Apr 2026 23:08:40 +0300 Subject: [PATCH 05/16] Revert reflection stack for benchmarks This reverts commits d7f8b33797743affb9e1c1f3053ab8749da82513 and d7f8b33797743affb9e1c1f3053ab8749da82513. --- .../Reflection/RuntimeCustomAttributeData.cs | 158 ++++++++++++++--- .../src/System/RuntimeHandles.cs | 39 +---- src/coreclr/vm/customattribute.cpp | 165 ++++++++++++++++-- .../src/System/Reflection/InvokerEmitUtil.cs | 19 +- 4 files changed, 304 insertions(+), 77 deletions(-) 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 d64b253e4b426e..d04b2209b0c747 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs @@ -1866,41 +1866,157 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p try { object?[]? parameters = *pContract->CtorArgs; - object? ctorObject = *pContract->CtorMethod; - object? ctorDeclaringTypeObject = *pContract->CtorDeclaringType; + bool[]? argIsValueType = *pContract->ArgIsValueType; + object? target = *pContract->CtorTarget; int argCount = pContract->ArgCount; - if (argCount < 0 || parameters is null || parameters.Length != argCount) + if (argCount < 0 || parameters is null || argIsValueType is null || parameters.Length != argCount || argIsValueType.Length != argCount) { throw new TargetParameterCountException(SR.Arg_ParmCnt); } - if (ctorObject is not System.IRuntimeMethodInfo methodInfo) + if (target is null) { - throw new InvalidOperationException("Invalid custom attribute constructor."); + throw new InvalidOperationException("Invalid constructor target."); } - if (ctorDeclaringTypeObject is not RuntimeType ctorDeclaringType) + object ctorTarget = target; + object?[] ctorArgs = parameters; + bool[] argIsValueTypeFlags = argIsValueType; + + if (pContract->CtorEntryPoint == null) { - throw new InvalidOperationException("Invalid custom attribute constructor."); + throw new InvalidOperationException("Invalid constructor entry point."); } - RuntimeMethodHandle methodHandle = new(methodInfo); - if (MethodBase.GetMethodFromHandle(methodHandle, ctorDeclaringType.TypeHandle) is not RuntimeConstructorInfo ctor) + if (argCount == 0) { - throw new InvalidOperationException("Invalid custom attribute constructor."); + ((delegate*)pContract->CtorEntryPoint)(ctorTarget); + *pResult = ctorTarget; + return; } - MethodBaseInvoker invoker = new(ctor); - object? result = argCount switch + if (argCount == 1) { - 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), - }; + object? arg0 = ctorArgs[0]; + if (!argIsValueTypeFlags[0]) + { + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, arg0); + } + else + { + if (arg0 is null) + { + throw new InvalidOperationException("Value-type custom attribute ctor argument cannot be null."); + } + + Type argType = arg0.GetType(); + if (argType.IsEnum) + { + argType = Enum.GetUnderlyingType(argType); + } - *pResult = result!; + switch (Type.GetTypeCode(argType)) + { + case TypeCode.Boolean: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (bool)arg0); + break; + case TypeCode.Char: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (char)arg0); + break; + case TypeCode.SByte: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (sbyte)arg0); + break; + case TypeCode.Byte: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (byte)arg0); + break; + case TypeCode.Int16: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (short)arg0); + break; + case TypeCode.UInt16: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (ushort)arg0); + break; + case TypeCode.Int32: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (int)arg0); + break; + case TypeCode.UInt32: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (uint)arg0); + break; + case TypeCode.Int64: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (long)arg0); + break; + case TypeCode.UInt64: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (ulong)arg0); + break; + case TypeCode.Single: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (float)arg0); + break; + case TypeCode.Double: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (double)arg0); + break; + default: + throw new InvalidOperationException("Unsupported value-type custom attribute ctor argument."); + } + } + + *pResult = ctorTarget; + return; + } + + if (argCount <= 6) + { + bool hasValueTypeArg = false; + for (int i = 0; i < argCount; i++) + { + if (argIsValueTypeFlags[i]) + { + hasValueTypeArg = true; + } + } + + if (hasValueTypeArg) + { + if (pContract->CtorInvokeStubEntryPoint == null) + { + throw new InvalidOperationException("Invalid constructor invoke stub entry point."); + } + + ((delegate*)pContract->CtorInvokeStubEntryPoint)(ctorTarget, ctorArgs, pContract->CtorEntryPoint); + *pResult = ctorTarget; + return; + } + + switch (argCount) + { + case 2: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1]); + break; + case 3: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2]); + break; + case 4: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3]); + break; + case 5: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3], ctorArgs[4]); + break; + case 6: + InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3], ctorArgs[4], ctorArgs[5]); + break; + } + + *pResult = ctorTarget; + return; + } + + if (pContract->CtorInvokeStubEntryPoint == null) + { + throw new InvalidOperationException("Invalid constructor invoke stub entry point."); + } + + ((delegate*)pContract->CtorInvokeStubEntryPoint)(ctorTarget, ctorArgs, pContract->CtorEntryPoint); + *pResult = ctorTarget; + return; } catch (Exception ex) { @@ -1912,9 +2028,11 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p private unsafe struct NativeCtorInvokeContract { public object[]* CtorArgs; + public bool[]* ArgIsValueType; public int ArgCount; - public object* CtorMethod; - public object* CtorDeclaringType; + public object* CtorTarget; + public void* CtorEntryPoint; + public void* CtorInvokeStubEntryPoint; } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CustomAttribute_CreateCustomAttributeInstance")] diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index e9cf0b9a3322b2..7e9d558c00dd57 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -1203,37 +1203,16 @@ internal static MdUtf8String GetUtf8Name(RuntimeMethodHandleInternal method) [RequiresUnsafe] internal static object? InvokeMethod(object? target, void** arguments, Signature sig, bool isConstructor) { - if (!RuntimeFeature.IsDynamicCodeSupported) - { - 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; - } - - MethodBase? method = RuntimeType.GetMethodBase(sig.DeclaringType, sig.MethodHandle); - if (method is null) - { - throw new InvalidOperationException(SR.Arg_InvalidHandle); - } - - Reflection.InvokerEmitUtil.InvokeFunc_RefArgs thunk = method is Reflection.RuntimeConstructorInfo && !isConstructor - ? s_invokeMethodCtorNoAllocCache.GetValue(method, static m => Reflection.InvokerEmitUtil.CreateInvokeDelegate_RefArgs(m, backwardsCompat: true, constructorWithoutAllocation: true)) - : s_invokeMethodCache.GetValue(method, static m => Reflection.InvokerEmitUtil.CreateInvokeDelegate_RefArgs(m, backwardsCompat: true)); - - return thunk(target, (IntPtr*)arguments); + 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; } - private static readonly ConditionalWeakTable s_invokeMethodCache = - new(); - - private static readonly ConditionalWeakTable s_invokeMethodCtorNoAllocCache = - new(); - /// /// For a true boxed Nullable{T}, re-box to a boxed {T} or null, otherwise just return the input. /// @@ -2167,8 +2146,6 @@ public Signature(void* pCorSig, int cCorSig, RuntimeType declaringType) #region Internal Members internal CallingConventions CallingConvention => (CallingConventions)(_managedCallingConventionAndArgIteratorFlags & 0xff); - internal RuntimeMethodHandleInternal MethodHandle => _pMethod; - internal RuntimeType DeclaringType => _declaringType; internal RuntimeType[] Arguments { get diff --git a/src/coreclr/vm/customattribute.cpp b/src/coreclr/vm/customattribute.cpp index 70723a672f27a3..9db020f10b8cdd 100644 --- a/src/coreclr/vm/customattribute.cpp +++ b/src/coreclr/vm/customattribute.cpp @@ -16,6 +16,7 @@ #include "runtimehandles.h" #include "typestring.h" #include "callhelpers.h" +#include "dllimport.h" static TypeHandle GetTypeForEnum(LPCUTF8 szEnumName, COUNT_T cbEnumName, Assembly* pAssembly) { @@ -898,6 +899,126 @@ extern "C" BOOL QCALLTYPE CustomAttribute_ParseAttributeUsageAttribute( return TRUE; } +struct CustomAttributeCtorInvokeHashBlob : ILStubHashBlobBase +{ + MethodDesc* pCtorMD; + MethodTable* pAttributeMT; +}; + +static Signature BuildCustomAttributeCtorInvokeStubSignature(LoaderAllocator* pLoaderAllocator) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(pLoaderAllocator)); + } + CONTRACTL_END; + + SigBuilder sigBuilder; + sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT); + sigBuilder.AppendData(3); + sigBuilder.AppendElementType(ELEMENT_TYPE_VOID); + sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); + sigBuilder.AppendElementType(ELEMENT_TYPE_SZARRAY); + sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); + sigBuilder.AppendElementType(ELEMENT_TYPE_I); + + DWORD cbSig = 0; + PVOID pSigRaw = sigBuilder.GetSignature(&cbSig); + AllocMemHolder pSig(pLoaderAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbSig))); + memcpy(pSig, pSigRaw, cbSig); + + Signature signature(pSig, cbSig); + pSig.SuppressRelease(); + return signature; +} + +static PCODE GetOrCreateCustomAttributeCtorInvokeStub(MethodDesc* pCtorMD, TypeHandle attributeType) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(pCtorMD)); + } + CONTRACTL_END; + + Module* pLoaderModule = pCtorMD->GetLoaderModule(); + + CustomAttributeCtorInvokeHashBlob hashBlob; + hashBlob.m_cbSizeOfBlob = sizeof(hashBlob); + hashBlob.pCtorMD = pCtorMD; + hashBlob.pAttributeMT = attributeType.GetMethodTable(); + + ILStubHashBlob* pHashBlob = reinterpret_cast(&hashBlob); + MethodDesc* pStubMD = pLoaderModule->GetILStubCache()->LookupStubMethodDesc(pHashBlob); + if (pStubMD == NULL) + { + LoaderAllocator* pLoaderAllocator = pCtorMD->GetLoaderAllocator(); + AllocMemTracker amTracker; + Signature stubSig = BuildCustomAttributeCtorInvokeStubSignature(pLoaderAllocator); + + SigTypeContext typeContext(pCtorMD); + ILStubLinker sl( + pCtorMD->GetModule(), + stubSig, + &typeContext, + pCtorMD, + ILSTUB_LINKER_FLAG_NONE); + + ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch); + + MetaSig ctorSig(pCtorMD, attributeType); + UINT argCount = ctorSig.NumFixedArgs(); + + pCode->EmitLDARG(0); + + for (UINT i = 0; i < argCount; i++) + { + CorElementType type = ctorSig.NextArg(); + _ASSERTE(type != ELEMENT_TYPE_END); + TypeHandle argType = ctorSig.GetLastTypeHandleThrowing(); + + pCode->EmitLDARG(1); + pCode->EmitLDC(i); + pCode->EmitLDELEM_REF(); + pCode->EmitUNBOX_ANY(pCode->GetToken(argType)); + } + + pCode->EmitLDARG(2); + pCode->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, argCount + 1, 0); + pCode->EmitRET(); + + pStubMD = ILStubCache::CreateAndLinkNewILStubMethodDesc( + pLoaderAllocator, + pLoaderModule->GetILStubCache()->GetOrCreateStubMethodTable(pLoaderModule), + ILSTUB_DELEGATE_SHUFFLE_THUNK, + pCtorMD->GetModule(), + stubSig.GetRawSig(), + stubSig.GetRawSigLen(), + &typeContext, + &sl); + + MetaSig targetSigMeta(pCtorMD, attributeType); + SigBuilder targetSigBuilder; + MethodDesc::CreateDerivedTargetSig(targetSigMeta, &targetSigBuilder); + + DWORD cbTargetSig = 0; + PCCOR_SIGNATURE pTargetSig = reinterpret_cast(targetSigBuilder.GetSignature(&cbTargetSig)); + + ILStubResolver* pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver(); + pResolver->SetStubTargetMethodSig(pTargetSig, cbTargetSig); + pResolver->SetStubTargetMethodDesc(pCtorMD); + + pStubMD = pLoaderModule->GetILStubCache()->InsertStubMethodDesc(pStubMD, pHashBlob); + } + + return pStubMD->GetSingleCallableAddrOfCode(); +} + extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( QCall::ModuleHandle pModule, QCall::ObjectHandleOnStack pCaType, @@ -926,19 +1047,21 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( struct { PTRARRAYREF ctorArgs; - OBJECTREF ctorMethod; - OBJECTREF ctorDeclaringType; + BOOLARRAYREF ctorArgIsValueTypeFlags; + OBJECTREF ctorTarget; OBJECTREF ctorResult; } gc; gc.ctorArgs = NULL; - gc.ctorMethod = NULL; - gc.ctorDeclaringType = NULL; + gc.ctorArgIsValueTypeFlags = NULL; + gc.ctorTarget = NULL; gc.ctorResult = NULL; GCPROTECT_BEGIN(gc); gc.ctorArgs = (PTRARRAYREF)AllocateObjectArray(cArgs, g_pObjectClass); - gc.ctorMethod = pMethod.Get(); - gc.ctorDeclaringType = pCaType.Get(); + gc.ctorArgIsValueTypeFlags = (BOOLARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_BOOLEAN, cArgs); + gc.ctorTarget = th.GetMethodTable()->Allocate(); + + CLR_BOOL* argIsValueTypeFlags = reinterpret_cast(static_cast(gc.ctorArgIsValueTypeFlags->GetDataPtr())); if (pBlob) { @@ -969,6 +1092,8 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( if (argTypeForParse.IsArray()) argTypeForParse = argTypeForParse.GetArrayElementTypeHandle(); + argIsValueTypeFlags[i] = CLR_BOOL_ARG(paramType.IsValueType()); + ARG_SLOT data = GetDataFromBlob( pCtorMD->GetAssembly(), (CorSerializationType)type, @@ -1024,18 +1149,38 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( if (*pcNamedArgs == 0 && pBlob != pEndBlob) COMPlusThrow(kCustomAttributeFormatException); + bool needsCtorInvokeStub = (cArgs > 6); + if (!needsCtorInvokeStub && cArgs > 1) + { + for (UINT argIndex = 0; argIndex < cArgs; argIndex++) + { + if (argIsValueTypeFlags[argIndex] != 0) + { + needsCtorInvokeStub = true; + break; + } + } + } + + PCODE ctorEntryPoint = pCtorMD->GetSingleCallableAddrOfCode(); + PCODE ctorInvokeStubEntryPoint = needsCtorInvokeStub ? GetOrCreateCustomAttributeCtorInvokeStub(pCtorMD, th) : (PCODE)NULL; + struct NativeCtorInvokeContract { PTRARRAYREF* ctorArgs; + BOOLARRAYREF* argIsValueType; INT32 argCount; - OBJECTREF* ctorMethod; - OBJECTREF* ctorDeclaringType; + OBJECTREF* ctorTarget; + PCODE ctorEntryPoint; + PCODE ctorInvokeStubEntryPoint; } contract; contract.ctorArgs = &gc.ctorArgs; + contract.argIsValueType = &gc.ctorArgIsValueTypeFlags; contract.argCount = (INT32)cArgs; - contract.ctorMethod = &gc.ctorMethod; - contract.ctorDeclaringType = &gc.ctorDeclaringType; + contract.ctorTarget = &gc.ctorTarget; + contract.ctorEntryPoint = ctorEntryPoint; + contract.ctorInvokeStubEntryPoint = ctorInvokeStubEntryPoint; UnmanagedCallersOnlyCaller invokeCustomAttributeCtor(METHOD__CUSTOMATTRIBUTE__INVOKE_CUSTOM_ATTRIBUTE_CTOR); invokeCustomAttributeCtor.InvokeThrowing(&contract, &gc.ctorResult); 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 d4bddc0767bf2e..05c6ee32132359 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs @@ -141,14 +141,10 @@ public static InvokeFunc_ObjSpanArgs CreateInvokeDelegate_ObjSpanArgs(MethodBase } public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method, bool backwardsCompat) - => CreateInvokeDelegate_RefArgs(method, backwardsCompat, constructorWithoutAllocation: false); - - public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method, bool backwardsCompat, bool constructorWithoutAllocation) { Debug.Assert(!method.ContainsGenericParameters); - Debug.Assert(!constructorWithoutAllocation || method is RuntimeConstructorInfo); - bool emitNew = method is RuntimeConstructorInfo && !constructorWithoutAllocation; + 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. @@ -194,7 +190,7 @@ public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method, } } - EmitCallAndReturnHandling(il, method, emitNew, backwardsCompat, constructorWithoutAllocation); + EmitCallAndReturnHandling(il, method, emitNew, backwardsCompat); // Create the delegate; it is also compiled at this point due to restrictedSkipVisibility=true. return (InvokeFunc_RefArgs)dm.CreateDelegate(typeof(InvokeFunc_RefArgs), target: null); @@ -209,7 +205,7 @@ private static void Unbox(ILGenerator il, Type parameterType) il.Emit(OpCodes.Ldobj, parameterType); } - private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method, bool emitNew, bool backwardsCompat, bool constructorWithoutAllocation = false) + private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method, bool emitNew, bool backwardsCompat) { // For CallStack reasons, don't inline target method. // Mono interpreter does not support\need this. @@ -228,11 +224,6 @@ private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method, { il.Emit(OpCodes.Newobj, (ConstructorInfo)method); } - else if (method is RuntimeConstructorInfo) - { - // Constructor invocation on an existing target object. - il.Emit(OpCodes.Call, (ConstructorInfo)method); - } else if (method.IsStatic || method.DeclaringType!.IsValueType) { il.Emit(OpCodes.Call, (MethodInfo)method); @@ -251,10 +242,6 @@ private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method, il.Emit(OpCodes.Box, returnType); } } - else if (constructorWithoutAllocation) - { - il.Emit(OpCodes.Ldnull); - } else { RuntimeType returnType; From 31de3bf6c9ffe1076b2f7d12b697d53059eb0307 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Thu, 9 Apr 2026 12:31:36 +0000 Subject: [PATCH 06/16] Revert to 3e6efe5703f39d39934caeea5d123fd7e927bb5c --- .../Reflection/RuntimeCustomAttributeData.cs | 158 +++-------------- src/coreclr/vm/customattribute.cpp | 165 ++---------------- 2 files changed, 30 insertions(+), 293 deletions(-) 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 d04b2209b0c747..d64b253e4b426e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs @@ -1866,157 +1866,41 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p try { object?[]? parameters = *pContract->CtorArgs; - bool[]? argIsValueType = *pContract->ArgIsValueType; - object? target = *pContract->CtorTarget; + object? ctorObject = *pContract->CtorMethod; + object? ctorDeclaringTypeObject = *pContract->CtorDeclaringType; int argCount = pContract->ArgCount; - if (argCount < 0 || parameters is null || argIsValueType is null || parameters.Length != argCount || argIsValueType.Length != argCount) + if (argCount < 0 || parameters is null || parameters.Length != argCount) { throw new TargetParameterCountException(SR.Arg_ParmCnt); } - if (target is null) + if (ctorObject is not System.IRuntimeMethodInfo methodInfo) { - throw new InvalidOperationException("Invalid constructor target."); + throw new InvalidOperationException("Invalid custom attribute constructor."); } - object ctorTarget = target; - object?[] ctorArgs = parameters; - bool[] argIsValueTypeFlags = argIsValueType; - - if (pContract->CtorEntryPoint == null) + if (ctorDeclaringTypeObject is not RuntimeType ctorDeclaringType) { - throw new InvalidOperationException("Invalid constructor entry point."); + throw new InvalidOperationException("Invalid custom attribute constructor."); } - if (argCount == 0) + RuntimeMethodHandle methodHandle = new(methodInfo); + if (MethodBase.GetMethodFromHandle(methodHandle, ctorDeclaringType.TypeHandle) is not RuntimeConstructorInfo ctor) { - ((delegate*)pContract->CtorEntryPoint)(ctorTarget); - *pResult = ctorTarget; - return; + throw new InvalidOperationException("Invalid custom attribute constructor."); } - if (argCount == 1) + MethodBaseInvoker invoker = new(ctor); + object? result = argCount switch { - object? arg0 = ctorArgs[0]; - if (!argIsValueTypeFlags[0]) - { - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, arg0); - } - else - { - if (arg0 is null) - { - throw new InvalidOperationException("Value-type custom attribute ctor argument cannot be null."); - } - - Type argType = arg0.GetType(); - if (argType.IsEnum) - { - argType = Enum.GetUnderlyingType(argType); - } + 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), + }; - switch (Type.GetTypeCode(argType)) - { - case TypeCode.Boolean: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (bool)arg0); - break; - case TypeCode.Char: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (char)arg0); - break; - case TypeCode.SByte: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (sbyte)arg0); - break; - case TypeCode.Byte: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (byte)arg0); - break; - case TypeCode.Int16: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (short)arg0); - break; - case TypeCode.UInt16: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (ushort)arg0); - break; - case TypeCode.Int32: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (int)arg0); - break; - case TypeCode.UInt32: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (uint)arg0); - break; - case TypeCode.Int64: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (long)arg0); - break; - case TypeCode.UInt64: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (ulong)arg0); - break; - case TypeCode.Single: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (float)arg0); - break; - case TypeCode.Double: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, (double)arg0); - break; - default: - throw new InvalidOperationException("Unsupported value-type custom attribute ctor argument."); - } - } - - *pResult = ctorTarget; - return; - } - - if (argCount <= 6) - { - bool hasValueTypeArg = false; - for (int i = 0; i < argCount; i++) - { - if (argIsValueTypeFlags[i]) - { - hasValueTypeArg = true; - } - } - - if (hasValueTypeArg) - { - if (pContract->CtorInvokeStubEntryPoint == null) - { - throw new InvalidOperationException("Invalid constructor invoke stub entry point."); - } - - ((delegate*)pContract->CtorInvokeStubEntryPoint)(ctorTarget, ctorArgs, pContract->CtorEntryPoint); - *pResult = ctorTarget; - return; - } - - switch (argCount) - { - case 2: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1]); - break; - case 3: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2]); - break; - case 4: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3]); - break; - case 5: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3], ctorArgs[4]); - break; - case 6: - InstanceCalliHelper.Call((delegate*)pContract->CtorEntryPoint, ctorTarget, ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3], ctorArgs[4], ctorArgs[5]); - break; - } - - *pResult = ctorTarget; - return; - } - - if (pContract->CtorInvokeStubEntryPoint == null) - { - throw new InvalidOperationException("Invalid constructor invoke stub entry point."); - } - - ((delegate*)pContract->CtorInvokeStubEntryPoint)(ctorTarget, ctorArgs, pContract->CtorEntryPoint); - *pResult = ctorTarget; - return; + *pResult = result!; } catch (Exception ex) { @@ -2028,11 +1912,9 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p private unsafe struct NativeCtorInvokeContract { public object[]* CtorArgs; - public bool[]* ArgIsValueType; public int ArgCount; - public object* CtorTarget; - public void* CtorEntryPoint; - public void* CtorInvokeStubEntryPoint; + public object* CtorMethod; + public object* CtorDeclaringType; } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CustomAttribute_CreateCustomAttributeInstance")] diff --git a/src/coreclr/vm/customattribute.cpp b/src/coreclr/vm/customattribute.cpp index 9db020f10b8cdd..70723a672f27a3 100644 --- a/src/coreclr/vm/customattribute.cpp +++ b/src/coreclr/vm/customattribute.cpp @@ -16,7 +16,6 @@ #include "runtimehandles.h" #include "typestring.h" #include "callhelpers.h" -#include "dllimport.h" static TypeHandle GetTypeForEnum(LPCUTF8 szEnumName, COUNT_T cbEnumName, Assembly* pAssembly) { @@ -899,126 +898,6 @@ extern "C" BOOL QCALLTYPE CustomAttribute_ParseAttributeUsageAttribute( return TRUE; } -struct CustomAttributeCtorInvokeHashBlob : ILStubHashBlobBase -{ - MethodDesc* pCtorMD; - MethodTable* pAttributeMT; -}; - -static Signature BuildCustomAttributeCtorInvokeStubSignature(LoaderAllocator* pLoaderAllocator) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(CheckPointer(pLoaderAllocator)); - } - CONTRACTL_END; - - SigBuilder sigBuilder; - sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT); - sigBuilder.AppendData(3); - sigBuilder.AppendElementType(ELEMENT_TYPE_VOID); - sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); - sigBuilder.AppendElementType(ELEMENT_TYPE_SZARRAY); - sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); - sigBuilder.AppendElementType(ELEMENT_TYPE_I); - - DWORD cbSig = 0; - PVOID pSigRaw = sigBuilder.GetSignature(&cbSig); - AllocMemHolder pSig(pLoaderAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbSig))); - memcpy(pSig, pSigRaw, cbSig); - - Signature signature(pSig, cbSig); - pSig.SuppressRelease(); - return signature; -} - -static PCODE GetOrCreateCustomAttributeCtorInvokeStub(MethodDesc* pCtorMD, TypeHandle attributeType) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(CheckPointer(pCtorMD)); - } - CONTRACTL_END; - - Module* pLoaderModule = pCtorMD->GetLoaderModule(); - - CustomAttributeCtorInvokeHashBlob hashBlob; - hashBlob.m_cbSizeOfBlob = sizeof(hashBlob); - hashBlob.pCtorMD = pCtorMD; - hashBlob.pAttributeMT = attributeType.GetMethodTable(); - - ILStubHashBlob* pHashBlob = reinterpret_cast(&hashBlob); - MethodDesc* pStubMD = pLoaderModule->GetILStubCache()->LookupStubMethodDesc(pHashBlob); - if (pStubMD == NULL) - { - LoaderAllocator* pLoaderAllocator = pCtorMD->GetLoaderAllocator(); - AllocMemTracker amTracker; - Signature stubSig = BuildCustomAttributeCtorInvokeStubSignature(pLoaderAllocator); - - SigTypeContext typeContext(pCtorMD); - ILStubLinker sl( - pCtorMD->GetModule(), - stubSig, - &typeContext, - pCtorMD, - ILSTUB_LINKER_FLAG_NONE); - - ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch); - - MetaSig ctorSig(pCtorMD, attributeType); - UINT argCount = ctorSig.NumFixedArgs(); - - pCode->EmitLDARG(0); - - for (UINT i = 0; i < argCount; i++) - { - CorElementType type = ctorSig.NextArg(); - _ASSERTE(type != ELEMENT_TYPE_END); - TypeHandle argType = ctorSig.GetLastTypeHandleThrowing(); - - pCode->EmitLDARG(1); - pCode->EmitLDC(i); - pCode->EmitLDELEM_REF(); - pCode->EmitUNBOX_ANY(pCode->GetToken(argType)); - } - - pCode->EmitLDARG(2); - pCode->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, argCount + 1, 0); - pCode->EmitRET(); - - pStubMD = ILStubCache::CreateAndLinkNewILStubMethodDesc( - pLoaderAllocator, - pLoaderModule->GetILStubCache()->GetOrCreateStubMethodTable(pLoaderModule), - ILSTUB_DELEGATE_SHUFFLE_THUNK, - pCtorMD->GetModule(), - stubSig.GetRawSig(), - stubSig.GetRawSigLen(), - &typeContext, - &sl); - - MetaSig targetSigMeta(pCtorMD, attributeType); - SigBuilder targetSigBuilder; - MethodDesc::CreateDerivedTargetSig(targetSigMeta, &targetSigBuilder); - - DWORD cbTargetSig = 0; - PCCOR_SIGNATURE pTargetSig = reinterpret_cast(targetSigBuilder.GetSignature(&cbTargetSig)); - - ILStubResolver* pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver(); - pResolver->SetStubTargetMethodSig(pTargetSig, cbTargetSig); - pResolver->SetStubTargetMethodDesc(pCtorMD); - - pStubMD = pLoaderModule->GetILStubCache()->InsertStubMethodDesc(pStubMD, pHashBlob); - } - - return pStubMD->GetSingleCallableAddrOfCode(); -} - extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( QCall::ModuleHandle pModule, QCall::ObjectHandleOnStack pCaType, @@ -1047,21 +926,19 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( struct { PTRARRAYREF ctorArgs; - BOOLARRAYREF ctorArgIsValueTypeFlags; - OBJECTREF ctorTarget; + OBJECTREF ctorMethod; + OBJECTREF ctorDeclaringType; OBJECTREF ctorResult; } gc; gc.ctorArgs = NULL; - gc.ctorArgIsValueTypeFlags = NULL; - gc.ctorTarget = NULL; + gc.ctorMethod = NULL; + gc.ctorDeclaringType = NULL; gc.ctorResult = NULL; GCPROTECT_BEGIN(gc); gc.ctorArgs = (PTRARRAYREF)AllocateObjectArray(cArgs, g_pObjectClass); - gc.ctorArgIsValueTypeFlags = (BOOLARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_BOOLEAN, cArgs); - gc.ctorTarget = th.GetMethodTable()->Allocate(); - - CLR_BOOL* argIsValueTypeFlags = reinterpret_cast(static_cast(gc.ctorArgIsValueTypeFlags->GetDataPtr())); + gc.ctorMethod = pMethod.Get(); + gc.ctorDeclaringType = pCaType.Get(); if (pBlob) { @@ -1092,8 +969,6 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( if (argTypeForParse.IsArray()) argTypeForParse = argTypeForParse.GetArrayElementTypeHandle(); - argIsValueTypeFlags[i] = CLR_BOOL_ARG(paramType.IsValueType()); - ARG_SLOT data = GetDataFromBlob( pCtorMD->GetAssembly(), (CorSerializationType)type, @@ -1149,38 +1024,18 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( if (*pcNamedArgs == 0 && pBlob != pEndBlob) COMPlusThrow(kCustomAttributeFormatException); - bool needsCtorInvokeStub = (cArgs > 6); - if (!needsCtorInvokeStub && cArgs > 1) - { - for (UINT argIndex = 0; argIndex < cArgs; argIndex++) - { - if (argIsValueTypeFlags[argIndex] != 0) - { - needsCtorInvokeStub = true; - break; - } - } - } - - PCODE ctorEntryPoint = pCtorMD->GetSingleCallableAddrOfCode(); - PCODE ctorInvokeStubEntryPoint = needsCtorInvokeStub ? GetOrCreateCustomAttributeCtorInvokeStub(pCtorMD, th) : (PCODE)NULL; - struct NativeCtorInvokeContract { PTRARRAYREF* ctorArgs; - BOOLARRAYREF* argIsValueType; INT32 argCount; - OBJECTREF* ctorTarget; - PCODE ctorEntryPoint; - PCODE ctorInvokeStubEntryPoint; + OBJECTREF* ctorMethod; + OBJECTREF* ctorDeclaringType; } contract; contract.ctorArgs = &gc.ctorArgs; - contract.argIsValueType = &gc.ctorArgIsValueTypeFlags; contract.argCount = (INT32)cArgs; - contract.ctorTarget = &gc.ctorTarget; - contract.ctorEntryPoint = ctorEntryPoint; - contract.ctorInvokeStubEntryPoint = ctorInvokeStubEntryPoint; + contract.ctorMethod = &gc.ctorMethod; + contract.ctorDeclaringType = &gc.ctorDeclaringType; UnmanagedCallersOnlyCaller invokeCustomAttributeCtor(METHOD__CUSTOMATTRIBUTE__INVOKE_CUSTOM_ATTRIBUTE_CTOR); invokeCustomAttributeCtor.InvokeThrowing(&contract, &gc.ctorResult); From 954af8a72db9571a18260d8a6c6571da3ff3497f Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Sat, 11 Apr 2026 14:59:38 +0000 Subject: [PATCH 07/16] Remove CallDescrWorkerWith from InvokeMethod path --- .../Reflection/ConstructorInvoker.CoreCLR.cs | 28 +- .../Reflection/MethodBaseInvoker.CoreCLR.cs | 52 +- .../Reflection/MethodInvoker.CoreCLR.cs | 53 +- .../RuntimeConstructorInfo.CoreCLR.cs | 2 +- .../Reflection/RuntimeCustomAttributeData.cs | 46 +- .../src/System/RuntimeHandles.cs | 21 - src/coreclr/vm/callhelpers.cpp | 62 -- src/coreclr/vm/callhelpers.h | 1 - src/coreclr/vm/qcallentrypoints.cpp | 1 - src/coreclr/vm/reflectioninvocation.cpp | 558 ------------------ src/coreclr/vm/runtimehandles.h | 7 - .../System/Reflection/ConstructorInvoker.cs | 30 +- .../src/System/Reflection/InvokerEmitUtil.cs | 126 +++- .../System/Reflection/MethodBaseInvoker.cs | 42 +- .../src/System/Reflection/MethodInvoker.cs | 52 +- .../System/Reflection/MethodInvokerCommon.cs | 73 ++- 16 files changed, 425 insertions(+), 729 deletions(-) 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..d6cf2ae96bbfb7 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,40 @@ // 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. + if (!IsCollectible(_method)) + { + _invokeFunc_RefArgs = emitDelegate; + } + + return emitDelegate(obj, args); + } + + private static bool IsCollectible(MethodBase method) + { + Type? declaringType = method.DeclaringType; + return declaringType is not null && declaringType.Assembly.IsCollectible; } } } 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..416d0b3f7477c4 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,69 @@ // 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; } 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 = CreateEmitDelegate(_method); + + // Don't cache for collectible assemblies: the DynamicMethod holds token + // references to types that would prevent the assembly from being unloaded. + if (!IsCollectible(_method)) + { + _invokeFunc_RefArgs = emitDelegate; + } + + return emitDelegate(obj, args); + } + + private unsafe object? InterpretedInvoke_Constructor(object? obj, IntPtr* args) + { + InvokeFunc_RefArgs emitDelegate = CreateEmitDelegate(_method); + + if (!IsCollectible(_method)) + { + _invokeFunc_RefArgs = emitDelegate; + } - [RequiresUnsafe] - private unsafe object? InterpretedInvoke_Method(object? obj, IntPtr* args) => - RuntimeMethodHandle.InvokeMethod(obj, (void**)args, _signature!, isConstructor: false); + return emitDelegate(obj, args); + } + + private static bool IsCollectible(MethodBase method) + { + Type? declaringType = method.DeclaringType; + return declaringType is not null && declaringType.Assembly.IsCollectible; + } + + private static InvokeFunc_RefArgs CreateEmitDelegate(MethodBase method) + { + using (AssemblyBuilder.ForceAllowDynamicCode()) + { + return CreateInvokeDelegate_RefArgs(method); + } + } } } 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..b3ad9c11655f66 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,67 @@ // 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_Constructor; } - [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 = CreateEmitDelegate(_method); + + if (!IsCollectible(_method)) + { + _invokeFunc_RefArgs = emitDelegate; + } - [RequiresUnsafe] - private unsafe object? InterpretedInvoke_Constructor(object? obj, IntPtr* args) => - RuntimeMethodHandle.InvokeMethod(obj, (void**)args, _signature!, isConstructor: obj is null); + return emitDelegate(obj, args); + } + + private unsafe object? InterpretedInvoke_Constructor(object? obj, IntPtr* args) + { + InvokeFunc_RefArgs emitDelegate = CreateEmitDelegate(_method); + + if (!IsCollectible(_method)) + { + _invokeFunc_RefArgs = emitDelegate; + } + + return emitDelegate(obj, args); + } + + private static bool IsCollectible(MethodBase method) + { + Type? declaringType = method.DeclaringType; + return declaringType is not null && declaringType.Assembly.IsCollectible; + } + + private static InvokeFunc_RefArgs CreateEmitDelegate(MethodBase method) + { + using (AssemblyBuilder.ForceAllowDynamicCode()) + { + return CreateInvokeDelegate_RefArgs(method); + } + } } } 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 d64b253e4b426e..b7080c6546d69a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs @@ -1867,7 +1867,6 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p { object?[]? parameters = *pContract->CtorArgs; object? ctorObject = *pContract->CtorMethod; - object? ctorDeclaringTypeObject = *pContract->CtorDeclaringType; int argCount = pContract->ArgCount; if (argCount < 0 || parameters is null || parameters.Length != argCount) @@ -1875,23 +1874,48 @@ private static unsafe void InvokeCustomAttributeCtor(NativeCtorInvokeContract* p throw new TargetParameterCountException(SR.Arg_ParmCnt); } - if (ctorObject is not System.IRuntimeMethodInfo methodInfo) + RuntimeConstructorInfo? ctor = ctorObject as RuntimeConstructorInfo; + if (ctor is null) { - throw new InvalidOperationException("Invalid custom attribute constructor."); - } + if (ctorObject is not System.IRuntimeMethodInfo methodInfo || + RuntimeType.GetMethodBase(methodInfo) is not RuntimeConstructorInfo resolvedCtor) + { + throw new InvalidOperationException("Invalid custom attribute constructor."); + } - if (ctorDeclaringTypeObject is not RuntimeType ctorDeclaringType) - { - throw new InvalidOperationException("Invalid custom attribute constructor."); + ctor = resolvedCtor; } - RuntimeMethodHandle methodHandle = new(methodInfo); - if (MethodBase.GetMethodFromHandle(methodHandle, ctorDeclaringType.TypeHandle) is not RuntimeConstructorInfo ctor) + ReadOnlySpan ctorParameters = default; + bool needCtorParameters = false; + + for (int i = 0; i < argCount; i++) { - throw new InvalidOperationException("Invalid custom attribute constructor."); + 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 = new(ctor); + MethodBaseInvoker invoker = ctor.Invoker; object? result = argCount switch { 0 => invoker.InvokeWithNoArgs(obj: null, BindingFlags.DoNotWrapExceptions), 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..fd060e6c7aa9f9 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) diff --git a/src/coreclr/vm/callhelpers.h b/src/coreclr/vm/callhelpers.h index d853823023a073..e53bba3e017d53 100644 --- a/src/coreclr/vm/callhelpers.h +++ b/src/coreclr/vm/callhelpers.h @@ -11,7 +11,6 @@ struct CallDescrData { - // Input arguments LPVOID pSrc; UINT32 numStackSlots; #ifdef CALLDESCR_ARGREGS 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..e0ffaa0f377c13 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -148,564 +148,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/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/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs index 63a22060a16696..5676b10c6d2b1a 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) { - DetermineStrategy_Obj4Args(ref _strategy, ref _invokeFunc_Obj4Args, _method, _needsByRefStrategy, backwardsCompat: false); + DetermineStrategy_Obj4Args(ref _strategy, ref _invokeFunc_Obj4Args, _method, _needsByRefStrategy +#if MONO + , backwardsCompat: false +#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) { - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: false); + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy +#if MONO + , backwardsCompat: false +#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) { - DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: false); + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method +#if MONO + , backwardsCompat: false +#endif + ); } StackAllocatedByRefs byrefs = default; @@ -310,7 +322,11 @@ internal unsafe object InvokeWithManyArgs(Span arguments) if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: false); + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy +#if MONO + , backwardsCompat: false +#endif + ); } if (_invokeFunc_ObjSpanArgs is not null) @@ -343,7 +359,11 @@ internal unsafe object InvokeWithManyArgs(Span arguments) { if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { - DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: false); + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method +#if MONO + , backwardsCompat: false +#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..a321ebee3a7099 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); - public static InvokeFunc_Obj4Args CreateInvokeDelegate_Obj4Args(MethodBase method, bool backwardsCompat) + public static InvokeFunc_Obj4Args CreateInvokeDelegate_Obj4Args(MethodBase method + #if MONO + , bool backwardsCompat + #endif + ) { Debug.Assert(!method.ContainsGenericParameters); @@ -77,13 +81,21 @@ public static InvokeFunc_Obj4Args CreateInvokeDelegate_Obj4Args(MethodBase metho } } - EmitCallAndReturnHandling(il, method, emitNew, backwardsCompat); + EmitCallAndReturnHandling(il, method, emitNew +#if MONO + , backwardsCompat +#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); } - public static InvokeFunc_ObjSpanArgs CreateInvokeDelegate_ObjSpanArgs(MethodBase method, bool backwardsCompat) + public static InvokeFunc_ObjSpanArgs CreateInvokeDelegate_ObjSpanArgs(MethodBase method + #if MONO + , bool backwardsCompat + #endif + ) { Debug.Assert(!method.ContainsGenericParameters); @@ -134,13 +146,21 @@ public static InvokeFunc_ObjSpanArgs CreateInvokeDelegate_ObjSpanArgs(MethodBase } } - EmitCallAndReturnHandling(il, method, emitNew, backwardsCompat); + EmitCallAndReturnHandling(il, method, emitNew +#if MONO + , backwardsCompat +#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); } - public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method, bool backwardsCompat) + public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method + #if MONO + , bool backwardsCompat + #endif + ) { Debug.Assert(!method.ContainsGenericParameters); @@ -170,6 +190,83 @@ public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method, } } + 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 +#if MONO + , backwardsCompat +#endif + ); + + 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); + } + // Push the arguments. ReadOnlySpan parameters = method.GetParametersAsSpan(); for (int i = 0; i < parameters.Length; i++) @@ -190,7 +287,11 @@ public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method, } } - EmitCallAndReturnHandling(il, method, emitNew, backwardsCompat); + EmitCallAndReturnHandling(il, method, emitNew +#if MONO + , backwardsCompat +#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 +306,26 @@ private static void Unbox(ILGenerator il, Type parameterType) il.Emit(OpCodes.Ldobj, parameterType); } - private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method, bool emitNew, bool backwardsCompat) + private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method, bool emitNew +#if MONO + , bool backwardsCompat +#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.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.cs index dd46f83e78dc87..d5bfbbefb7d674 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) { - DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: true); + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method +#if MONO + , backwardsCompat: true +#endif + ); } try @@ -83,7 +87,11 @@ internal static void ThrowTargetParameterCountException() object? ret; if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: true); + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy +#if MONO + , backwardsCompat: true +#endif + ); } CheckArguments(parametersSpan, copyOfArgs, shouldCopyBack, binder, culture, invokeAttr); @@ -124,7 +132,11 @@ internal static void ThrowTargetParameterCountException() object? ret; if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: true); + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy +#if MONO + , backwardsCompat: true +#endif + ); } CheckArguments(parameters, copyOfArgs, shouldCopyBack, binder, culture, invokeAttr); @@ -156,7 +168,11 @@ internal static void ThrowTargetParameterCountException() if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { - DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: true); + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method +#if MONO + , backwardsCompat: true +#endif + ); } StackAllocatedByRefs byrefs = default; @@ -195,7 +211,11 @@ internal static void ThrowTargetParameterCountException() if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: true); + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy +#if MONO + , backwardsCompat: true +#endif + ); } if (_invokeFunc_ObjSpanArgs is not null) @@ -232,7 +252,11 @@ internal static void ThrowTargetParameterCountException() { if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { - DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: true); + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method +#if MONO + , backwardsCompat: true +#endif + ); } IntPtr* pStorage = stackalloc IntPtr[3 * _argCount]; @@ -312,7 +336,11 @@ internal void InvokePropertySetter( if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { // Initialize for next time. - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: true); + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy +#if MONO + , backwardsCompat: true +#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..29579b47747389 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) { - DetermineStrategy_Obj4Args(ref _strategy, ref _invokeFunc_Obj4Args, _method, _needsByRefStrategy, backwardsCompat: false); + DetermineStrategy_Obj4Args(ref _strategy, ref _invokeFunc_Obj4Args, _method, _needsByRefStrategy +#if MONO + , backwardsCompat: false +#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) { - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: false); + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy +#if MONO + , backwardsCompat: false +#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) { - DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: false); + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method +#if MONO + , backwardsCompat: false +#endif + ); } StackAllocatedByRefs byrefs = default; @@ -363,7 +397,11 @@ private void ThrowForBadInvocationFlags() if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy, backwardsCompat: false); + DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy +#if MONO + , backwardsCompat: false +#endif + ); } if (_invokeFunc_ObjSpanArgs is not null) @@ -396,7 +434,11 @@ private void ThrowForBadInvocationFlags() { if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { - DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: false); + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method +#if MONO + , backwardsCompat: false +#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..e83c0af84c2f58 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,11 @@ internal static void DetermineStrategy_ObjSpanArgs( ref InvokeFunc_ObjSpanArgs? invokeFunc_ObjSpanArgs, MethodBase method, - bool needsByRefStrategy, - bool backwardsCompat) + bool needsByRefStrategy +#if MONO + , bool backwardsCompat +#endif + ) { if (needsByRefStrategy) { @@ -123,7 +126,11 @@ ref InvokeFunc_ObjSpanArgs? { if (RuntimeFeature.IsDynamicCodeSupported) { - invokeFunc_ObjSpanArgs = CreateInvokeDelegate_ObjSpanArgs(method, backwardsCompat); + invokeFunc_ObjSpanArgs = CreateInvokeDelegate_ObjSpanArgs(method +#if MONO + , backwardsCompat +#endif + ); } strategy |= InvokerStrategy.StrategyDetermined_ObjSpanArgs; @@ -134,8 +141,11 @@ internal static void DetermineStrategy_Obj4Args( ref InvokerStrategy strategy, ref InvokeFunc_Obj4Args? invokeFunc_Obj4Args, MethodBase method, - bool needsByRefStrategy, - bool backwardsCompat) + bool needsByRefStrategy +#if MONO + , bool backwardsCompat +#endif + ) { if (needsByRefStrategy) { @@ -152,7 +162,11 @@ internal static void DetermineStrategy_Obj4Args( { if (RuntimeFeature.IsDynamicCodeSupported) { - invokeFunc_Obj4Args = CreateInvokeDelegate_Obj4Args(method, backwardsCompat); + invokeFunc_Obj4Args = CreateInvokeDelegate_Obj4Args(method +#if MONO + , backwardsCompat +#endif + ); } strategy |= InvokerStrategy.StrategyDetermined_Obj4Args; @@ -162,9 +176,45 @@ internal static void DetermineStrategy_Obj4Args( internal static void DetermineStrategy_RefArgs( ref InvokerStrategy strategy, ref InvokeFunc_RefArgs? invokeFunc_RefArgs, - MethodBase method, - bool backwardsCompat) + MethodBase method +#if MONO + , bool backwardsCompat +#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 +225,16 @@ internal static void DetermineStrategy_RefArgs( { if (RuntimeFeature.IsDynamicCodeSupported) { - invokeFunc_RefArgs = CreateInvokeDelegate_RefArgs(method, backwardsCompat); + invokeFunc_RefArgs = CreateInvokeDelegate_RefArgs(method +#if MONO + , backwardsCompat +#endif + ); } strategy |= InvokerStrategy.StrategyDetermined_RefArgs; } +#endif } } } From aa0da9dbc179e749ece9086afb632760c00761e9 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Sat, 11 Apr 2026 15:53:20 +0000 Subject: [PATCH 08/16] Fix mono --- .../System/Reflection/ConstructorInvoker.cs | 30 +++++----- .../src/System/Reflection/InvokerEmitUtil.cs | 60 +++++++++---------- .../System/Reflection/MethodBaseInvoker.cs | 42 ++++++------- .../src/System/Reflection/MethodInvoker.cs | 30 +++++----- .../System/Reflection/MethodInvokerCommon.cs | 39 ++++++------ 5 files changed, 102 insertions(+), 99 deletions(-) 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 5676b10c6d2b1a..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,11 +184,11 @@ private object InvokeImpl(object? arg1, object? arg2, object? arg3, object? arg4 if ((_strategy & InvokerStrategy.StrategyDetermined_Obj4Args) == 0) { - DetermineStrategy_Obj4Args(ref _strategy, ref _invokeFunc_Obj4Args, _method, _needsByRefStrategy #if MONO - , backwardsCompat: false + 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)!; @@ -268,11 +268,11 @@ internal object InvokeWithFewArgs(Span arguments) if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy #if MONO - , backwardsCompat: false + 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)!; @@ -294,11 +294,11 @@ internal unsafe object InvokeDirectByRefWithFewArgs(Span copyOfArgs) { if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { - DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method #if MONO - , backwardsCompat: false + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: false); +#else + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method); #endif - ); } StackAllocatedByRefs byrefs = default; @@ -322,11 +322,11 @@ internal unsafe object InvokeWithManyArgs(Span arguments) if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy #if MONO - , backwardsCompat: false + 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) @@ -359,11 +359,11 @@ internal unsafe object InvokeWithManyArgs(Span arguments) { if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { - DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method #if MONO - , backwardsCompat: false + 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 a321ebee3a7099..1ac7a52f1f1775 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs @@ -16,11 +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); - public static InvokeFunc_Obj4Args CreateInvokeDelegate_Obj4Args(MethodBase method - #if MONO - , bool backwardsCompat - #endif - ) +#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); @@ -81,21 +81,21 @@ public static InvokeFunc_Obj4Args CreateInvokeDelegate_Obj4Args(MethodBase metho } } - EmitCallAndReturnHandling(il, method, emitNew #if MONO - , backwardsCompat + 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); } - public static InvokeFunc_ObjSpanArgs CreateInvokeDelegate_ObjSpanArgs(MethodBase method - #if MONO - , bool backwardsCompat - #endif - ) +#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); @@ -146,21 +146,21 @@ public static InvokeFunc_ObjSpanArgs CreateInvokeDelegate_ObjSpanArgs(MethodBase } } - EmitCallAndReturnHandling(il, method, emitNew #if MONO - , backwardsCompat + 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); } - public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method - #if MONO - , bool backwardsCompat - #endif - ) +#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); @@ -254,11 +254,11 @@ public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method } } - EmitCallAndReturnHandling(il, method, emitNew #if MONO - , backwardsCompat + EmitCallAndReturnHandling(il, method, emitNew, backwardsCompat); +#else + EmitCallAndReturnHandling(il, method, emitNew); #endif - ); il.MarkLabel(done); il.Emit(OpCodes.Ret); @@ -287,11 +287,11 @@ public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method } } - EmitCallAndReturnHandling(il, method, emitNew #if MONO - , backwardsCompat + 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); @@ -306,11 +306,11 @@ private static void Unbox(ILGenerator il, Type parameterType) il.Emit(OpCodes.Ldobj, parameterType); } - private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method, bool emitNew #if MONO - , bool backwardsCompat + 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. 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 d5bfbbefb7d674..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,11 +49,11 @@ internal static void ThrowTargetParameterCountException() if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { - DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method #if MONO - , backwardsCompat: true + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: true); +#else + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method); #endif - ); } try @@ -87,11 +87,11 @@ internal static void ThrowTargetParameterCountException() object? ret; if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy #if MONO - , backwardsCompat: true + 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); @@ -132,11 +132,11 @@ internal static void ThrowTargetParameterCountException() object? ret; if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy #if MONO - , backwardsCompat: true + 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); @@ -168,11 +168,11 @@ internal static void ThrowTargetParameterCountException() if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { - DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method #if MONO - , backwardsCompat: true + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: true); +#else + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method); #endif - ); } StackAllocatedByRefs byrefs = default; @@ -211,11 +211,11 @@ internal static void ThrowTargetParameterCountException() if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy #if MONO - , backwardsCompat: true + 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) @@ -252,11 +252,11 @@ internal static void ThrowTargetParameterCountException() { if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { - DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method #if MONO - , backwardsCompat: true + 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]; @@ -336,11 +336,11 @@ internal void InvokePropertySetter( if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { // Initialize for next time. - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy #if MONO - , backwardsCompat: true + 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 29579b47747389..33aebbd33e4821 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs @@ -234,11 +234,11 @@ private MethodInvoker(MethodBase method, RuntimeType[] argumentTypes) if ((_strategy & InvokerStrategy.StrategyDetermined_Obj4Args) == 0) { - DetermineStrategy_Obj4Args(ref _strategy, ref _invokeFunc_Obj4Args, _method, _needsByRefStrategy #if MONO - , backwardsCompat: false + 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); @@ -343,11 +343,11 @@ private void ThrowForBadInvocationFlags() if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy #if MONO - , backwardsCompat: false + 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); @@ -369,11 +369,11 @@ private void ThrowForBadInvocationFlags() { if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { - DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method #if MONO - , backwardsCompat: false + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method, backwardsCompat: false); +#else + DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method); #endif - ); } StackAllocatedByRefs byrefs = default; @@ -397,11 +397,11 @@ private void ThrowForBadInvocationFlags() if ((_strategy & InvokerStrategy.StrategyDetermined_ObjSpanArgs) == 0) { - DetermineStrategy_ObjSpanArgs(ref _strategy, ref _invokeFunc_ObjSpanArgs, _method, _needsByRefStrategy #if MONO - , backwardsCompat: false + 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) @@ -434,11 +434,11 @@ private void ThrowForBadInvocationFlags() { if ((_strategy & InvokerStrategy.StrategyDetermined_RefArgs) == 0) { - DetermineStrategy_RefArgs(ref _strategy, ref _invokeFunc_RefArgs, _method #if MONO - , backwardsCompat: false + 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 e83c0af84c2f58..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,11 +105,12 @@ internal static void DetermineStrategy_ObjSpanArgs( ref InvokeFunc_ObjSpanArgs? invokeFunc_ObjSpanArgs, MethodBase method, - bool needsByRefStrategy #if MONO - , bool backwardsCompat + bool needsByRefStrategy, + bool backwardsCompat) +#else + bool needsByRefStrategy) #endif - ) { if (needsByRefStrategy) { @@ -126,11 +127,11 @@ bool needsByRefStrategy { if (RuntimeFeature.IsDynamicCodeSupported) { - invokeFunc_ObjSpanArgs = CreateInvokeDelegate_ObjSpanArgs(method #if MONO - , backwardsCompat + invokeFunc_ObjSpanArgs = CreateInvokeDelegate_ObjSpanArgs(method, backwardsCompat); +#else + invokeFunc_ObjSpanArgs = CreateInvokeDelegate_ObjSpanArgs(method); #endif - ); } strategy |= InvokerStrategy.StrategyDetermined_ObjSpanArgs; @@ -141,11 +142,12 @@ internal static void DetermineStrategy_Obj4Args( ref InvokerStrategy strategy, ref InvokeFunc_Obj4Args? invokeFunc_Obj4Args, MethodBase method, - bool needsByRefStrategy #if MONO - , bool backwardsCompat + bool needsByRefStrategy, + bool backwardsCompat) +#else + bool needsByRefStrategy) #endif - ) { if (needsByRefStrategy) { @@ -162,11 +164,11 @@ bool needsByRefStrategy { if (RuntimeFeature.IsDynamicCodeSupported) { - invokeFunc_Obj4Args = CreateInvokeDelegate_Obj4Args(method #if MONO - , backwardsCompat + invokeFunc_Obj4Args = CreateInvokeDelegate_Obj4Args(method, backwardsCompat); +#else + invokeFunc_Obj4Args = CreateInvokeDelegate_Obj4Args(method); #endif - ); } strategy |= InvokerStrategy.StrategyDetermined_Obj4Args; @@ -176,11 +178,12 @@ bool needsByRefStrategy internal static void DetermineStrategy_RefArgs( ref InvokerStrategy strategy, ref InvokeFunc_RefArgs? invokeFunc_RefArgs, - MethodBase method #if MONO - , bool backwardsCompat + MethodBase method, + bool backwardsCompat) +#else + MethodBase method) #endif - ) { #if !MONO if (((strategy & InvokerStrategy.HasBeenInvoked_RefArgs) == 0) && !Debugger.IsAttached) @@ -225,11 +228,11 @@ MethodBase method { if (RuntimeFeature.IsDynamicCodeSupported) { - invokeFunc_RefArgs = CreateInvokeDelegate_RefArgs(method #if MONO - , backwardsCompat + invokeFunc_RefArgs = CreateInvokeDelegate_RefArgs(method, backwardsCompat); +#else + invokeFunc_RefArgs = CreateInvokeDelegate_RefArgs(method); #endif - ); } strategy |= InvokerStrategy.StrategyDetermined_RefArgs; From a981f2d0848520de120e4bdbc5d883eadc574eec Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Sat, 11 Apr 2026 20:18:31 +0300 Subject: [PATCH 09/16] Update callhelpers-reverse for wasm --- src/coreclr/vm/wasm/callhelpers-reverse.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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 } }, From da967a96d4828f714e377213c196fbc9b64e7111 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Sat, 11 Apr 2026 22:02:21 +0300 Subject: [PATCH 10/16] Fix mono condition --- .../src/System/Reflection/InvokerEmitUtil.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 1ac7a52f1f1775..916317d7f95bb9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs @@ -190,6 +190,7 @@ public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method) } } +#if !MONO if (emitNew) { Debug.Assert(method is RuntimeConstructorInfo); @@ -254,11 +255,7 @@ public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method) } } -#if MONO - EmitCallAndReturnHandling(il, method, emitNew, backwardsCompat); -#else EmitCallAndReturnHandling(il, method, emitNew); -#endif il.MarkLabel(done); il.Emit(OpCodes.Ret); @@ -266,6 +263,7 @@ public static InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase method) // 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(); From 53679e0cc5da9273b7afd33cc33d98a2bd233949 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Sat, 11 Apr 2026 23:54:49 +0300 Subject: [PATCH 11/16] Skip arg slot fixup for ref-likes --- src/coreclr/vm/customattribute.cpp | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/coreclr/vm/customattribute.cpp b/src/coreclr/vm/customattribute.cpp index 70723a672f27a3..e15bcfa2194042 100644 --- a/src/coreclr/vm/customattribute.cpp +++ b/src/coreclr/vm/customattribute.cpp @@ -985,12 +985,26 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( } else { - MethodTable* pMTValue = (type == ELEMENT_TYPE_VALUETYPE || type == ELEMENT_TYPE_CLASS) - ? argTypeForParse.GetMethodTable() - : CoreLibBinder::GetElementType(type); - - _ASSERTE(pMTValue != NULL); - argObj = pMTValue->Box((void*)ArgSlotEndiannessFixup(&data, pMTValue->GetNumInstanceFieldBytes())); + 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 + { + 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); From a575025db91c4c42d2ff518defda52fed22d2b69 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Sun, 12 Apr 2026 12:14:45 +0300 Subject: [PATCH 12/16] Dedups --- .../Reflection/ConstructorInvoker.CoreCLR.cs | 9 ++--- .../Reflection/MethodBaseInvoker.CoreCLR.cs | 36 +++++-------------- .../Reflection/MethodInvoker.CoreCLR.cs | 32 ++++------------- .../MethodBaseInvoker.Constructor.cs | 4 +-- 4 files changed, 18 insertions(+), 63 deletions(-) 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 d6cf2ae96bbfb7..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 @@ -23,18 +23,13 @@ internal unsafe ConstructorInvoker(RuntimeConstructorInfo constructor) : this(co // Don't cache for collectible assemblies: the DynamicMethod holds token // references to types that would prevent the assembly from being unloaded. - if (!IsCollectible(_method)) + Type? declaringType = _method.DeclaringType; + if (declaringType is null || !declaringType.Assembly.IsCollectible) { _invokeFunc_RefArgs = emitDelegate; } return emitDelegate(obj, args); } - - private static bool IsCollectible(MethodBase method) - { - Type? declaringType = method.DeclaringType; - return declaringType is not null && declaringType.Assembly.IsCollectible; - } } } 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 416d0b3f7477c4..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 @@ -18,7 +18,7 @@ internal unsafe MethodBaseInvoker(RuntimeMethodInfo method) : this(method, metho internal unsafe MethodBaseInvoker(RuntimeConstructorInfo constructor) : this(constructor, constructor.Signature.Arguments) { _invocationFlags = constructor.ComputeAndUpdateInvocationFlags(); - _invokeFunc_RefArgs = InterpretedInvoke_Constructor; + _invokeFunc_RefArgs = InterpretedInvoke_Method; } internal unsafe MethodBaseInvoker(DynamicMethod method, Signature signature) : this(method, signature.Arguments) @@ -28,23 +28,16 @@ internal unsafe MethodBaseInvoker(DynamicMethod method, Signature signature) : t private unsafe object? InterpretedInvoke_Method(object? obj, IntPtr* args) { - InvokeFunc_RefArgs emitDelegate = CreateEmitDelegate(_method); - - // Don't cache for collectible assemblies: the DynamicMethod holds token - // references to types that would prevent the assembly from being unloaded. - if (!IsCollectible(_method)) + InvokeFunc_RefArgs emitDelegate; + using (AssemblyBuilder.ForceAllowDynamicCode()) { - _invokeFunc_RefArgs = emitDelegate; + emitDelegate = CreateInvokeDelegate_RefArgs(_method); } - return emitDelegate(obj, args); - } - - private unsafe object? InterpretedInvoke_Constructor(object? obj, IntPtr* args) - { - InvokeFunc_RefArgs emitDelegate = CreateEmitDelegate(_method); - - if (!IsCollectible(_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; } @@ -52,18 +45,5 @@ internal unsafe MethodBaseInvoker(DynamicMethod method, Signature signature) : t return emitDelegate(obj, args); } - private static bool IsCollectible(MethodBase method) - { - Type? declaringType = method.DeclaringType; - return declaringType is not null && declaringType.Assembly.IsCollectible; - } - - private static InvokeFunc_RefArgs CreateEmitDelegate(MethodBase method) - { - using (AssemblyBuilder.ForceAllowDynamicCode()) - { - return CreateInvokeDelegate_RefArgs(method); - } - } } } 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 b3ad9c11655f66..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 @@ -23,26 +23,19 @@ private unsafe MethodInvoker(DynamicMethod method) : this(method, method.Signatu private unsafe MethodInvoker(RuntimeConstructorInfo constructor) : this(constructor, constructor.Signature.Arguments) { _invocationFlags = constructor.ComputeAndUpdateInvocationFlags(); - _invokeFunc_RefArgs = InterpretedInvoke_Constructor; + _invokeFunc_RefArgs = InterpretedInvoke_Method; } private unsafe object? InterpretedInvoke_Method(object? obj, IntPtr* args) { - InvokeFunc_RefArgs emitDelegate = CreateEmitDelegate(_method); - - if (!IsCollectible(_method)) + InvokeFunc_RefArgs emitDelegate; + using (AssemblyBuilder.ForceAllowDynamicCode()) { - _invokeFunc_RefArgs = emitDelegate; + emitDelegate = CreateInvokeDelegate_RefArgs(_method); } - return emitDelegate(obj, args); - } - - private unsafe object? InterpretedInvoke_Constructor(object? obj, IntPtr* args) - { - InvokeFunc_RefArgs emitDelegate = CreateEmitDelegate(_method); - - if (!IsCollectible(_method)) + Type? declaringType = _method.DeclaringType; + if (declaringType is null || !declaringType.Assembly.IsCollectible) { _invokeFunc_RefArgs = emitDelegate; } @@ -50,18 +43,5 @@ private unsafe MethodInvoker(RuntimeConstructorInfo constructor) : this(construc return emitDelegate(obj, args); } - private static bool IsCollectible(MethodBase method) - { - Type? declaringType = method.DeclaringType; - return declaringType is not null && declaringType.Assembly.IsCollectible; - } - - private static InvokeFunc_RefArgs CreateEmitDelegate(MethodBase method) - { - using (AssemblyBuilder.ForceAllowDynamicCode()) - { - return CreateInvokeDelegate_RefArgs(method); - } - } } } 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..9ea2b51d30de22 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,7 @@ internal sealed partial class MethodBaseInvoker try { // Use the interpreted version to avoid having to generate a new method that doesn't allocate. - ret = InterpretedInvoke_Constructor(obj, pByRefStorage); + ret = InterpretedInvoke_Method(obj, pByRefStorage); } catch (Exception e) when (wrapInTargetInvocationException) { @@ -70,7 +70,7 @@ internal sealed partial class MethodBaseInvoker try { // Use the interpreted version to avoid having to generate a new method that doesn't allocate. - return InterpretedInvoke_Constructor(obj, null); + return InterpretedInvoke_Method(obj, null); } catch (Exception e) when (wrapInTargetInvocationException) { From 05af3bd8e8f9d9adfb51f015300e24f8ebfe7415 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Sun, 12 Apr 2026 13:25:19 +0300 Subject: [PATCH 13/16] Exclude mono --- .../System/Reflection/MethodBaseInvoker.Constructor.cs | 8 ++++++++ 1 file changed, 8 insertions(+) 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 9ea2b51d30de22..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) { From 0eb188941cb589c6c6cd515e06686c9dc0c6df01 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Sun, 12 Apr 2026 13:31:44 +0300 Subject: [PATCH 14/16] Restore comment --- src/coreclr/vm/callhelpers.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/vm/callhelpers.h b/src/coreclr/vm/callhelpers.h index e53bba3e017d53..d853823023a073 100644 --- a/src/coreclr/vm/callhelpers.h +++ b/src/coreclr/vm/callhelpers.h @@ -11,6 +11,7 @@ struct CallDescrData { + // Input arguments LPVOID pSrc; UINT32 numStackSlots; #ifdef CALLDESCR_ARGREGS From 2378b88a56ba1e0c0c984cffc465c7e2b7c5adce Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Sun, 12 Apr 2026 13:33:28 +0300 Subject: [PATCH 15/16] Cleanups --- src/coreclr/vm/callhelpers.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/coreclr/vm/callhelpers.h b/src/coreclr/vm/callhelpers.h index d853823023a073..b5cd60a2791f82 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,10 +488,6 @@ enum EEToManagedCallFlags #define END_CALL_TO_MANAGED() \ } -/***********************************************************************/ -/* Macros that provide abstraction to the usage of DispatchCallSimple */ -/***********************************************************************/ - #define ARGHOLDER_TYPE LPVOID void CallDefaultConstructor(OBJECTREF ref); From 6dd7e5d6aec258834c05f22ed8bb6cff7e07cdd6 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Sun, 12 Apr 2026 11:00:23 +0000 Subject: [PATCH 16/16] More cleanups --- src/coreclr/vm/callhelpers.cpp | 31 ----------------------- src/coreclr/vm/callhelpers.h | 2 -- src/coreclr/vm/clrex.cpp | 6 ++++- src/coreclr/vm/reflectioninvocation.cpp | 6 ++++- src/coreclr/vm/runtimecallablewrapper.cpp | 8 +++++- 5 files changed, 17 insertions(+), 36 deletions(-) diff --git a/src/coreclr/vm/callhelpers.cpp b/src/coreclr/vm/callhelpers.cpp index fd060e6c7aa9f9..3934d6dbc0f20e 100644 --- a/src/coreclr/vm/callhelpers.cpp +++ b/src/coreclr/vm/callhelpers.cpp @@ -474,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 b5cd60a2791f82..a6cee69b008113 100644 --- a/src/coreclr/vm/callhelpers.h +++ b/src/coreclr/vm/callhelpers.h @@ -490,8 +490,6 @@ enum EEToManagedCallFlags #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/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index e0ffaa0f377c13..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); 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);