Skip to content
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

Merged
23 commits merged into from
Aug 18, 2021
Merged

Some cleanups in Assembly/Loader area #57023

23 commits merged into from
Aug 18, 2021

Conversation

VSadov
Copy link
Member

@VSadov VSadov commented Aug 7, 2021

For the most part just mechanical changes and refactoring with no impact to functionality.

  • renamed GAC -> TPA
  • removed ICLRPrivAssembly interface
  • replaced ICLRPrivBinder interface with AssemblyBinder - a base binder class.
  • Assembly does not need to be a binder. (it has a binder, but no longer is a binder itself)
  • moved ApplicationContext down to AssemblyBinder to remove some redundancy.
  • removed GetBinderID from everything that had it as BinderID appears to be a self-supporting feature.
  • renamed CLRPrivBinderCoreCLR and CLRPrivBinderAssemblyLoadContext
    to more sensible DefaultAssemblyBinder and CustomAssemblyBinder

Since ICLRPriv. . . and COM related stuff is gone, we can now have a simple hierarchy of binders that looks like this:

                ................................. use-> ...  BINDER_SPACE::AssemblyBinderCommon 
              /                              /                      (bunch of static helpers)
DefaultAssemblyBinder        CustomAssemblyBinder
                    \        /
                AssemblyLoadContext
                         |
                   AssemblyBinder

@ghost
Copy link

ghost commented Aug 7, 2021

Tagging subscribers to this area: @vitek-karas, @agocke, @VSadov
See info in area-owners.md if you want to be subscribed.

Issue Details
  • rename GAC -> TPA
  • remove ICLRPrivAssembly
  • remove ICLRPrivBinder
Author: VSadov
Assignees: -
Labels:

area-AssemblyLoader-coreclr

Milestone: -

@VSadov VSadov force-pushed the aloader01 branch 3 times, most recently from d300f4c to 731f68c Compare August 7, 2021 19:16
@VSadov VSadov changed the title [WIP] Some cleanups in Assembly/Loader area Some cleanups in Assembly/Loader area Aug 16, 2021
@VSadov VSadov marked this pull request as ready for review August 16, 2021 22:28
@VSadov VSadov force-pushed the aloader01 branch 2 times, most recently from 6a3199c to 624f386 Compare August 16, 2021 23:42
@VSadov
Copy link
Member Author

VSadov commented Aug 17, 2021

I think this is ready for review.
There are certainly more cleanup opportunities, but this change achieves a particular goal - getting rid of COM interfaces in binder/loader area and simplifying binder hierarchy.

@vitek-karas
Copy link
Member

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.

Copy link
Member

@vitek-karas vitek-karas left a 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
Copy link
Member

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.

Copy link
Member Author

@VSadov VSadov Aug 18, 2021

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
Copy link
Member

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.

Copy link
Member Author

@VSadov VSadov Aug 18, 2021

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.

Copy link
Member

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 :-)

Copy link
Member

@elinor-fung elinor-fung Aug 18, 2021

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.

Copy link
Member Author

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).

Copy link
Member

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)

Copy link
Member

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the fact that in managed there is a public class ApplicationContext (in winforms) and that there is an AppContext class (also public) in CoreLib. Either of which has absolutely no relationship to the native one :-)

UINT_PTR ptrAssemblyLoadContext,
CLRPrivBinderAssemblyLoadContext **ppBindContext)
HRESULT CustomAssemblyBinder::SetupContext(DefaultAssemblyBinder *pTPABinder,
AssemblyLoaderAllocator* pLoaderAllocator,
Copy link
Member

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;
Copy link
Member

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;
Copy link
Member

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();
Copy link
Member

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

Copy link
Member Author

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 :
Copy link
Member

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...

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member Author

@VSadov VSadov Aug 18, 2021

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 :-)

Copy link
Member

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; }
Copy link
Member

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).

Copy link
Member Author

@VSadov VSadov Aug 18, 2021

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());
Copy link
Member

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.

Copy link
Member

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.

Copy link
Member Author

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@vitek-karas
Copy link
Member

Side note: First time for me to use the . key on a PR - it was SO MUCH easier to review that way.

@VSadov
Copy link
Member Author

VSadov commented Aug 18, 2021

could we please rename all of the "binder context" variables/names to just "binder"

YES, just did not want to put too much into one change.

Copy link
Member

@elinor-fung elinor-fung left a 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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: alignment

Copy link
Member

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();
Copy link
Member

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
Copy link
Member

@elinor-fung elinor-fung Aug 18, 2021

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.

@ghost
Copy link

ghost commented Aug 18, 2021

Hello @VSadov!

Because this pull request has the auto-merge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

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 (@msftbot) and give me an instruction to get started! Learn more here.

@ghost ghost merged commit 7f85a8c into dotnet:main Aug 18, 2021
@VSadov VSadov deleted the aloader01 branch August 19, 2021 00:21
@@ -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
Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member Author

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.

@ghost ghost locked as resolved and limited conversation to collaborators Oct 2, 2021
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants