diff options
Diffstat (limited to 'src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs')
-rw-r--r-- | src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs | 97 |
1 files changed, 82 insertions, 15 deletions
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs index e2f6a6535..2da7c8d02 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs @@ -15,8 +15,14 @@ namespace ILCompiler.DependencyAnalysis { TypeDesc _type; - public InterfaceDispatchMapNode(TypeDesc type) + public InterfaceDispatchMapNode(NodeFactory factory, TypeDesc type) { + // Multidimensional arrays should not get a sealed vtable or a dispatch map. Runtime should use the + // sealed vtable and dispatch map of the System.Array basetype instead. + // Pointer arrays also follow the same path + Debug.Assert(!type.IsArrayTypeWithoutGenericInterfaces()); + Debug.Assert(MightHaveInterfaceDispatchMap(type, factory)); + _type = type; } @@ -57,14 +63,71 @@ namespace ILCompiler.DependencyAnalysis return result; } + /// <summary> + /// Gets a value indicating whether '<paramref name="type"/>' might have a non-empty dispatch map. + /// Note that this is only an approximation because we might not be able to take into account + /// whether the interface methods are actually used. + /// </summary> + public static bool MightHaveInterfaceDispatchMap(TypeDesc type, NodeFactory factory) + { + if (type.IsArrayTypeWithoutGenericInterfaces()) + return false; + + if (!type.IsArray && !type.IsDefType) + return false; + + TypeDesc declType = type.GetClosestDefType(); + + for (int interfaceIndex = 0; interfaceIndex < declType.RuntimeInterfaces.Length; interfaceIndex++) + { + DefType interfaceType = declType.RuntimeInterfaces[interfaceIndex]; + InstantiatedType interfaceOnDefinitionType = interfaceType.IsTypeDefinition ? + null : + (InstantiatedType)declType.GetTypeDefinition().RuntimeInterfaces[interfaceIndex]; + + IEnumerable<MethodDesc> slots; + + // If the vtable has fixed slots, we can query it directly. + // If it's a lazily built vtable, we might not be able to query slots + // just yet, so approximate by looking at all methods. + VTableSliceNode vtableSlice = factory.VTable(interfaceType); + if (vtableSlice.HasFixedSlots) + slots = vtableSlice.Slots; + else + slots = interfaceType.GetAllMethods(); + + foreach (MethodDesc slotMethod in slots) + { + MethodDesc declMethod = slotMethod; + if (interfaceOnDefinitionType != null) + declMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(declMethod.GetTypicalMethodDefinition(), interfaceOnDefinitionType); + + if (declMethod.Signature.IsStatic) + continue; + + var implMethod = declType.GetTypeDefinition().ResolveInterfaceMethodToVirtualMethodOnType(declMethod); + if (implMethod != null) + return true; + } + } + + return false; + } + void EmitDispatchMap(ref ObjectDataBuilder builder, NodeFactory factory) { var entryCountReservation = builder.ReserveInt(); int entryCount = 0; - - for (int interfaceIndex = 0; interfaceIndex < _type.RuntimeInterfaces.Length; interfaceIndex++) + + TypeDesc declType = _type.GetClosestDefType(); + + // Catch any runtime interface collapsing. We shouldn't have any + Debug.Assert(declType.RuntimeInterfaces.Length == declType.GetTypeDefinition().RuntimeInterfaces.Length); + + for (int interfaceIndex = 0; interfaceIndex < declType.RuntimeInterfaces.Length; interfaceIndex++) { - var interfaceType = _type.RuntimeInterfaces[interfaceIndex]; + var interfaceType = declType.RuntimeInterfaces[interfaceIndex]; + var interfaceDefinitionType = declType.GetTypeDefinition().RuntimeInterfaces[interfaceIndex]; Debug.Assert(interfaceType.IsInterface); IReadOnlyList<MethodDesc> virtualSlots = factory.VTable(interfaceType).Slots; @@ -72,15 +135,26 @@ namespace ILCompiler.DependencyAnalysis for (int interfaceMethodSlot = 0; interfaceMethodSlot < virtualSlots.Count; interfaceMethodSlot++) { MethodDesc declMethod = virtualSlots[interfaceMethodSlot]; - var implMethod = _type.GetClosestDefType().ResolveInterfaceMethodToVirtualMethodOnType(declMethod); + if(!interfaceType.IsTypeDefinition) + declMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(declMethod.GetTypicalMethodDefinition(), (InstantiatedType)interfaceDefinitionType); + + var implMethod = declType.GetTypeDefinition().ResolveInterfaceMethodToVirtualMethodOnType(declMethod); // Interface methods first implemented by a base type in the hierarchy will return null for the implMethod (runtime interface // dispatch will walk the inheritance chain). if (implMethod != null) { + TypeDesc implType = declType; + while (!implType.HasSameTypeDefinition(implMethod.OwningType)) + implType = implType.BaseType; + + MethodDesc targetMethod = implMethod; + if (!implType.IsTypeDefinition) + targetMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(implMethod.GetTypicalMethodDefinition(), (InstantiatedType)implType); + builder.EmitShort(checked((short)interfaceIndex)); builder.EmitShort(checked((short)(interfaceMethodSlot + (interfaceType.HasGenericDictionarySlot() ? 1 : 0)))); - builder.EmitShort(checked((short)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, implMethod))); + builder.EmitShort(checked((short)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, declType))); entryCount++; } } @@ -103,18 +177,11 @@ namespace ILCompiler.DependencyAnalysis return objData.ToObjectData(); } - protected internal override int ClassCode => 848664602; + public override int ClassCode => 848664602; - protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { return comparer.Compare(_type, ((InterfaceDispatchMapNode)other)._type); } - - int ISortableSymbolNode.ClassCode => ClassCode; - - int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) - { - return CompareToImpl((ObjectNode)other, comparer); - } } } |