-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make delegates immutable #99200
base: main
Are you sure you want to change the base?
Make delegates immutable #99200
Changes from all commits
5b3126e
64baa1d
a489288
b0b31bd
bc132e4
2dce6a8
00fcd02
7941123
4de2754
b9f7f14
90c6085
9509023
557bf4d
1eab602
477a3c5
fff595a
2c16705
cbe22f7
bb2a509
3bb047b
b957392
f3e48e1
35e4404
8701078
e22a731
63cba0a
10964cd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1204,6 +1204,8 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, | |
|
||
pTargetMethod->EnsureActive(); | ||
|
||
refRealDelegate->SetMethodDesc(pTargetMethod); | ||
|
||
if (fIsOpenDelegate) | ||
{ | ||
_ASSERTE(pRefFirstArg == NULL || *pRefFirstArg == NULL); | ||
|
@@ -1291,8 +1293,9 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, | |
|
||
LoaderAllocator *pLoaderAllocator = pTargetMethod->GetLoaderAllocator(); | ||
|
||
_ASSERTE(refRealDelegate->GetInvocationList() == NULL); | ||
if (pLoaderAllocator->IsCollectible()) | ||
refRealDelegate->SetMethodBase(pLoaderAllocator->GetExposedObject()); | ||
refRealDelegate->SetInvocationList(pLoaderAllocator->GetExposedObject()); | ||
|
||
GCPROTECT_END(); | ||
} | ||
|
@@ -1505,6 +1508,8 @@ OBJECTREF COMDelegate::ConvertToDelegate(LPVOID pCallback, MethodTable* pMT) | |
// Wire up the unmanaged call stub to the delegate. | ||
delObj->SetTarget(delObj); // We are the "this" object | ||
|
||
delObj->SetMethodDesc(pMD); | ||
|
||
// For X86, we save the entry point in the delegate's method pointer and the UM Callsite in the aux pointer. | ||
delObj->SetMethodPtr(pMarshalStub); | ||
delObj->SetMethodPtrAux((PCODE)pCallback); | ||
|
@@ -1725,8 +1730,11 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q | |
if (COMDelegate::NeedsWrapperDelegate(pMeth)) | ||
refThis = COMDelegate::CreateWrapperDelegate(refThis, pMeth); | ||
|
||
refThis->SetMethodDesc(pMethOrig); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not 100% positive I use the correct descs when setting the field from VM in places like these. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this trying to be a perf fix or correctness fix?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It was supposed to be a perf improvement, but as per discussion with @AndyAyersMS, if we could guarantee the desc is always reliably set by the VM when a delegate is created and never lazily initialized, delegate GDV could then be improved by switching to it. I feel like guaranteeing that should be left to a future PR though since it needs somebody with more VM/reflection knowledge to check all the possible paths. |
||
|
||
_ASSERTE(refThis->GetInvocationList() == NULL); | ||
if (pMeth->GetLoaderAllocator()->IsCollectible()) | ||
refThis->SetMethodBase(pMeth->GetLoaderAllocator()->GetExposedObject()); | ||
refThis->SetInvocationList(pMeth->GetLoaderAllocator()->GetExposedObject()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
// Open delegates. | ||
if (invokeArgCount == methodArgCount) | ||
|
@@ -1817,11 +1825,16 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) | |
// If you modify this logic, please update DacDbiInterfaceImpl::GetDelegateType, DacDbiInterfaceImpl::GetDelegateType, | ||
// DacDbiInterfaceImpl::GetDelegateFunctionData, and DacDbiInterfaceImpl::GetDelegateTargetObject. | ||
|
||
MethodDesc *pMethodHandle = NULL; | ||
|
||
DELEGATEREF thisDel = (DELEGATEREF) orDelegate; | ||
DELEGATEREF innerDel = NULL; | ||
|
||
MethodDesc *pMethodHandle = thisDel->GetMethodDesc(); | ||
|
||
if (pMethodHandle != NULL) | ||
{ | ||
return pMethodHandle; | ||
} | ||
|
||
INT_PTR count = thisDel->GetInvocationCount(); | ||
if (count != 0) | ||
{ | ||
|
@@ -1889,6 +1902,9 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) | |
} | ||
|
||
_ASSERTE(pMethodHandle); | ||
|
||
thisDel->SetMethodDesc(pMethodHandle); | ||
|
||
return pMethodHandle; | ||
} | ||
|
||
|
@@ -2132,6 +2148,8 @@ DELEGATEREF COMDelegate::CreateWrapperDelegate(DELEGATEREF delegate, MethodDesc* | |
|
||
// save the secure invoke stub. GetWrapperInvoke() can trigger GC. | ||
PCODE tmp = GetWrapperInvoke(pMD); | ||
|
||
gc.refWrapperDel->SetMethodDesc(pMD); | ||
gc.refWrapperDel->SetMethodPtr(tmp); | ||
// save the delegate MethodDesc for the frame | ||
gc.refWrapperDel->SetInvocationCount((INT_PTR)pMD); | ||
|
@@ -2146,34 +2164,32 @@ DELEGATEREF COMDelegate::CreateWrapperDelegate(DELEGATEREF delegate, MethodDesc* | |
} | ||
|
||
// This method will get the MethodInfo for a delegate | ||
extern "C" void QCALLTYPE Delegate_FindMethodHandle(QCall::ObjectHandleOnStack d, QCall::ObjectHandleOnStack retMethodInfo) | ||
extern "C" void QCALLTYPE Delegate_CreateMethodInfo(MethodDesc* methodDesc, QCall::ObjectHandleOnStack retMethodInfo) | ||
{ | ||
QCALL_CONTRACT; | ||
|
||
BEGIN_QCALL; | ||
|
||
GCX_COOP(); | ||
|
||
MethodDesc* pMD = COMDelegate::GetMethodDesc(d.Get()); | ||
MethodDesc* pMD = methodDesc; | ||
pMD = MethodDesc::FindOrCreateAssociatedMethodDescForReflection(pMD, TypeHandle(pMD->GetMethodTable()), pMD->GetMethodInstantiation()); | ||
retMethodInfo.Set(pMD->GetStubMethodInfo()); | ||
|
||
END_QCALL; | ||
} | ||
|
||
extern "C" BOOL QCALLTYPE Delegate_InternalEqualMethodHandles(QCall::ObjectHandleOnStack left, QCall::ObjectHandleOnStack right) | ||
extern "C" MethodDesc* QCALLTYPE Delegate_GetMethodDesc(QCall::ObjectHandleOnStack instance) | ||
{ | ||
QCALL_CONTRACT; | ||
|
||
BOOL fRet = FALSE; | ||
MethodDesc* fRet = nullptr; | ||
|
||
BEGIN_QCALL; | ||
|
||
GCX_COOP(); | ||
|
||
MethodDesc* pMDLeft = COMDelegate::GetMethodDesc(left.Get()); | ||
MethodDesc* pMDRight = COMDelegate::GetMethodDesc(right.Get()); | ||
fRet = pMDLeft == pMDRight; | ||
fRet = COMDelegate::GetMethodDesc(instance.Get()); | ||
|
||
END_QCALL; | ||
|
||
|
@@ -2811,8 +2827,8 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT | |
LoaderAllocator *pTargetMethodLoaderAllocator = pTargetMethod->GetLoaderAllocator(); | ||
BOOL isCollectible = pTargetMethodLoaderAllocator->IsCollectible(); | ||
// A method that may be instantiated over a collectible type, and is static will require a delegate | ||
// that has the _methodBase field filled in with the LoaderAllocator of the collectible assembly | ||
// associated with the instantiation. | ||
// that has the LoaderAllocator of the collectible assembly associated with the instantiation | ||
// stored in the MethodInfo cache. | ||
BOOL fMaybeCollectibleAndStatic = FALSE; | ||
|
||
// Do not allow static methods with [UnmanagedCallersOnlyAttribute] to be a delegate target. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could any of the checks above be removed now that the "slow" path is not that slow?