-
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
Fix support for multiple internal metadata view interfaces defined from multiple assemblies #93
Changes from all commits
68cb6a0
8c3054a
5e8bc49
b970746
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ namespace Microsoft.VisualStudio.Composition.Reflection | |
{ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Reflection.Emit; | ||
|
@@ -64,33 +65,64 @@ internal SkipClrVisibilityChecks(AssemblyBuilder assemblyBuilder, ModuleBuilder | |
} | ||
|
||
/// <summary> | ||
/// Ensures the CLR will skip visibility checks when accessing | ||
/// the assembly that contains the specified member. | ||
/// Gets the set of assemblies that a generated assembly must be granted the ability to skip visiblity checks for | ||
/// in order to access the specified type. | ||
/// </summary> | ||
/// <param name="memberInfo">The member that may not be publicly accessible.</param> | ||
internal void SkipVisibilityChecksFor(MemberInfo memberInfo) | ||
/// <param name="typeInfo">The type which may be internal.</param> | ||
/// <returns>The set of names of assemblies to skip visibility checks for.</returns> | ||
internal static ImmutableHashSet<AssemblyName> GetSkipVisibilityChecksRequirements(TypeInfo typeInfo) | ||
{ | ||
this.SkipVisibilityChecksFor(memberInfo.Module.Assembly); | ||
Requires.NotNull(typeInfo, nameof(typeInfo)); | ||
|
||
if (ReflectionHelpers.IsPublic(typeInfo.AsType())) | ||
{ | ||
return ImmutableHashSet<AssemblyName>.Empty; | ||
} | ||
else | ||
{ | ||
var result = ImmutableHashSet<AssemblyName>.Empty | ||
.Add(typeInfo.Assembly.GetName()); | ||
|
||
// If this type has a base type defined in another assembly that is also internal | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you mean to leave the commented code here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, since it really feels like it should be here, and I want it to remind me to ask the CLR folks why I don't need visibility into transitive assemblies when I only ask to skip visibility checks for the first assembly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because what's happening there is that access checks are only done on the metadata as written. For instance, A derives from B derives from C. As written, A only knows about B, so the access check from A is only to see if it can access B. For access to C, the only access check is that B is allowed to access C. These rules are straightforward as long as no techniques are used to skirt around access checks, but when you do, you can see surprises like this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, thanks. If A defines a member that my proxy implements, evidently that doesn't require C's visibility into A. I guess if A defines a member with a Type that is internal to A, at that point C couldn't implement it without an explicit skip visibility check for A. Yes? |
||
// (with InternalsVisibleTo to the assembly hosting the original type) | ||
// then we'll need to add that. | ||
// TODO: Learn we somehow we don't need to do this, even given our test defines an internal interface | ||
// that derives from another internal interface defined in another assembly. | ||
////if (typeInfo.BaseType != null) | ||
////{ | ||
//// result = result.Union(GetSkipVisibilityChecksRequirements(typeInfo.BaseType.GetTypeInfo())); | ||
////} | ||
|
||
////foreach (var iface in typeInfo.GetInterfaces()) | ||
////{ | ||
//// result = result.Union(GetSkipVisibilityChecksRequirements(iface.GetTypeInfo())); | ||
////} | ||
|
||
return result; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Add an attribute to the dynamic assembly so that the CLR will skip visibility checks | ||
/// for the specified assembly. | ||
/// Add attributes to a dynamic assembly so that the CLR will skip visibility checks | ||
/// for the assemblies with the specified names. | ||
/// </summary> | ||
/// <param name="assembly">The assembly to skip visibility checks for.</param> | ||
private void SkipVisibilityChecksFor(Assembly assembly) | ||
/// <param name="assemblyNames">The names of the assemblies to skip visibility checks for.</param> | ||
internal void SkipVisibilityChecksFor(IEnumerable<AssemblyName> assemblyNames) | ||
{ | ||
Requires.NotNull(assembly, nameof(assembly)); | ||
var assemblyName = assembly.GetName(); | ||
this.SkipVisibilityChecksFor(assemblyName); | ||
Requires.NotNull(assemblyNames, nameof(assemblyNames)); | ||
|
||
foreach (var assemblyName in assemblyNames) | ||
{ | ||
this.SkipVisibilityChecksFor(assemblyName); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Add an attribute to the dynamic assembly so that the CLR will skip visibility checks | ||
/// Add an attribute to a 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) | ||
internal void SkipVisibilityChecksFor(AssemblyName assemblyName) | ||
{ | ||
Requires.NotNull(assemblyName, nameof(assemblyName)); | ||
|
||
|
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.
If the type is public, do you still need to do this? (i.e when
skipVisibilityCheckAssemblies
isImmutableHashSet<AssemblyName>.Empty
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.
When the set is empty, we still need a dynamic assembly to generate into. Our lookup dictionary needs an entry for the empty set as well so we don't end up special casing empty sets -- we execute the same code regardless of the size of the set.