Skip to content

Locate implementations for reference assemblies using the GAC#24291

Merged
sharwell merged 4 commits intodotnet:dev15.6.xfrom
sharwell:ilspy-ref-assem
Feb 5, 2018
Merged

Locate implementations for reference assemblies using the GAC#24291
sharwell merged 4 commits intodotnet:dev15.6.xfrom
sharwell:ilspy-ref-assem

Conversation

@sharwell
Copy link
Contributor

@sharwell sharwell commented Jan 17, 2018

This pull request partially implements the "locate the runtime assembly" feature described in #24349, specifically for cases where the metadata reference resolves to a reference assembly (i.e. an assembly which does not contain IL for method bodies). Reference assemblies do not produce meaningful results when decompiled, and currently all references to the .NET Framework assemblies fall into this category. For common assemblies already located in the GAC, including the .NET Framework assemblies, this pull request allows the Navigate to Decompiled Sources feature to provide meaningful results.

Fixes #24185 (other related limitations will become separate bugs)

Customer scenario

A user attempts to use the Navigate to Decompiled Sources feature to navigate to a member defined as part of the .NET Framework. The feature does not work as expected, because the decompiled source file shows the code for a reference assembly, which does not have any contents in the bodies of members.

Bugs this fixes

Fixes #24185

Workarounds, if any

None.

Risk

Low. This only impacts the Navigate to Decompiled Sources feature, which is disabled by default and only exposed as an experimental option in the Advanced page of C# settings.

Performance impact

This code is only in effect on the code paths where it is required.

Is this a regression from a previous update?

No.

Root cause analysis

Known limitation of a new feature.

How was the bug found?

Known limitation of a new feature.

Test documentation updated?

Yes.

@sharwell sharwell requested a review from a team as a code owner January 17, 2018 21:13
@sharwell sharwell requested a review from tmat January 19, 2018 19:54
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we should be resolving or loading assemblies in devenv process.

Copy link
Contributor

@CyrusNajmabadi CyrusNajmabadi Jan 19, 2018

Choose a reason for hiding this comment

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

Is there something in .Net, the project system, VS, dtar (etc. etc.) that would be able to do this mapping for us?

Copy link
Member

@tmat tmat Jan 19, 2018

Choose a reason for hiding this comment

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

No (afaik)

Copy link
Member

Choose a reason for hiding this comment

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

We shouldn't report NFW for load failures.

Copy link
Contributor Author

@sharwell sharwell Jan 21, 2018

Choose a reason for hiding this comment

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

❓ I was hoping it would provide insight into cases where Navigate to Decompiled Sources doesn't work properly, in order to prioritize future improvements. In light of this, can you confirm that this edge case should be removed from the telemetry consideration?

@sharwell
Copy link
Contributor Author

@tmat What if we change this to only consider assemblies that are already loaded in the IDE process? In other words, remove the fallback call to Assembly.ReflectionOnlyLoad(fullAssemblyName).

@tmat
Copy link
Member

tmat commented Jan 22, 2018

@sharwell I don't like this approach at all. Customers will get different behavior based on what's loaded in devenv process and what not. That's totally based on what VS features and extension the user have used prior to triggering Go To Disassembly. This would make the feature work seemingly non-deterministically.

@sharwell
Copy link
Contributor Author

@tmat I tried to replace the current logic with GacFileResolver but found that the code for the resolvers is in the Scripting assembly, which EditorFeatures does not reference. How would you recommend I proceed?

@sharwell sharwell requested a review from a team as a code owner January 29, 2018 14:55
@sharwell sharwell changed the title Locate implementations for reference assemblies using the process binding path Locate implementations for reference assemblies using the GAC Jan 29, 2018
@sharwell
Copy link
Contributor Author

@tmat replied to my question above with the following:

Link files from src\Compilers\Shared\GlobalAssemblyCacheHelpers – start with GlobalAssemblyCache.cs, ClrGlobalAssemblyCache.cs and MonoGlobalAssemblyCache.cs. They might need some more.
GacFileResolver implements a specific resolver compiler abstraction that isn’t needed in your scenario.

I implemented the suggested approach, and this should be ready for review again.

@sharwell sharwell requested a review from a team as a code owner January 29, 2018 16:13
var reference = compilation.GetMetadataReference(symbol.ContainingAssembly);

string assemblyLocation = null;
var isReferenceAssembly = symbol.ContainingAssembly.GetAttributes().Any(attribute => attribute.AttributeClass.Name == nameof(ReferenceAssemblyAttribute));
Copy link
Member

Choose a reason for hiding this comment

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

ReferenceAssemblyAttribute [](start = 137, length = 26)

We should check for namespace as well.

var fullAssemblyName = symbol.ContainingAssembly.Identity.GetDisplayName();
GlobalAssemblyCache.Instance.ResolvePartialName(fullAssemblyName, out assemblyLocation, preferredCulture: CultureInfo.CurrentCulture);
}
catch (Exception e) when (FatalError.ReportWithoutCrashUnlessCanceled(e))
Copy link
Member

Choose a reason for hiding this comment

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

ReportWithoutCrashUnlessCanceled [](start = 53, length = 32)

Nit: the code is not cancellable, calling ReportWithoutCrash is sufficient

if (assemblyLocation == null)
{
var reference = compilation.GetMetadataReference(symbol.ContainingAssembly);
assemblyLocation = reference.Display;
Copy link
Member

@tmat tmat Feb 2, 2018

Choose a reason for hiding this comment

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

reference.Display [](start = 34, length = 18)

Display is only meant to be used in messages for the user.
To get the path of the assembly use (reference as PortableExecutableReference)?.FilePath and handle case when the result is null.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

handle case when the result is null

What would an appropriate error handling strategy be here?

Copy link
Member

Choose a reason for hiding this comment

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

I guess we should report that we can't navigate to the symbol.


In reply to: 165761856 [](ancestors = 165761856)

Copy link
Member

@tmat tmat left a comment

Choose a reason for hiding this comment

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

🕐

@tmat
Copy link
Member

tmat commented Feb 2, 2018

                    useDecompiler = !symbol.ContainingAssembly.GetAttributes().Any(attribute => attribute.AttributeClass.Name == nameof(SuppressIldasmAttribute));

Should also check namespace


Refers to: src/EditorFeatures/Core/Implementation/MetadataAsSource/MetadataAsSourceFileService.cs:122 in 4b1853f. [](commit_id = 4b1853f, deletion_comment = False)

* Check fully-qualified names for SuppressIldasmAttribute and ReferenceAssemblyAttribute
* Use correct reference location, or fail decompilation if it's not available
@sharwell
Copy link
Contributor Author

sharwell commented Feb 2, 2018

@tmat Feedback should be addressed now! 😄

Copy link
Member

@tmat tmat left a comment

Choose a reason for hiding this comment

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

:shipit:

@sharwell
Copy link
Contributor Author

sharwell commented Feb 2, 2018

Requesting reviews from @dotnet/roslyn-compiler for the pragma change

Requesting reviews from @dotnet/roslyn-ide for behavior

@sharwell sharwell added this to the 15.6 milestone Feb 2, 2018
@sharwell sharwell self-assigned this Feb 2, 2018
Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

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

Compiler portion LGTM

@natidea
Copy link
Contributor

natidea commented Feb 5, 2018

Copy link
Contributor

@dpoeschl dpoeschl left a comment

Choose a reason for hiding this comment

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

👍

@sharwell sharwell merged commit 79e7182 into dotnet:dev15.6.x Feb 5, 2018
@sharwell sharwell deleted the ilspy-ref-assem branch February 5, 2018 22:09
Copy link
Member

@jasonmalinowski jasonmalinowski left a comment

Choose a reason for hiding this comment

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

Oops I had some pending questions that I never submitted the review on. I approve of the change, treat these as "Jason wants to learn something new" questions.

<Compile Include="..\..\Compilers\Shared\GlobalAssemblyCacheHelpers\ClrGlobalAssemblyCache.cs" Link="Shared\GlobalAssemblyCacheHelpers\ClrGlobalAssemblyCache.cs" />
<Compile Include="..\..\Compilers\Shared\GlobalAssemblyCacheHelpers\FusionAssemblyIdentity.cs" Link="Shared\GlobalAssemblyCacheHelpers\FusionAssemblyIdentity.cs" />
<Compile Include="..\..\Compilers\Shared\GlobalAssemblyCacheHelpers\GlobalAssemblyCache.cs" Link="Shared\GlobalAssemblyCacheHelpers\GlobalAssemblyCache.cs" />
<Compile Include="..\..\Compilers\Shared\GlobalAssemblyCacheHelpers\MonoGlobalAssemblyCache.cs" Link="Shared\GlobalAssemblyCacheHelpers\MonoGlobalAssemblyCache.cs" />
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, how does this work with cross-plat? There's some runtime sniffing to use the right one at runtime?

throw new ArgumentException(Scripting.ScriptingResources.InvalidCharactersInAssemblyName, nameof(name));

#elif WORKSPACE_DESKTOP
#elif WORKSPACE_DESKTOP || EDITOR_FEATURES
Copy link
Member

Choose a reason for hiding this comment

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

@tmat How do we eventually detangle this linked file mess?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants