| File: Compiler\DependencyAnalysis\ReflectionVirtualInvokeMapNode.cs | Web Access |
| Project: src\runtime\src\coreclr\tools\aot\ILCompiler.Compiler\ILCompiler.Compiler.csproj (ILCompiler.Compiler) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Diagnostics; using System.Collections.Generic; using Internal.Text; using Internal.TypeSystem; using Internal.NativeFormat; using VirtualInvokeTableEntry = Internal.Runtime.VirtualInvokeTableEntry; namespace ILCompiler.DependencyAnalysis { /// <summary> /// Represents a map containing the necessary information needed to resolve /// a virtual method target called through reflection. /// </summary> internal sealed class ReflectionVirtualInvokeMapNode : ObjectNode, ISymbolDefinitionNode { private ExternalReferencesTableNode _externalReferences; public ReflectionVirtualInvokeMapNode(ExternalReferencesTableNode externalReferences) { _externalReferences = externalReferences; } public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix).Append("__VirtualInvokeMap"u8); } public int Offset => 0; public override bool IsShareable => false; public override ObjectNodeSection GetSection(NodeFactory factory) => _externalReferences.GetSection(factory); public override bool StaticDependenciesAreComputed => true; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public static bool NeedsVirtualInvokeInfo(NodeFactory factory, MethodDesc method) { if (!method.IsVirtual) return false; if (factory.DevirtualizationManager.IsEffectivelySealed(method)) return false; return true; } public static MethodDesc GetDeclaringVirtualMethodAndHierarchyDistance(MethodDesc method, out int parentHierarchyDistance) { parentHierarchyDistance = 0; MethodDesc declaringMethodForSlot = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method.GetTypicalMethodDefinition()); TypeDesc typeOfDeclaringMethodForSlot = declaringMethodForSlot.OwningType.GetTypeDefinition(); TypeDesc currentType = method.OwningType.GetTypeDefinition(); TypeDesc containingTypeOfDeclaringMethodForSlot = method.OwningType; while (typeOfDeclaringMethodForSlot != currentType) { parentHierarchyDistance++; currentType = currentType.BaseType.GetTypeDefinition(); containingTypeOfDeclaringMethodForSlot = containingTypeOfDeclaringMethodForSlot.BaseType; } if (containingTypeOfDeclaringMethodForSlot.HasInstantiation) { declaringMethodForSlot = method.Context.GetMethodForInstantiatedType( declaringMethodForSlot.GetTypicalMethodDefinition(), (InstantiatedType)containingTypeOfDeclaringMethodForSlot); } Debug.Assert(declaringMethodForSlot != null); return declaringMethodForSlot; } public static void GetVirtualInvokeMapDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) { if (NeedsVirtualInvokeInfo(factory, method)) { dependencies ??= new DependencyList(); dependencies.Add( factory.NecessaryTypeSymbol(method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific)), "Reflection virtual invoke owning type"); MethodDesc methodDefinition = method.GetMethodDefinition(); MethodDesc slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(methodDefinition); if (method.HasInstantiation) { if (method != methodDefinition) slotDefiningMethod = factory.TypeSystemContext.GetInstantiatedMethod(slotDefiningMethod, method.Instantiation); dependencies.Add(factory.GVMDependencies(slotDefiningMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)), "GVM callable reflectable method"); } else { if (!factory.VTable(slotDefiningMethod.OwningType).HasKnownVirtualMethodUse) { dependencies.Add(factory.VirtualMethodUse(slotDefiningMethod), "Reflection virtual invoke method"); } } } } public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. if (relocsOnly) return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this }); // Ensure the native layout blob has been saved factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); var writer = new NativeWriter(); var typeMapHashTable = new VertexHashtable(); Section hashTableSection = writer.NewSection(); hashTableSection.Place(typeMapHashTable); Dictionary<int, HashSet<TypeDesc>> methodsEmitted = new Dictionary<int, HashSet<TypeDesc>>(); // Get a list of all methods that have a method body and metadata from the metadata manager. foreach (var mappingEntry in factory.MetadataManager.GetMethodMapping(factory)) { Debug.Assert(!mappingEntry.Entity.IsAsyncVariant()); MethodDesc method = mappingEntry.Entity; // The current format requires us to have an MethodTable for the owning type. We might want to lift this. if (!factory.MetadataManager.TypeGeneratesEEType(method.OwningType)) continue; // We have a method body, we have a metadata token, but we can't get an invoke stub. Bail. if (!factory.MetadataManager.IsReflectionInvokable(method)) continue; // Only virtual methods are interesting if (!NeedsVirtualInvokeInfo(factory, method)) continue; // // The vtable entries for each instantiated type might not necessarily exist. // Example 1: // If there's a call to Foo<string>.Method1 and a call to Foo<int>.Method2, Foo<string> will // not have Method2 in its vtable and Foo<int> will not have Method1. // Example 2: // If there's a call to Foo<string>.Method1 and a call to Foo<object>.Method2, given that both // of these instantiations share the same canonical form, Foo<__Canon> will have both method // entries, and therefore Foo<string> and Foo<object> will have both entries too. // For this reason, the entries that we write to the map in CoreRT will be based on the canonical form // of the method's containing type instead of the open type definition. // TypeDesc containingTypeKey = method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); HashSet<TypeDesc> cache; if (!methodsEmitted.TryGetValue(mappingEntry.MetadataHandle, out cache)) methodsEmitted[mappingEntry.MetadataHandle] = cache = new HashSet<TypeDesc>(); // Only one record is needed for any instantiation. if (!cache.Add(containingTypeKey)) continue; // Grammar of an entry in the hash table: // Virtual Method uses a normal slot // TypeKey + MethodHandle + (NumberOfStepsUpParentHierarchyToType << 1) + slot // OR // Generic Virtual Method // TypeKey + MethodHandle + (NumberOfStepsUpParentHierarchyToType << 1 + 1) int parentHierarchyDistance; MethodDesc declaringMethodForSlot = GetDeclaringVirtualMethodAndHierarchyDistance(method, out parentHierarchyDistance); ISymbolNode containingTypeKeyNode = factory.NecessaryTypeSymbol(containingTypeKey); int token = factory.MetadataManager.GetMetadataHandleForMethod(factory, method.GetTypicalMethodDefinition()); Vertex vertex; if (method.HasInstantiation) { vertex = writer.GetTuple( writer.GetUnsignedConstant(_externalReferences.GetIndex(containingTypeKeyNode)), writer.GetUnsignedConstant((uint)token), writer.GetUnsignedConstant(((uint)parentHierarchyDistance << 1) + VirtualInvokeTableEntry.GenericVirtualMethod)); } else { // Get the declaring method for slot on the instantiated declaring type int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, declaringMethodForSlot, declaringMethodForSlot.OwningType, true); Debug.Assert(slot != -1); vertex = writer.GetTuple( writer.GetUnsignedConstant(_externalReferences.GetIndex(containingTypeKeyNode)), writer.GetUnsignedConstant((uint)token)); vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant((uint)parentHierarchyDistance << 1), writer.GetUnsignedConstant((uint)slot)); } int hashCode = containingTypeKey.GetHashCode(); typeMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex)); } byte[] hashTableBytes = writer.Save(); return new ObjectData(hashTableBytes, Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this }); } protected internal override int Phase => (int)ObjectNodePhase.Ordered; public override int ClassCode => (int)ObjectNodeOrder.ReflectionVirtualInvokeMapNode; } }