-
Notifications
You must be signed in to change notification settings - Fork 87
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
Add support for internal metadata view interfaces #10
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
8ac106a
Add SkipClrVisibilityChecks class
AArnott a85ee35
Add tests for internal metadata view interfaces
AArnott e8e7712
Merge skip CLR visibility checks
AArnott 0a8a1a4
Enable dynamically generated metadata views to implement internal int…
AArnott d48ba14
Add test for metadata view that derives from internal base interface
AArnott File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
142 changes: 142 additions & 0 deletions
142
src/Microsoft.VisualStudio.Composition/Reflection/SkipClrVisibilityChecks.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
|
||
namespace Microsoft.VisualStudio.Composition.Reflection | ||
{ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Reflection; | ||
using System.Reflection.Emit; | ||
|
||
/// <summary> | ||
/// Gives a dynamic assembly the ability to skip CLR visibility checks, | ||
/// allowing the assembly to access private members of another assembly. | ||
/// </summary> | ||
internal class SkipClrVisibilityChecks | ||
{ | ||
/// <summary> | ||
/// The assembly builder that is constructing the dynamic assembly. | ||
/// </summary> | ||
private readonly AssemblyBuilder assemblyBuilder; | ||
|
||
/// <summary> | ||
/// The module builder for the default module of the <see cref="assemblyBuilder"/>. | ||
/// This is where the special attribute will be defined. | ||
/// </summary> | ||
private readonly ModuleBuilder moduleBuilder; | ||
|
||
/// <summary> | ||
/// The set of assemblies that already have visibility checks skipped for. | ||
/// </summary> | ||
private readonly HashSet<string> attributedAssemblyNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | ||
|
||
/// <summary> | ||
/// The constructor on the special attribute to reference for each skipped assembly. | ||
/// </summary> | ||
private ConstructorInfo magicAttributeCtor; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="SkipClrVisibilityChecks"/> class. | ||
/// </summary> | ||
/// <param name="assemblyBuilder">The builder for the dynamic assembly.</param> | ||
/// <param name="moduleBuilder">The builder for the default module defined by <see cref="assemblyBuilder"/>.</param> | ||
internal SkipClrVisibilityChecks(AssemblyBuilder assemblyBuilder, ModuleBuilder moduleBuilder) | ||
{ | ||
Requires.NotNull(assemblyBuilder, nameof(assemblyBuilder)); | ||
Requires.NotNull(moduleBuilder, nameof(moduleBuilder)); | ||
this.assemblyBuilder = assemblyBuilder; | ||
this.moduleBuilder = moduleBuilder; | ||
} | ||
|
||
/// <summary> | ||
/// Ensures the CLR will skip visibility checks when accessing | ||
/// the assembly that contains the specified member. | ||
/// </summary> | ||
/// <param name="memberInfo">The member that may not be publicly accessible.</param> | ||
internal void SkipVisibilityChecksFor(MemberInfo memberInfo) | ||
{ | ||
this.SkipVisibilityChecksFor(memberInfo.Module.Assembly); | ||
} | ||
|
||
/// <summary> | ||
/// Add an attribute to the dynamic assembly so that the CLR will skip visibility checks | ||
/// for the specified assembly. | ||
/// </summary> | ||
/// <param name="assembly">The assembly to skip visibility checks for.</param> | ||
private void SkipVisibilityChecksFor(Assembly assembly) | ||
{ | ||
Requires.NotNull(assembly, nameof(assembly)); | ||
var assemblyName = assembly.GetName(); | ||
this.SkipVisibilityChecksFor(assemblyName); | ||
} | ||
|
||
/// <summary> | ||
/// Add an attribute to the dynamic assembly so that the CLR will skip visibility checks | ||
/// for the assembly with the specified name. | ||
/// </summary> | ||
/// <param name="assemblyName">The name of the assembly to skip visibility checks for.</param> | ||
private void SkipVisibilityChecksFor(AssemblyName assemblyName) | ||
{ | ||
Requires.NotNull(assemblyName, nameof(assemblyName)); | ||
|
||
string assemblyNameArg = assemblyName.Name; | ||
if (this.attributedAssemblyNames.Add(assemblyNameArg)) | ||
{ | ||
var cab = new CustomAttributeBuilder(this.GetMagicAttributeCtor(), new object[] { assemblyNameArg }); | ||
this.assemblyBuilder.SetCustomAttribute(cab); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets the constructor to the IgnoresAccessChecksToAttribute, generating the attribute if necessary. | ||
/// </summary> | ||
/// <returns>The constructor to the IgnoresAccessChecksToAttribute.</returns> | ||
private ConstructorInfo GetMagicAttributeCtor() | ||
{ | ||
if (this.magicAttributeCtor == null) | ||
{ | ||
var magicAttribute = this.EmitMagicAttribute(); | ||
this.magicAttributeCtor = magicAttribute.GetConstructor(new Type[] { typeof(string) }); | ||
} | ||
|
||
return this.magicAttributeCtor; | ||
} | ||
|
||
/// <summary> | ||
/// Defines the special IgnoresAccessChecksToAttribute type in the <see cref="moduleBuilder"/>. | ||
/// </summary> | ||
/// <returns>The generated attribute type.</returns> | ||
private TypeInfo EmitMagicAttribute() | ||
{ | ||
var tb = this.moduleBuilder.DefineType( | ||
"System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute", | ||
TypeAttributes.NotPublic, | ||
typeof(Attribute)); | ||
|
||
var attributeUsageCtor = typeof(AttributeUsageAttribute).GetConstructor( | ||
new Type[] { typeof(AttributeTargets) }); | ||
var attributeUsage = new CustomAttributeBuilder( | ||
attributeUsageCtor, | ||
new object[] { AttributeTargets.Assembly }, | ||
new PropertyInfo[] { typeof(AttributeUsageAttribute).GetProperty(nameof(AttributeUsageAttribute.AllowMultiple)) }, | ||
new object[] { false }); | ||
tb.SetCustomAttribute(attributeUsage); | ||
|
||
var cb = tb.DefineConstructor( | ||
MethodAttributes.Public | | ||
MethodAttributes.HideBySig | | ||
MethodAttributes.SpecialName | | ||
MethodAttributes.RTSpecialName, | ||
CallingConventions.Standard, | ||
new Type[] { typeof(string) }); | ||
cb.DefineParameter(1, ParameterAttributes.None, "assemblyName"); | ||
|
||
var il = cb.GetILGenerator(); | ||
il.Emit(OpCodes.Ldarg_0); | ||
ConstructorInfo attrBaseCtor = typeof(Attribute).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null); | ||
il.Emit(OpCodes.Call, attrBaseCtor); | ||
il.Emit(OpCodes.Ret); | ||
|
||
return tb.CreateTypeInfo(); | ||
} | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
src/tests/Microsoft.VisualStudio.Composition.AssemblyDiscoveryTests/AssemblyInfo.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
|
||
using System.Runtime.CompilerServices; | ||
|
||
// This is an important part of testing. We need to know that our ability to handle | ||
// internal types can span assemblies when, for example, an internal type in one assembly | ||
// references an internal type in another assembly. | ||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Composition.Tests")] |
13 changes: 13 additions & 0 deletions
13
src/tests/Microsoft.VisualStudio.Composition.AssemblyDiscoveryTests/IInternalMetadataView.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
|
||
namespace Microsoft.VisualStudio.Composition.AssemblyDiscoveryTests | ||
{ | ||
/// <summary> | ||
/// An internal interface that is used as the base interface | ||
/// of a metadata view defined in another assembly. | ||
/// </summary> | ||
internal interface IInternalMetadataView | ||
{ | ||
string MetadataOnInternalInterface { get; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Are there docs for this attribute? I haven't heard of it before, but it definitely seems to come in handy here. I'm assuming that this attribute is supported in .NET 4.5, right?
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's not documented on MSDN. It is in CoreCLR and .NET Framework, although I'm not sure which version started supporting it. If vs-mef runs on a version that doesn't support it, it will just be ignored and we'll "regress" to behavior before this PR. This PR won't make anything worse.
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 opened a bug to get the attribute documented: Bug 438899: Document IgnoresAccessChecksToAttribute
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.
.NET 4.6 added support for this attribute.
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.
Okay - that should be fine in VS since we require .NET 4.6. I was initially worried about the situations where we launch
devenv /updateConfiguration
when only .NET 4.5 is installed - this can happen if we're pending reboot to install .NET 4.6. However, I just remember this was "fixed" in 15.2, though, by avoiding composing MEF in the cases where .NET 4.6 is not installed (DevDiv PR 66331). The next launch of VS will do the composition in that case.