Conversation
| /// </summary> | ||
| internal sealed class ReflectionVirtualInvokeMapNode : ObjectNode, ISymbolNode | ||
| { | ||
| private const uint GenericVirtualMethod = 1; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)))); |
There was a problem hiding this comment.
Maybe make it interfaceType.HasGenericDictionarySlot() for clarity? #Resolved
There was a problem hiding this comment.
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)) |
There was a problem hiding this comment.
Nit: could you TryGetValue to avoid two lookups. #Resolved
There was a problem hiding this comment.
|
|
||
| if (slot == -1) | ||
| { | ||
| // This method doesn't have a slot. (At this time, this is only done for the Object.Finalize method) |
There was a problem hiding this comment.
Should we assert this? #Resolved
| // 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) |
There was a problem hiding this comment.
Can you make this countDictionarySlots? Double negation is kind of hard to parse for humans. #Resolved
There was a problem hiding this comment.
| { | ||
| return !type.IsInterface && type.HasInstantiation && | ||
| // Dictionary slots on generic interfaces are necessary to support static methods on interfaces | ||
| if (type.IsInterface) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
davidwrighton
left a comment
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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); | |||
|
|
|||
There was a problem hiding this comment.
A corresponding change to TryGetMethodNameAndSigFromVirtualResolveData is missing from this change.
There was a problem hiding this comment.
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).
7345fac to
301e7c6
Compare
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.
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.
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).