Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.

VirtualInvokeMap Table#3100

Merged
fadimounir merged 1 commit intodotnet:masterfrom
fadimounir:VirtualInvokeMap
Mar 27, 2017
Merged

VirtualInvokeMap Table#3100
fadimounir merged 1 commit intodotnet:masterfrom
fadimounir:VirtualInvokeMap

Conversation

@fadimounir
Copy link

Implementation of the VirtualInvokeMap table to enable Reflection invocation to virtual and generic virtual methods.

Implementation supports both CoreRT and ProjectX models.
Fixed interface EEType generation to unconditionally produce a dictionary slot for generic interfaces (to support static interface methods, and enable correct Reflection virtual invoke slot computation on ProjectX).

@fadimounir
Copy link
Author

@davidwrighton @MichalStrehovsky PTAL.

/// </summary>
internal sealed class ReflectionVirtualInvokeMapNode : ObjectNode, ISymbolNode
{
private const uint GenericVirtualMethod = 1;
Copy link
Member

@MichalStrehovsky MichalStrehovsky Mar 24, 2017

Choose a reason for hiding this comment

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

Would be nice to make this a constant we can share with the framework code. I've started dumping these to Common\src\Internal\Runtime\RuntimeConstants.cs.

It makes it really easy to findstr all the places that use this. #Resolved

Copy link
Author

Choose a reason for hiding this comment

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

Reusing VirtualInvokeTableEntry.GenericVirtualMethod from MappingTableFlags.cs instead.


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

{
// Note: Canonical type instantiations always have a generic dictionary vtable slot, but it's empty
if (declType.IsCanonicalSubtype(CanonicalFormKind.Any))
if (declType.IsInterface || declType.IsCanonicalSubtype(CanonicalFormKind.Any))
Copy link
Member

@MichalStrehovsky MichalStrehovsky Mar 24, 2017

Choose a reason for hiding this comment

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

Should this actually go down the path that gets the dictionary instead? If it doesn't work right now for whatever reason, there should at least be a comment. #Resolved

Copy link
Author

@fadimounir fadimounir Mar 24, 2017

Choose a reason for hiding this comment

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

I was looking at what the binder did, and it was not filling the dictionary slot with anything useful.
I'll investigate this further to see if we ever fill the dictionary slot.
@davidwrighton, do you know if we ever fill the slot with a dictionary pointer? If so, under what circumstances? #Resolved

Copy link
Member

@MichalStrehovsky MichalStrehovsky Mar 24, 2017

Choose a reason for hiding this comment

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

I would assume this actually being used if there's a static nongeneric method on a generic interface. I wouldn't be surprised if we have no test coverage for that and it simply AV's (because the dictionary is null) in Project N. #Resolved

Copy link
Author

@fadimounir fadimounir Mar 25, 2017

Choose a reason for hiding this comment

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

I'll put a TODO comment to generate the right dictionary entry for static methods. #Resolved

{
builder.EmitShort(checked((short)interfaceIndex));
builder.EmitShort(checked((short)interfaceMethodSlot));
builder.EmitShort(checked((short)(interfaceMethodSlot + (interfaceType.HasInstantiation ? 1 : 0))));
Copy link
Member

@MichalStrehovsky MichalStrehovsky Mar 24, 2017

Choose a reason for hiding this comment

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

Maybe make it interfaceType.HasGenericDictionarySlot() for clarity? #Resolved

Copy link
Author

@fadimounir fadimounir Mar 24, 2017

Choose a reason for hiding this comment

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

Ah yes... I wanted to change it to HasGenericDictionarySlot too but forgot. #Resolved

else
containingTypeKey = method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific);

if (!methodsEmitted.ContainsKey(mappingEntry.MetadataHandle))
Copy link
Member

@MichalStrehovsky MichalStrehovsky Mar 24, 2017

Choose a reason for hiding this comment

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

Nit: could you TryGetValue to avoid two lookups. #Resolved

Copy link
Author

Choose a reason for hiding this comment

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

fixed


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


if (slot == -1)
{
// This method doesn't have a slot. (At this time, this is only done for the Object.Finalize method)
Copy link
Member

@MichalStrehovsky MichalStrehovsky Mar 24, 2017

Choose a reason for hiding this comment

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

Should we assert this? #Resolved

Copy link
Author

@fadimounir fadimounir Mar 24, 2017

Choose a reason for hiding this comment

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

Added one.

// For types that have a generic dictionary, the introduced virtual method slots are
// prefixed with a pointer to the generic dictionary.
if (baseType.HasGenericDictionarySlot())
if (baseType.HasGenericDictionarySlot() && !doNotCountDictionarySlots)
Copy link
Member

@MichalStrehovsky MichalStrehovsky Mar 24, 2017

Choose a reason for hiding this comment

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

Can you make this countDictionarySlots? Double negation is kind of hard to parse for humans. #Resolved

Copy link
Author

Choose a reason for hiding this comment

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

Sure :)


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

{
return !type.IsInterface && type.HasInstantiation &&
// Dictionary slots on generic interfaces are necessary to support static methods on interfaces
if (type.IsInterface)
Copy link
Member

@MichalStrehovsky MichalStrehovsky Mar 24, 2017

Choose a reason for hiding this comment

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

Just remove the type.IsInterface from the return below. Otherwise you're forcing dictionary slots for interfaces even if we're not compiling shared code, or when interface methods wouldn't need a dictionary anyway (e.g. IEnumerable<int>). #WontFix

Copy link
Author

@fadimounir fadimounir Mar 24, 2017

Choose a reason for hiding this comment

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

The dictionary slot is unconditionally created on generic interfaces, regardless of whether they share code or not.
See code/comment in TypeLoaderEnvironment.Metadata.cs:
if (RuntimeAugments.IsGenericType(searchForSharedGenericTypesInParentHierarchy)) { if (RuntimeAugments.IsInterface(searchForSharedGenericTypesInParentHierarchy)) { // Generic interfaces always have a dictionary slot in the vtable (see binder code in MdilModule::SaveMethodTable) // Interfaces do not have base types, so we can just break out of the loop here ... slot++; break; } #Closed

Copy link
Author

@fadimounir fadimounir Mar 25, 2017

Choose a reason for hiding this comment

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

I chatted with @davidwrighton about this. The historical reason behind making this unconditional is simplicity, and keeping method slot indices for methods on IFoo and IFoo identical. That won't change. #Closed

Copy link
Member

@MichalStrehovsky MichalStrehovsky Mar 25, 2017

Choose a reason for hiding this comment

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

The historical reason behind making this unconditional is simplicity, and keeping method slot indices for methods on IFoo and IFoo identical. That won't change.

Can you add that to the comment? Sounds like something that would be nice to capture. #Resolved

Copy link
Author

@fadimounir fadimounir Mar 27, 2017

Choose a reason for hiding this comment

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

ok #Resolved

Copy link
Member

@davidwrighton davidwrighton left a comment

Choose a reason for hiding this comment

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

I'm a little disappointed in the multiple ABIs logic in the runtime, but I can live with it. (It might be nice to convert to supporting both formats in the runtime) In any case, if you change the format of ReflectionMapBlob.VirtualInvokeMap, you must update all consumers of that format.

if (!factory.NecessaryTypeSymbol(templateType).RepresentsIndirectionCell)
dependencyList.Add(factory.NativeLayout.TemplateTypeLayout(templateType), "Template Type Layout");
if (templateType.IsCanonicalSubtype(CanonicalFormKind.Any))
if (!factory.NecessaryTypeSymbol(templateType).RepresentsIndirectionCell)
Copy link
Member

@davidwrighton davidwrighton Mar 27, 2017

Choose a reason for hiding this comment

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

Please wrap the inner if statement in {} brackets. Nested ifs without {} are rife for accidental screwup. #Resolved

@@ -823,11 +823,38 @@ public static bool TryGetVirtualResolveData(NativeFormatModuleInfo module,
ExternalReferencesTable externalReferences = default(ExternalReferencesTable);
externalReferences.InitializeCommonFixupsTable(module);

Copy link
Member

Choose a reason for hiding this comment

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

A corresponding change to TryGetMethodNameAndSigFromVirtualResolveData is missing from this change.

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for catching this.

Copy link
Author

Choose a reason for hiding this comment

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

Fixed for the declaring type semantics. No changes done to the slot index computation as we discussed, since this code is only used with SUPPORTS_NATIVE_METADATA_TYPE_LOADING

…ocation to virtual and generic virtual methods.

Implementation supports both CoreRT and ProjectX models.
Fixed interface EEType generation to unconditionally produce a dictionary slot for generic interfaces (to support static interface methods, and enable correct Reflection virtual invoke slot computation on ProjectX).
@fadimounir fadimounir merged commit f2d7716 into dotnet:master Mar 27, 2017
@fadimounir fadimounir deleted the VirtualInvokeMap branch April 6, 2017 17:00
MichalStrehovsky added a commit to MichalStrehovsky/corert that referenced this pull request May 19, 2017
Generic interface types have a generic dictionary slot like any other
type, except sometimes the slot is null.

We can now call static methods on generic interfaces with a usable
generic context.

Got reminded of this while reading about the planned C# language support
for static methods on interfaces. I didn't want to push on it when dotnet#3100
added the logic, but it turns out this is really easy.
MichalStrehovsky added a commit that referenced this pull request May 20, 2017
Generic interface types have a generic dictionary slot like any other
type, except sometimes the slot is null.

We can now call static methods on generic interfaces with a usable
generic context.

Got reminded of this while reading about the planned C# language support
for static methods on interfaces. I didn't want to push on it when #3100
added the logic, but it turns out this is really easy.
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