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

Handle satellite assembly loading in .NET Core #70769

Merged
merged 8 commits into from
Nov 15, 2023
Merged

Conversation

jaredpar
Copy link
Member

@jaredpar jaredpar commented Nov 10, 2023

This fixes a bug where-by the compiler did not load satellite assemblies properly on Unix and Linux. The compiler recently changed (#69406) our loading strategy there to use AssemblyLoadContext.LoadFromStream. The runtime fallback logic for loading satellite assemblies is effectively the following:

  1. Call AssemblyLoadContext.Load(satelliteAssemblyName)
  2. If that fails call AssemblyLoadContext.Load(realAssemblyName) for the real assembly
  3. Look at Assembly.Location on the return and change it to have the appropriate satellite path.
  4. Call AssemblyLoadContext.LoadFromPath(...) on the result of (3)

That logic breaks when assemblies are loaded via LoadFromStream as there is no location information.

This changes our AssemblyLoadContext implementation to just handle satellite loads in step (1). This is done irrespective of whether the loader is loading via path or via stream. The loader has the information already so simpler to just handle it the same way every time.

closes #56708

Note: these are the docs describing satellite assembly loading.

This fixes a bug where-by the compiler did not load satellite assemblies
properly on Unix and Linux. The compiler recently changed (dotnet#69406) our
loading strategy there to use `AssemblyLoadContext.LoadFromStream`. The
runtime fallback logic for loading satellite assemblies is effectively
the following:

1. Call `AssemblyLoadContext.Load(satelliteAssemblyName)`
2. If that fails call `AssemblyLoadContext.Load(realAssemblyName)` for
   the real assembly
3. Look at `Assembly.Location` on the return and change it to have the
   appropriate satellite path.
4. Call `AssemblyLoadContext.LoadFromPath(...)` on the result of (3)

That logic breaks when assemblies are loaded via `LoadFromStream` as
there is no location information.

This changes our `AssemblyLoadContext` implementation to just handle
satellite loads in step (1). This is done irrespective of whether the
loader is loading via path or via stream. The loader has the information
already so simpler to just handle it the same way every time.

closes dotnet#56708

Note: these are [the
docs](https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/loading-resources)
describing satellite assembly loading.
@jaredpar jaredpar requested a review from a team as a code owner November 10, 2023 22:19
@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged Issues and PRs which have not yet been triaged by a lead label Nov 10, 2023
@jaredpar
Copy link
Member Author

@dotnet/roslyn-compiler, @jjonescz, @RikkiGibson PTAL

@@ -128,59 +129,41 @@ private void DeleteLeftoverDirectories()
}
}

protected override string PreparePathToLoad(string originalFullPath)
protected override string PreparePathToLoad(string originalAnalyzerPath, ImmutableHashSet<string> cultureNames)
Copy link
Member Author

Choose a reason for hiding this comment

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

For this file I recommend just looking at the new code, not the diff. The code is pretty straight forward.

var rm = new ResourceManager("rmc", typeof(Util).Assembly);
_ = rm.GetString("hello", ci);
}
catch (Exception ex)
Copy link
Member

Choose a reason for hiding this comment

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

Why is this catch needed?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's needed because the resource doesn't actually exist. This code is just getting the runtime to probe for the satellite assemblies to prove that we do the loading correct. So the satellite load will succeed then it will throw when it can't find the resource.

Getting a real resource was going to involve checking in a couple of binary resources that I couldn't think of an easy way to maintain. As such decided to just focus on testing the load portion here.

// loader has a mode where it loads from Stream though and the runtime will not handle
// that automatically. Rather than bifurate our loading behavior between Disk and
// Stream both modes just handle satellite loading directly
if (!string.IsNullOrEmpty(assemblyName.CultureName) && simpleName.EndsWith(".resources", StringComparison.Ordinal))
Copy link
Member

Choose a reason for hiding this comment

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

Is ordinal comparison correct? Shouldn't it ignore case?

Copy link
Member Author

Choose a reason for hiding this comment

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

There are unfortunately no right answers to this. Whichever you pick can be made wrong. Generally though in the compiler we try and use case insensitive when searching for files but case sensitive when actually looking at files in hand.

@jaredpar jaredpar requested a review from a team as a code owner November 13, 2023 17:53
@jaredpar
Copy link
Member Author

@RikkiGibson PTAL

@jaredpar jaredpar enabled auto-merge (squash) November 15, 2023 18:16
@jaredpar jaredpar merged commit 14168c4 into dotnet:main Nov 15, 2023
22 of 27 checks passed
@ghost ghost added this to the Next milestone Nov 15, 2023
@jaredpar jaredpar deleted the resource branch November 15, 2023 23:48
@RikkiGibson RikkiGibson modified the milestones: Next, 17.9 P2 Nov 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Handle satellite assemblies in analyzer loader
3 participants