-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Some cleanups in Assembly/Loader area #57023
Conversation
Tagging subscribers to this area: @vitek-karas, @agocke, @VSadov Issue Details
|
d300f4c
to
731f68c
Compare
6a3199c
to
624f386
Compare
I think this is ready for review. |
Thinking forward a bit (future changes, not this one): could we please rename all of the "binder context" variables/names to just "binder"? It's just confusing. Or alternatively (and maybe even better) - merge AssemblyBinder and AssemblyLoadContext to a single AssemblyLoadContext. This might be a bit tricky with keeping the binder separation, but maybe that should go as well. In which case the whole "binder" term could go away as well. |
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.
This is really nice!
Mostly just nits and suggestions for possible future improvements. Feel free to keep this as is since it's already big enough :-)
@@ -174,40 +131,17 @@ namespace BINDER_SPACE | |||
} | |||
|
|||
// -------------------------------------------------------------------- | |||
// ICLRPrivAssembly methods | |||
// BINDER_SPACE::Assembly methods |
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.
I would remove this comment - I don't think it makes sense anymore.
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.
Right. Some comments moved with the code and the style could be out of place in the new location or may not be very helpful. Some were not very helpful to start with and likely were forced by some kind of "every method/class must have a comment" policy.
'An assembly represents a particular set of bits.' is my favorite :-).
class PEAssembly; | ||
class PEImage; | ||
|
||
class DefaultAssemblyBinder final : public AssemblyLoadContext |
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.
Nit: I would prefer DefaultAssemblyLoadContext
. It just feels weird that the inheritance is AssemblyBinder
-> AssemblyLoadContext
-> DefaultAssemblyBinder
.
Same for CustomAssemblyLoadContext
.
But it's just a subjective naming - so feel free to keep as is.
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.
I think "Context" works better for something passive - like environment used for binding. When the class itself provides binding functionality it should be Binder, since it is more than just context.
The base class main purpose is to provide "Bind" APIs, so it feels like it should be "Binder".
The AssemblyLoadContext
feels a bit odd in the hierarchy until you realize its purpose is to link to the managed AssemblyLoadContext counterpart.
ALC does not add any "Bind" methods and is not normally used for binding (or should not be encouraged). We could in theory imagine a Binder that inherits from AssemblyBinder directly and does not have ALC stuff on it. In theory.
Technically ALC could be merged with the base class, but since it serves distinct purposes, it feels better to keep it a separate concern in the hierarchy and keep its name too.
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.
ALC does not add any "Bind" methods and is not normally used for binding (or should not be encouraged).
I'm not sure I follow this. Logically ALC.LoadFromAssemblyPath
is the managed equivalent of AssemblyBinder.BindUsingPEImage
(this is actually pretty much exactly the relationship). ALC.LoadFromAssemblyName
maps onto AssemblyBinder.BindUsingAssemblyName
(it's also a pretty good match in reality). It's not exact since there are additional layers between the managed code and native (to deal with marshalling validation and so on). So while I agree that nobody in the runtime should call the Bind methods directly, they are sort of called "directly" from managed a lot.
But I guess I see your point. Runtime has a separate concept of "binding" (finding the right file for a given assembly spec) and a separate concept of "loading" (actually parsing a file and making it available to run code from). The managed APIs don't have these as separate concepts (although in the past we discussed that having it would be beneficial in some cases).
I still think that the sealed classes should be called DefaultAssemblyLoadContext
/CustomAssemblyLoadContext
- since they're a specialization of AssemblyLoadContext
- but it's just a subjective naming thing :-)
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.
DefaultAssemblyLoadContext
/CustomAssemblyLoadContext
would fit in my brain better as well. Although I also have the native AssemblyLoadContext
and AssemblyBinder
classes lumped together in my brain, so that may be why.
it serves distinct purposes, it feels better to keep it a separate concern in the hierarchy and keep its name too.
Since what is clearer / or less clear to each person is pretty subjective, it might help to have comments in the headers with the intent / describing the intentional separation.
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.
As we discuss this I am getting more and more convinced that native AssemblyLoadContext
should probably be merged with its base.
Native AssemblyLoadContext
is not a native representation of the managed one, it just has a link to the managed, and can provide it via GetManagedAssemblyLoadContext
. As a matter of completely mechanical naming it would be something like AssemblyBinderWithManagedAssemblyLoadContext
.
However we do not really have concrete binders that cannot provide a managed context. If we do not envision such a case, then maybe we do not need an abstract one either.
Native AssemblyLoadContext
seems to add more naming confusion than actual functionality (which is not all that much).
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.
Native AssemblyLoadContext is not a native representation of the managed one, it just has a link to the managed, and can provide it via GetManagedAssemblyLoadContext
True, but for all intents and purposes AssemblyLoadContext
-native has 1:1 relationship with AssemblyLoadContext
-managed. The native one has a pointer to the managed one (and vice versa). Also all of the functionality of managed ALC is implemented (indirectly) in the native ALC (lot of this is hidden in the ApplicationContext
which is embeded in the native ALC - and yes - that name is TERRIBLE and has to go :-), but that's a different refactoring)
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.
the
ApplicationContext
which is embeded in the native ALC - and yes - that name is TERRIBLE and has to go
The ApplicationContext
which also contains an ExecutionContext
which is a subclass of LoadContext
! So much context.
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.
UINT_PTR ptrAssemblyLoadContext, | ||
CLRPrivBinderAssemblyLoadContext **ppBindContext) | ||
HRESULT CustomAssemblyBinder::SetupContext(DefaultAssemblyBinder *pTPABinder, | ||
AssemblyLoaderAllocator* pLoaderAllocator, |
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.
Nit: indentation seems to be off
@@ -614,7 +614,7 @@ class MulticoreJitRecorder | |||
{ | |||
private: | |||
AppDomain * m_pDomain; // AutoStartProfile could be called from SystemDomain | |||
ICLRPrivBinder * m_pBinderContext; | |||
AssemblyBinder * m_pBinderContext; |
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.
Nit: match indentation of the code around
@@ -274,7 +274,7 @@ class MulticoreJitProfilePlayer | |||
friend class MulticoreJitRecorder; | |||
|
|||
private: | |||
ICLRPrivBinder * m_pBinderContext; | |||
AssemblyBinder * m_pBinderContext; |
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.
Nit: match indentation
UINT_PTR thisBinderId = 0; | ||
if (FAILED(this->GetHostAssembly()->GetBinderID(&thisBinderId))) | ||
return FALSE; | ||
AssemblyBinder* fileBinderId = pFile->GetHostAssembly()->GetBinder(); |
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.
Nit: fileBinderId
-> fileBinder
and also thisBinderId
-> thisBinder
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.
Right. I missed that these are still Ids
m_pAssemblyLoadContext = (pOpaqueBinder != NULL) ? | ||
(AssemblyLoadContext*)pOpaqueBinder : | ||
m_pAssemblyLoadContext = (pBindingContext != NULL) ? | ||
(AssemblyLoadContext*)pBindingContext : |
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.
Just curious: This means that all AssemblyBinder
instances are also AssemblyLoadContext
instances. Maybe in the future we can remove another layer of abstraction...
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.
I thought about merging AssemblyBinder and AssemblyLoadContext an decided to keep them separate on purpose.
AssemblyLoadContext should keep providing the link to the managed ALC, but should itself not be involved in binding business.
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.
Well - it has to be involved by the virtue of having a link to the managed extensions points (events and so on). But I guess it's OK to view them as slightly separate.
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.
At the first glance I saw enough usage of AssemblyLoadContext
on the native side, but looking closer it mostly refers to the the managed one or to its name. The part that there is a native class that matches the name of the managed type may not be all that helpful.
Perhaps we do not need AssemblyLoadContext on the native side and should just move its stuff to the AssemblyBinder
.
I need to think about this.
It is easier to merge types than to un-merge them back later, if we regret it :-)
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.
It is easier to merge types than to un-merge them back later, if we regret it :-)
That is very true. 👍
@@ -1103,7 +1103,7 @@ class BaseDomain | |||
|
|||
#endif // DACCESS_COMPILE && !CROSSGEN_COMPILE | |||
|
|||
CLRPrivBinderCoreCLR *GetTPABinderContext() {LIMITED_METHOD_CONTRACT; return m_pTPABinderContext; } | |||
DefaultAssemblyBinder *GetTPABinderContext() {LIMITED_METHOD_CONTRACT; return m_pTPABinderContext; } |
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.
I would like if we could rename this method to GetDefaultBinderContext
- or even better GetDefaultBinder
. And in general get rid of the "binder context" term, and just use "binder".
The "TPA" part should be more or less an implementation detail. I know we're now keeping it on per-assembly level, but eventually that should go away as well (as a term and maybe even as the functionality - I'm having certain doubts about its usefulness - but that's a different discussion).
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.
Yes. It might be ok to use "TPA" when referring to the actual set of assemblies, but there is really no such thing as TPA binder - there is DefaultBinder who's binding behavior mostly guided by TPA set, but that is an implementation detail for the binder.
{ | ||
// If the parent assembly is a platform (TPA) assembly, then its binding context will always be the TPABinder context. In | ||
// such case, we will return the default context for binding to allow the bind to go | ||
// via the custom binder context, if it was overridden. If it was not overridden, then we will get the expected | ||
// TPABinder context anyways. | ||
// | ||
// Get the reference to the default binding context (this could be the TPABinder context or custom AssemblyLoadContext) | ||
pParentAssemblyBinder = static_cast<ICLRPrivBinder*>(pDomain->GetTPABinderContext()); | ||
pParentAssemblyBinder = static_cast<AssemblyBinder*>(pDomain->GetTPABinderContext()); |
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.
Do we even need the specific cast here? This should cast implicitly just fine.
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.
Actually - we don't any of this now - if pParentAssemblyBinder
is already the default binder - then this will assign the exact same value to it - no change.
We should simply remove this entire if and just keep the one below which uses default binder if it's null.
Maybe keep the comments in some way.
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.
Right. I also think adding IsDefault()
to the AssemblyBinder could simplify a few places that do if (binder == GetDomain()->GetBinder()) { . . . }
hr = pTPABinder->BindUsingPEImage(pImage, fIsNativeImage, &pAssembly); | ||
} | ||
DefaultAssemblyBinder *pTPABinder = pCurDomain->GetTPABinderContext(); | ||
hr = pBinderContext->BindUsingPEImage(pImage, fIsNativeImage, &pAssembly); |
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.
Nice!
Side note: First time for me to use the |
YES, just did not want to put too much into one change. |
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.
This is great 🎉
// and fExplicitBindToNativeImage, and see code:CEECompileInfo::LoadAssemblyByPath | ||
// for an example of how they're used. | ||
HRESULT AssemblyBinder::BindAssembly(/* in */ ApplicationContext *pApplicationContext, | ||
HRESULT AssemblyBinderCommon::BindAssembly(/* in */ AssemblyBinder *pBinder, |
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.
nit: alignment
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.
Throughout due to a bunch of the AssemblyBinder
-> AssemblyBinderCommon
renames.
// Bind the assembly using TPA binder | ||
hr = pTPABinder->BindUsingPEImage(pImage, fIsNativeImage, &pAssembly); | ||
} | ||
DefaultAssemblyBinder *pTPABinder = pCurDomain->GetTPABinderContext(); |
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.
Don't think you need pCurDomain
or pTPABinder
anymore.
class PEAssembly; | ||
class PEImage; | ||
|
||
class DefaultAssemblyBinder final : public AssemblyLoadContext |
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.
DefaultAssemblyLoadContext
/CustomAssemblyLoadContext
would fit in my brain better as well. Although I also have the native AssemblyLoadContext
and AssemblyBinder
classes lumped together in my brain, so that may be why.
it serves distinct purposes, it feels better to keep it a separate concern in the hierarchy and keep its name too.
Since what is clearer / or less clear to each person is pretty subjective, it might help to have comments in the headers with the intent / describing the intentional separation.
Hello @VSadov! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
@@ -71,6 +76,15 @@ namespace BINDER_SPACE | |||
|
|||
static HRESULT TranslatePEToArchitectureType(DWORD *pdwPAFlags, PEKIND *PeKind); | |||
|
|||
static HRESULT DefaultBinderSetupContext(DefaultAssemblyBinder** ppTPABinder); | |||
|
|||
// TODO: The call indicates that this can come from a case where |
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.
@VSadov is this TODO obsolete? If not, it would be nice to have an issue for it and reference it here.
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.
Possibly obsolete. CorLib may have NULL binder, but I am not sure if it goes through here.
I will check on this TODO
in later changes.
I also think the statics on AssemblyBinderCommon
may be moved to AssemblyBinder
, but have not decided on that yet.
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.
I think some of this code will go to managed eventually, just want to clean it up a bit as-is beforehand. It seems like a good thing to do regardless.
For the most part just mechanical changes and refactoring with no impact to functionality.
ICLRPrivAssembly
interfaceICLRPrivBinder
interface withAssemblyBinder
- a base binder class.Assembly
does not need to be a binder. (it has a binder, but no longer is a binder itself)ApplicationContext
down toAssemblyBinder
to remove some redundancy.GetBinderID
from everything that had it as BinderID appears to be a self-supporting feature.CLRPrivBinderCoreCLR
andCLRPrivBinderAssemblyLoadContext
to more sensible
DefaultAssemblyBinder
andCustomAssemblyBinder
Since
ICLRPriv. . .
and COM related stuff is gone, we can now have a simple hierarchy of binders that looks like this: