|
// 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 slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method);
if (method.HasInstantiation)
{
// FindSlotDefiningMethod might uninstantiate. We might want to fix the method not to do that.
if (slotDefiningMethod.IsMethodDefinition)
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;
}
}
|