From 3d795161868fbcd541facd59ef2a21513fe48c47 Mon Sep 17 00:00:00 2001 From: fadimounir Date: Tue, 17 Jan 2017 12:26:32 -0800 Subject: [PATCH] GVM dependency tracking analysis and GVM table building logic: 1) GenericVirtualMethodTableNode: Node that builds the GVM hashtable blob for GVMs on classes 2) InterfaceGenericVirtualMethodTableNode: Node that builds the GVM hashtable blob for GVMs on interfaces 3) MethodLdTokenNode: Analysis node that tracks dependencies of GVMs for derived types and variant interfaces 4) TypeGVMEntriesNode: Analysis node that scans an input type and computes the list of GVM entries that would be added to the hashtables. Also used to track dependencies of GVM table entries (ex: native layout signatures of methods). Other 1) New helper method to compute whether a type has generic virtual methods --- .../TypeSystem/Common/TypeSystemHelpers.cs | 20 ++ .../ConstructedEETypeNode.cs | 14 ++ .../DependencyAnalysis/GVMDependenciesNode.cs | 182 +++++++++++++++ .../GenericVirtualMethodTableNode.cs | 151 ++++++++++++ .../InterfaceGenericVirtualMethodTableNode.cs | 220 ++++++++++++++++++ .../DependencyAnalysis/MethodCodeNode.cs | 7 + .../DependencyAnalysis/NodeFactory.cs | 22 ++ .../ReadyToRunHelperNode.cs | 10 + .../DependencyAnalysis/TypeGVMEntriesNode.cs | 112 +++++++++ .../DependencyAnalysis/VTableSliceNode.cs | 6 + .../VirtualMethodUseNode.cs | 2 +- .../src/Compiler/MetadataGeneration.cs | 20 +- .../src/Compiler/TypeExtensions.cs | 15 ++ .../src/ILCompiler.Compiler.csproj | 8 +- .../CoreTestAssembly/InterfaceArrangements.cs | 43 ++-- .../tests/InterfacesTests.cs | 52 ++++- .../TypeLoaderEnvironment.SignatureParsing.cs | 6 +- 17 files changed, 863 insertions(+), 27 deletions(-) create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs diff --git a/src/Common/src/TypeSystem/Common/TypeSystemHelpers.cs b/src/Common/src/TypeSystem/Common/TypeSystemHelpers.cs index a3ccbf9b8d4..4e7a363c3ab 100644 --- a/src/Common/src/TypeSystem/Common/TypeSystemHelpers.cs +++ b/src/Common/src/TypeSystem/Common/TypeSystemHelpers.cs @@ -301,5 +301,25 @@ public static MethodDesc InstantiateAsOpen(this MethodDesc method) return method; } + + /// + /// Scan the type and its base types for an implementation of an interface method. Returns null if no + /// implementation is found. + /// + public static MethodDesc ResolveInterfaceMethodTarget(this TypeDesc thisType, MethodDesc interfaceMethodToResolve) + { + Debug.Assert(interfaceMethodToResolve.OwningType.IsInterface); + + MethodDesc result = null; + TypeDesc currentType = thisType; + do + { + result = currentType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethodToResolve); + currentType = currentType.BaseType; + } + while (result == null && currentType != null); + + return result; + } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs index da169eabdc5..21abb42d86c 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs @@ -26,6 +26,14 @@ public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) return false; } + public override bool InterestingForDynamicDependencyAnalysis + { + get + { + return _type.IsDefType && _type.HasGenericVirtualMethod(); + } + } + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { DefType closestDefType = _type.GetClosestDefType(); @@ -89,6 +97,12 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact dependencyList.Add(factory.TypeNonGCStaticsSymbol((MetadataType)_type), "Class constructor"); } + // Generated type contains generic virtual methods that will get added to the GVM tables + if (TypeGVMEntriesNode.TypeNeedsGVMTableEntries(_type)) + { + dependencyList.Add(new DependencyListEntry(factory.TypeGVMEntries(_type), "Type with generic virtual methods")); + } + return dependencyList; } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs new file mode 100644 index 00000000000..a95fd08746e --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs @@ -0,0 +1,182 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// This analysis node is used for computing GVM dependencies for the following cases: + /// 1) Derived types where the GVM is overridden + /// 2) Variant-interfaces GVMs + /// This analysis node will ensure that the proper GVM instantiations are compiled on types. + /// + internal class GVMDependenciesNode : DependencyNodeCore + { + private MethodDesc _method; + + public GVMDependenciesNode(MethodDesc method) + { + Debug.Assert(!method.IsRuntimeDeterminedExactMethod); + Debug.Assert(method.IsVirtual && method.HasInstantiation); + _method = method; + } + + public override bool HasConditionalStaticDependencies => false; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName() => "__GVMDependenciesNode_" + NodeFactory.NameMangler.GetMangledMethodName(_method); + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return Array.Empty(); + } + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) + { + return Array.Empty(); + } + + public override bool HasDynamicDependencies + { + get + { + if (_method.IsCanonicalMethod(CanonicalFormKind.Specific)) + return false; + + if (_method.OwningType.IsCanonicalSubtype(CanonicalFormKind.Universal) && + _method.OwningType != _method.OwningType.ConvertToCanonForm(CanonicalFormKind.Universal)) + return false; + + return true; + } + } + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) + { + Debug.Assert(_method.IsVirtual && _method.HasInstantiation); + + List dynamicDependencies = new List(); + + for (int i = firstNode; i < markedNodes.Count; i++) + { + DependencyNodeCore entry = markedNodes[i]; + EETypeNode entryAsEETypeNode = entry as EETypeNode; + + if (entryAsEETypeNode == null) + continue; + + TypeDesc potentialOverrideType = entryAsEETypeNode.Type; + if (!(potentialOverrideType is DefType)) + continue; + + Debug.Assert(!potentialOverrideType.IsRuntimeDeterminedSubtype); + + if (_method.OwningType.HasSameTypeDefinition(potentialOverrideType) && potentialOverrideType.IsInterface && (potentialOverrideType != _method.OwningType)) + { + if (_method.OwningType.CanCastTo(potentialOverrideType)) + { + // Variance expansion + MethodDesc matchingMethodOnRelatedVariantMethod = potentialOverrideType.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature); + matchingMethodOnRelatedVariantMethod = _method.Context.GetInstantiatedMethod(matchingMethodOnRelatedVariantMethod, _method.Instantiation); + dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(matchingMethodOnRelatedVariantMethod), null, "GVM Variant Interface dependency")); + } + } + + // If this is an interface gvm, look for types that implement the interface + // and other instantantiations that have the same canonical form. + // This ensure the various slot numbers remain equivalent across all types where there is an equivalence + // relationship in the vtable. + if (_method.OwningType.IsInterface) + { + if (potentialOverrideType.IsInterface) + continue; + + foreach (DefType interfaceImpl in potentialOverrideType.RuntimeInterfaces) + { + if (interfaceImpl.ConvertToCanonForm(CanonicalFormKind.Specific) == _method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific)) + { + // Find if the type implements this method. (Note, do this comparision against the generic definition of the method, not the + // specific method instantiation that is "method" + MethodDesc genericDefinition = interfaceImpl.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature); + MethodDesc slotDecl = potentialOverrideType.ResolveInterfaceMethodTarget(genericDefinition); + if (slotDecl != null) + CreateDependencyForMethodSlotAndInstantiation(slotDecl, dynamicDependencies, factory); + } + } + } + else + { + // TODO: Ensure GVM Canon Target + + TypeDesc overrideTypeCanonCur = potentialOverrideType; + TypeDesc methodCanonContainingType = _method.OwningType; + while (overrideTypeCanonCur != null) + { + if (overrideTypeCanonCur.ConvertToCanonForm(CanonicalFormKind.Specific) == methodCanonContainingType.ConvertToCanonForm(CanonicalFormKind.Specific)) + { + MethodDesc methodDefInDerivedType = potentialOverrideType.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature); + if(methodDefInDerivedType != null) + CreateDependencyForMethodSlotAndInstantiation(methodDefInDerivedType, dynamicDependencies, factory); + + MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(_method); + if (slotDecl != null) + CreateDependencyForMethodSlotAndInstantiation(slotDecl.GetMethodDefinition(), dynamicDependencies, factory); + } + + overrideTypeCanonCur = overrideTypeCanonCur.BaseType; + } + } + } + return dynamicDependencies; + } + + private void CreateDependencyForMethodSlotAndInstantiation(MethodDesc methodDef, List dynamicDependencies, NodeFactory factory) + { + Debug.Assert(methodDef != null); + Debug.Assert(!methodDef.Signature.IsStatic); + + if (methodDef.IsAbstract) + return; + + MethodDesc derivedMethodInstantiation = _method.Context.GetInstantiatedMethod(methodDef, _method.Instantiation); + + // Universal canonical instantiations should be entirely universal canon + if (derivedMethodInstantiation.IsCanonicalMethod(CanonicalFormKind.Universal)) + { + derivedMethodInstantiation = derivedMethodInstantiation.GetCanonMethodTarget(CanonicalFormKind.Universal); + } + + // TODO: verify for invalid instantiations, like List? + bool validInstantiation = + derivedMethodInstantiation.IsSharedByGenericInstantiations || // Non-exact methods are always valid instantiations (always pass constraints check) + derivedMethodInstantiation.CheckConstraints(); // Verify that the instantiation does not violate constraints + + if (validInstantiation) + { + MethodDesc canonMethodTarget = derivedMethodInstantiation.GetCanonMethodTarget(CanonicalFormKind.Specific); + + bool getUnboxingStub = (derivedMethodInstantiation.OwningType.IsValueType || derivedMethodInstantiation.OwningType.IsEnum); + dynamicDependencies.Add(new CombinedDependencyListEntry(factory.MethodEntrypoint(canonMethodTarget, getUnboxingStub), null, "DerivedMethodInstantiation")); + + if (canonMethodTarget != derivedMethodInstantiation) + { + // Dependency includes the generic method dictionary of the instantiation + // TODO: detect large recursive generics and fallback to USG templates + Debug.Assert(!derivedMethodInstantiation.IsCanonicalMethod(CanonicalFormKind.Any)); + dynamicDependencies.Add(new CombinedDependencyListEntry(factory.MethodGenericDictionary(derivedMethodInstantiation), null, "DerivedMethodInstantiation dictionary")); + } + } + else + { + // TODO: universal generics + } + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs new file mode 100644 index 00000000000..c85635d52db --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs @@ -0,0 +1,151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Diagnostics; +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a map between reflection metadata and generated method bodies. + /// + internal sealed class GenericVirtualMethodTableNode : ObjectNode, ISymbolNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + private Dictionary> _gvmImplemenations; + + public GenericVirtualMethodTableNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__gvm_table_End", true); + _externalReferences = externalReferences; + _gvmImplemenations = new Dictionary>(); + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__gvm_table"); + } + + public ISymbolNode EndSymbol => _endSymbol; + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => ObjectNodeSection.DataSection; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName() => this.GetMangledName(); + + /// + /// Helper method to compute the dependencies that would be needed by a hashtable entry for a GVM call. + /// This helper is used by the TypeGVMEntriesNode, which is used by the dependency analysis to compute the + /// GVM hashtable entries for the compiled types. + /// The dependencies returned from this function will be reported as static dependencies of the TypeGVMEntriesNode, + /// which we create for each type that has generic virtual methods. + /// + public static DependencyList GetGenericVirtualMethodImplementationDependencies(NodeFactory factory, MethodDesc callingMethod, MethodDesc implementationMethod) + { + Debug.Assert(!callingMethod.OwningType.IsInterface); + + DependencyList dependencyNodes = new DependencyList(); + + // Compute the open method signatures + MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition(); + MethodDesc openImplementationMethod = implementationMethod.GetTypicalMethodDefinition(); + + var openCallingMethodNameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(openCallingMethod); + var openImplementationMethodNameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(openImplementationMethod); + + dependencyNodes.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(openCallingMethodNameAndSig), "gvm table calling method signature")); + dependencyNodes.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(openImplementationMethodNameAndSig), "gvm table implementation method signature")); + + return dependencyNodes; + } + + private void AddGenericVirtualMethodImplementation(NodeFactory factory, MethodDesc callingMethod, MethodDesc implementationMethod) + { + Debug.Assert(!callingMethod.OwningType.IsInterface); + + // Compute the open method signatures + MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition(); + MethodDesc openImplementationMethod = implementationMethod.GetTypicalMethodDefinition(); + + // Insert open method signatures into the GVM map + if (!_gvmImplemenations.ContainsKey(openCallingMethod)) + _gvmImplemenations[openCallingMethod] = new Dictionary(); + + _gvmImplemenations[openCallingMethod][openImplementationMethod.OwningType] = openImplementationMethod; + } + + 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(), Array.Empty(), 1, new ISymbolNode[] { this }); + + // Build the GVM table entries from the list of interesting GVMTableEntryNodes + foreach (var interestingEntry in factory.MetadataManager.GetTypeGVMEntries()) + { + foreach (var typeGVMEntryInfo in interestingEntry.ScanForGenericVirtualMethodEntries()) + { + AddGenericVirtualMethodImplementation(factory, typeGVMEntryInfo.CallingMethod, typeGVMEntryInfo.ImplementationMethod); + } + } + + // Ensure the native layout blob has been saved + factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); + + NativeWriter nativeFormatWriter = new NativeWriter(); + VertexHashtable gvmHashtable = new VertexHashtable(); + + Section gvmHashtableSection = nativeFormatWriter.NewSection(); + gvmHashtableSection.Place(gvmHashtable); + + // Emit the GVM target information entries + foreach (var gvmEntry in _gvmImplemenations) + { + Debug.Assert(!gvmEntry.Key.OwningType.IsInterface); + + foreach (var implementationEntry in gvmEntry.Value) + { + MethodDesc callingMethod = gvmEntry.Key; + TypeDesc implementationType = implementationEntry.Key; + MethodDesc implementationMethod = implementationEntry.Value; + + uint callingTypeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(callingMethod.OwningType)); + Vertex vertex = nativeFormatWriter.GetUnsignedConstant(callingTypeId); + + uint targetTypeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(implementationType)); + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant(targetTypeId)); + + var nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(callingMethod)); + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)nameAndSig.SavedVertex.VertexOffset)); + + nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(implementationMethod)); + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)nameAndSig.SavedVertex.VertexOffset)); + + int hashCode = callingMethod.OwningType.GetHashCode(); + hashCode = ((hashCode << 13) ^ hashCode) ^ implementationType.GetHashCode(); + + gvmHashtable.Append((uint)hashCode, gvmHashtableSection.Place(vertex)); + } + } + + // Zero out the dictionary so that we AV if someone tries to insert after we're done. + _gvmImplemenations = null; + + MemoryStream stream = new MemoryStream(); + nativeFormatWriter.Save(stream); + byte[] streamBytes = stream.ToArray(); + + _endSymbol.SetSymbolOffset(streamBytes.Length); + + return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolNode[] { this, _endSymbol }); + } + } +} \ No newline at end of file diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs new file mode 100644 index 00000000000..1635980a205 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs @@ -0,0 +1,220 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Diagnostics; +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a map between reflection metadata and generated method bodies. + /// + internal sealed class InterfaceGenericVirtualMethodTableNode : ObjectNode, ISymbolNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + private Dictionary> _interfaceGvmSlots; + private Dictionary>> _interfaceImpls; + + public InterfaceGenericVirtualMethodTableNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__interface_gvm_table_End", true); + _externalReferences = externalReferences; + _interfaceGvmSlots = new Dictionary>(); + _interfaceImpls = new Dictionary>>(); + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__interface_gvm_table"); + } + public ISymbolNode EndSymbol => _endSymbol; + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => ObjectNodeSection.DataSection; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName() => this.GetMangledName(); + + /// + /// Helper method to compute the dependencies that would be needed by a hashtable entry for an interface GVM call. + /// This helper is used by the TypeGVMEntriesNode, which is used by the dependency analysis to compute the + /// GVM hashtable entries for the compiled types. + /// The dependencies returned from this function will be reported as static dependencies of the TypeGVMEntriesNode, + /// which we create for each type that has generic virtual methods. + /// + public static DependencyList GetGenericVirtualMethodImplementationDependencies(NodeFactory factory, MethodDesc callingMethod, TypeDesc implementationType, MethodDesc implementationMethod) + { + Debug.Assert(callingMethod.OwningType.IsInterface); + + DependencyList dependencyNodes = new DependencyList(); + + // Compute the open method signatures + MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition(); + MethodDesc openImplementationMethod = implementationMethod.GetTypicalMethodDefinition(); + TypeDesc openImplementationType = implementationType.GetTypeDefinition(); + + var openCallingMethodNameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(openCallingMethod); + var openImplementationMethodNameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(openImplementationMethod); + + dependencyNodes.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(openCallingMethodNameAndSig), "interface gvm table calling method signature")); + dependencyNodes.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(openImplementationMethodNameAndSig), "interface gvm table implementation method signature")); + + if (!openImplementationType.IsInterface) + { + for(int index = 0; index < implementationType.RuntimeInterfaces.Length; index++) + { + if (implementationType.RuntimeInterfaces[index] == callingMethod.OwningType) + { + TypeDesc currentInterface = openImplementationType.RuntimeInterfaces[index]; + var currentInterfaceSignature = factory.NativeLayout.TypeSignatureVertex(currentInterface); + dependencyNodes.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(currentInterfaceSignature), "interface gvm table interface signature")); + } + } + } + + return dependencyNodes; + } + + private void AddGenericVirtualMethodImplementation(NodeFactory factory, MethodDesc callingMethod, TypeDesc implementationType, MethodDesc implementationMethod) + { + Debug.Assert(callingMethod.OwningType.IsInterface); + + // Compute the open method signatures + MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition(); + MethodDesc openImplementationMethod = implementationMethod.GetTypicalMethodDefinition(); + TypeDesc openImplementationType = implementationType.GetTypeDefinition(); + + // Add the entry to the interface GVM slots mapping table + if (!_interfaceGvmSlots.ContainsKey(openCallingMethod)) + _interfaceGvmSlots[openCallingMethod] = new HashSet(); + _interfaceGvmSlots[openCallingMethod].Add(openImplementationMethod); + + // If the implementation method is implementing some interface method, compute which + // interface explicitly implemented on the type that the current method implements an interface method for. + // We need this because at runtime, the interfaces explicitly implemented on the type will have + // runtime-determined signatures that we can use to make generic substitutions and check for interface matching. + if (!openImplementationType.IsInterface) + { + if (!_interfaceImpls.ContainsKey(openImplementationMethod)) + _interfaceImpls[openImplementationMethod] = new Dictionary>(); + if (!_interfaceImpls[openImplementationMethod].ContainsKey(openImplementationType)) + _interfaceImpls[openImplementationMethod][openImplementationType] = new HashSet(); + + int numIfacesAdded = 0; + for (int index = 0; index < implementationType.RuntimeInterfaces.Length; index++) + { + if (implementationType.RuntimeInterfaces[index] == callingMethod.OwningType) + { + _interfaceImpls[openImplementationMethod][openImplementationType].Add(index); + numIfacesAdded++; + } + } + + Debug.Assert(numIfacesAdded > 0); + } + } + + 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(), Array.Empty(), 1, new ISymbolNode[] { this }); + + // Build the GVM table entries from the list of interesting GVMTableEntryNodes + foreach (var interestingEntry in factory.MetadataManager.GetTypeGVMEntries()) + { + foreach (var typeGVMEntryInfo in interestingEntry.ScanForInterfaceGenericVirtualMethodEntries()) + { + AddGenericVirtualMethodImplementation(factory, typeGVMEntryInfo.CallingMethod, typeGVMEntryInfo.ImplementationType, typeGVMEntryInfo.ImplementationMethod); + } + } + + // Ensure the native layout blob has been saved + factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); + + NativeWriter nativeFormatWriter = new NativeWriter(); + VertexHashtable gvmHashtable = new VertexHashtable(); + + Section gvmHashtableSection = nativeFormatWriter.NewSection(); + gvmHashtableSection.Place(gvmHashtable); + + // Emit the interface slot resolution entries + foreach (var gvmEntry in _interfaceGvmSlots) + { + Debug.Assert(gvmEntry.Key.OwningType.IsInterface); + + MethodDesc callingMethod = gvmEntry.Key; + + // Emit the method signature and containing type of the current interface method + uint typeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(callingMethod.OwningType)); + var nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(callingMethod)); + Vertex vertex = nativeFormatWriter.GetTuple( + nativeFormatWriter.GetUnsignedConstant(typeId), + nativeFormatWriter.GetUnsignedConstant((uint)nameAndSig.SavedVertex.VertexOffset)); + + // Emit the method name / sig and containing type of each GVM target method for the current interface method entry + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)gvmEntry.Value.Count)); + foreach (MethodDesc implementationMethod in gvmEntry.Value) + { + nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(implementationMethod)); + typeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(implementationMethod.OwningType)); + vertex = nativeFormatWriter.GetTuple( + vertex, + nativeFormatWriter.GetUnsignedConstant((uint)nameAndSig.SavedVertex.VertexOffset), + nativeFormatWriter.GetUnsignedConstant(typeId)); + + // Emit the interface GVM slot details for each type that implements the interface methods + { + Debug.Assert(_interfaceImpls.ContainsKey(implementationMethod)); + + var ifaceImpls = _interfaceImpls[implementationMethod]; + + // First, emit how many types have method implementations for this interface method entry + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)ifaceImpls.Count)); + + // Emit each type that implements the interface method, and the interface signatures for the interfaces implemented by the type + foreach (var currentImpl in ifaceImpls) + { + TypeDesc implementationType = currentImpl.Key; + + typeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(implementationType)); + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant(typeId)); + + // Emit information on which interfaces the current method entry provides implementations for + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)currentImpl.Value.Count)); + foreach (var ifaceId in currentImpl.Value) + { + // Emit the signature of the current interface implemented by the method + Debug.Assert(((uint)ifaceId) < implementationType.RuntimeInterfaces.Length); + TypeDesc currentInterface = implementationType.RuntimeInterfaces[ifaceId]; + var typeSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.TypeSignatureVertex(currentInterface)); + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)typeSig.SavedVertex.VertexOffset)); + } + } + } + } + + int hashCode = callingMethod.OwningType.GetHashCode(); + gvmHashtable.Append((uint)hashCode, gvmHashtableSection.Place(vertex)); + } + + // Zero out the dictionary so that we AV if someone tries to insert after we're done. + _interfaceGvmSlots = null; + + MemoryStream stream = new MemoryStream(); + nativeFormatWriter.Save(stream); + byte[] streamBytes = stream.ToArray(); + + _endSymbol.SetSymbolOffset(streamBytes.Length); + + return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolNode[] { this, _endSymbol }); + } + } +} \ No newline at end of file diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs index fb45db873af..64c691e864e 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs @@ -107,6 +107,13 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact dependencies = dependencies ?? new DependencyList(); dependencies.AddRange(exactMethodInstantiationDependencies); } + + if (_method.IsVirtual) + { + // Generic virtual methods dependency tracking + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(new DependencyListEntry(factory.GVMDependencies(_method), "GVM Dependencies Support")); + } } return dependencies; diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index 083017c3ab5..42d875fecf9 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -192,6 +192,16 @@ private void CreateNodeCaches() return new FatFunctionPointerNode(method); }); + _gvmDependenciesNode = new NodeCache(method => + { + return new GVMDependenciesNode(method); + }); + + _gvmTableEntries = new NodeCache(type => + { + return new TypeGVMEntriesNode(type); + }); + _shadowConcreteMethods = new NodeCache(method => { return new ShadowConcreteMethodNode(method, @@ -520,6 +530,18 @@ public IMethodNode FatFunctionPointer(MethodDesc method) return _fatFunctionPointers.GetOrAdd(method); } + private NodeCache _gvmDependenciesNode; + internal GVMDependenciesNode GVMDependencies(MethodDesc method) + { + return _gvmDependenciesNode.GetOrAdd(method); + } + + private NodeCache _gvmTableEntries; + internal TypeGVMEntriesNode TypeGVMEntries(TypeDesc type) + { + return _gvmTableEntries.GetOrAdd(type); + } + private NodeCache _shadowConcreteMethods; public IMethodNode ShadowConcreteMethod(MethodDesc method) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs index 477eef6dd80..d39334dd340 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs @@ -156,6 +156,16 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact dependencyList.Add(factory.VirtualMethodUse((MethodDesc)_target), "ReadyToRun Virtual Method Address Load"); return dependencyList; } + else if (_id == ReadyToRunHelperId.ResolveGenericVirtualMethod) + { + MethodDesc method = _target as MethodDesc; + Debug.Assert(method != null && method.HasInstantiation && method.IsVirtual); + + // GVM dependency tracking + DependencyList dependencyList = new DependencyList(); + dependencyList.Add(new DependencyListEntry(factory.GVMDependencies(method), "R2R GVM dependency tracking")); + return dependencyList; + } else { return null; diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs new file mode 100644 index 00000000000..836783e56be --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs @@ -0,0 +1,112 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// This node is used for GVM dependency analysis and GVM tables building. Given an input + /// type, this node can scan the type for a list of GVM table entries, and compute their dependencies. + /// + internal sealed class TypeGVMEntriesNode : DependencyNodeCore + { + internal class TypeGVMEntryInfo + { + public TypeGVMEntryInfo(MethodDesc callingMethod, MethodDesc implementationMethod, TypeDesc implementationType) + { + CallingMethod = callingMethod; + ImplementationMethod = implementationMethod; + ImplementationType = implementationType; + } + public MethodDesc CallingMethod { get; private set; } + public MethodDesc ImplementationMethod { get; private set; } + public TypeDesc ImplementationType { get; private set; } + } + + private TypeDesc _associatedType; + private DependencyList _staticDependencies; + + public TypeGVMEntriesNode(TypeDesc associatedType) + { + Debug.Assert(!associatedType.IsRuntimeDeterminedSubtype); + Debug.Assert(TypeNeedsGVMTableEntries(associatedType)); + _associatedType = associatedType; + } + + public override bool HasConditionalStaticDependencies => false; + public override bool HasDynamicDependencies => false; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName() => "__TypeGVMEntriesNode_" + NodeFactory.NameMangler.GetMangledTypeName(_associatedType); + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) + { + return Array.Empty(); + } + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) + { + return Array.Empty(); + } + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + if (_staticDependencies == null) + { + _staticDependencies = new DependencyList(); + + foreach(var entry in ScanForGenericVirtualMethodEntries()) + _staticDependencies.AddRange(GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(context, entry.CallingMethod, entry.ImplementationMethod)); + + foreach (var entry in ScanForInterfaceGenericVirtualMethodEntries()) + _staticDependencies.AddRange(InterfaceGenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(context, entry.CallingMethod, entry.ImplementationType, entry.ImplementationMethod)); + } + + return _staticDependencies; + } + + public static bool TypeNeedsGVMTableEntries(TypeDesc type) + { + // Only non-interface deftypes can have entries for their GVMs in the GVM hashtables. + // Interface GVM entries are computed for types that implemenent the interface (not for the interface on its own) + if(!type.IsDefType || type.IsInterface) + return false; + + return type.HasGenericVirtualMethod(); + } + + public IEnumerable ScanForGenericVirtualMethodEntries() + { + foreach (var method in _associatedType.GetMethods()) + { + if (!method.IsVirtual || !method.HasInstantiation) + continue; + + MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); + Debug.Assert(slotDecl != null); + yield return new TypeGVMEntryInfo(slotDecl, method, null); + } + } + + public IEnumerable ScanForInterfaceGenericVirtualMethodEntries() + { + foreach (var iface in _associatedType.RuntimeInterfaces) + { + foreach (var method in iface.GetMethods()) + { + if (!method.HasInstantiation || method.Signature.IsStatic) + continue; + + MethodDesc slotDecl = _associatedType.ResolveInterfaceMethodTarget(method); + if (slotDecl != null) + yield return new TypeGVMEntryInfo(method, slotDecl, _associatedType); + } + } + } + } +} \ No newline at end of file diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs index 666b8569aca..f82e6f4732e 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs @@ -60,6 +60,10 @@ public EagerlyBuiltVTableSliceNode(TypeDesc type) if (!method.IsVirtual) continue; + // GVMs are not emitted in the type's vtable. + if (method.HasInstantiation) + continue; + slots.Add(method); } @@ -121,6 +125,8 @@ public override IReadOnlyList Slots public void AddEntry(NodeFactory factory, MethodDesc virtualMethod) { + // GVMs are not emitted in the type's vtable. + Debug.Assert(!virtualMethod.HasInstantiation); Debug.Assert(virtualMethod.IsVirtual); #if DEBUG Debug.Assert(!_slotsCommitted); diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs index 259207ec60f..7dff15fe339 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs @@ -32,7 +32,7 @@ protected override void OnMarked(NodeFactory factory) // If the VTable slice is getting built on demand, the fact that the virtual method is used means // that the slot is used. var lazyVTableSlice = factory.VTable(_decl.OwningType) as LazilyBuiltVTableSliceNode; - if (lazyVTableSlice != null) + if (lazyVTableSlice != null && !_decl.HasInstantiation) lazyVTableSlice.AddEntry(factory, _decl); } diff --git a/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs b/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs index ff39e1b514c..f1da9479109 100644 --- a/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs +++ b/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs @@ -43,6 +43,7 @@ public class MetadataGeneration private HashSet _typesWithEETypesGenerated = new HashSet(); private HashSet _methodDefinitionsGenerated = new HashSet(); private HashSet _methodsGenerated = new HashSet(); + private List _typeGVMEntries = new List(); private Dictionary _dynamicInvokeThunks = new Dictionary(); @@ -104,7 +105,13 @@ public void AddToReadyToRunHeader(ReadyToRunHeaderNode header) var genericsHashtable = new GenericsHashtableNode(nativeReferencesTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericsHashtable), genericsHashtable, genericsHashtable, genericsHashtable.EndSymbol); - // This one should go last + var genericVirtualMethodTableNode = new GenericVirtualMethodTableNode(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericVirtualMethodTable), genericVirtualMethodTableNode, genericVirtualMethodTableNode, genericVirtualMethodTableNode.EndSymbol); + + var interfaceGenericVirtualMethodTableNode = new InterfaceGenericVirtualMethodTableNode(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.InterfaceGenericVirtualMethodTable), interfaceGenericVirtualMethodTableNode, interfaceGenericVirtualMethodTableNode, interfaceGenericVirtualMethodTableNode.EndSymbol); + + // The external references tables should go last header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode, commonFixupsTableNode, commonFixupsTableNode.EndSymbol); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeReferences), nativeReferencesTableNode, nativeReferencesTableNode, nativeReferencesTableNode.EndSymbol); } @@ -143,6 +150,12 @@ private void Graph_NewMarkedNode(DependencyNodeCore obj) { _cctorContextsGenerated.Add(nonGcStaticSectionNode); } + + var gvmEntryNode = obj as TypeGVMEntriesNode; + if (gvmEntryNode != null) + { + _typeGVMEntries.Add(gvmEntryNode); + } } public bool HasReflectionInvokeStub(MethodDesc method) @@ -384,6 +397,11 @@ internal IEnumerable GetArrayTypeMapping() return _arrayTypesGenerated; } + internal IEnumerable GetTypeGVMEntries() + { + return _typeGVMEntries; + } + internal IEnumerable GetCompiledMethods() { return _methodsGenerated; diff --git a/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs b/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs index 93286b1cdf9..fc6c804c365 100644 --- a/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs +++ b/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs @@ -84,5 +84,20 @@ public static bool AcquiresInstMethodTableFromThis(this MethodDesc method) !method.Signature.IsStatic && !method.ImplementationType.IsValueType; } + + + /// + /// Gets a value indicating whether this type has any generic virtual methods. + /// + public static bool HasGenericVirtualMethod(this TypeDesc type) + { + foreach (var method in type.GetAllMethods()) + { + if (method.IsVirtual && method.HasInstantiation) + return true; + } + + return false; + } } } diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 2330e0a02f3..71e22fd64b5 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -102,13 +102,17 @@ + + + + + - @@ -131,7 +135,7 @@ - + diff --git a/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InterfaceArrangements.cs b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InterfaceArrangements.cs index e6118fe85b5..c92aafdeef6 100644 --- a/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InterfaceArrangements.cs +++ b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InterfaceArrangements.cs @@ -7,32 +7,39 @@ namespace InterfaceArrangements { - interface I1 - { - } + interface I1 { } - interface I2 : I1 - { - } + interface I2 : I1 { } - interface IGen1 - { + interface IGen1 { } - } + class NoInterfaces { } + + class OneInterface : I1 { } + + class Base : IGen1, I1 { } - class NoInterfaces - {} + class Mid : Base, IGen1 { } - class OneInterface : I1 - { } + class DerivedFromMid : Mid, IGen1 { } - class Base : IGen1, I1 + interface IFoo { + void IMethod(); } - class Mid : Base, IGen1 - { } + class Foo : IFoo, IFoo + { + public virtual void IMethod() { } + } - class DerivedFromMid : Mid, IGen1 - { } + class DerivedFromFoo : Foo, IFoo, IFoo + { + void IFoo.IMethod() { } + } + + class SuperDerivedFromFoo : DerivedFromFoo, IFoo, IFoo + { + void IFoo.IMethod() { } + } } diff --git a/src/ILCompiler.TypeSystem/tests/InterfacesTests.cs b/src/ILCompiler.TypeSystem/tests/InterfacesTests.cs index 62711b5d962..7f741fabf0e 100644 --- a/src/ILCompiler.TypeSystem/tests/InterfacesTests.cs +++ b/src/ILCompiler.TypeSystem/tests/InterfacesTests.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System; - +using System.Linq; using Internal.TypeSystem; using Xunit; @@ -137,5 +137,55 @@ public void TestPointerArrayInterfaces() // Pointer arrays should have the same set of interfaces as System.Array Assert.Equal(systemArrayType.RuntimeInterfaces, intPointerArray.RuntimeInterfaces); } + + [Fact] + public void TestInterafaceMethodResolution() + { + MetadataType fooType = _testModule.GetType("InterfaceArrangements", "Foo"); + MetadataType derivedType = _testModule.GetType("InterfaceArrangements", "DerivedFromFoo"); + MetadataType superDerivedType = _testModule.GetType("InterfaceArrangements", "SuperDerivedFromFoo"); + + MetadataType ifooOfInt = _testModule.GetType("InterfaceArrangements", "IFoo`1").MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Int32)); + MetadataType ifooOfString = _testModule.GetType("InterfaceArrangements", "IFoo`1").MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.String)); + + MethodDesc ifooOfIntMethod = ifooOfInt.GetMethods().Where(m => m.Name == "IMethod").Single(); + MethodDesc ifooOfStringMethod = ifooOfString.GetMethods().Where(m => m.Name == "IMethod").Single(); + + MethodDesc result; + + // Resolve on type Foo + { + result = fooType.ResolveInterfaceMethodTarget(ifooOfIntMethod); + Assert.NotNull(result); + Assert.Equal(result.OwningType, fooType); + + result = fooType.ResolveInterfaceMethodTarget(ifooOfStringMethod); + Assert.NotNull(result); + Assert.Equal(result.OwningType, fooType); + } + + // Resolve on type DerivedFromFoo + { + result = derivedType.ResolveInterfaceMethodTarget(ifooOfIntMethod); + Assert.NotNull(result); + Assert.Equal(result.OwningType, fooType); + + result = derivedType.ResolveInterfaceMethodTarget(ifooOfStringMethod); + Assert.NotNull(result); + Assert.Equal(result.OwningType, derivedType); + } + + + // Resolve on type SuperDerivedFromFoo + { + result = superDerivedType.ResolveInterfaceMethodTarget(ifooOfIntMethod); + Assert.NotNull(result); + Assert.Equal(result.OwningType, superDerivedType); + + result = superDerivedType.ResolveInterfaceMethodTarget(ifooOfStringMethod); + Assert.NotNull(result); + Assert.Equal(result.OwningType, derivedType); + } + } } } diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs index aafd0ee2675..8ceef7f4cbe 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs @@ -91,8 +91,7 @@ public bool TryGetMethodNameAndSignatureFromNativeLayoutSignature(RuntimeSignatu nameAndSignature = null; NativeReader reader = GetNativeLayoutInfoReader(signature.ModuleHandle); - uint offset = signature.NativeLayoutOffset; - NativeParser parser = new NativeParser(reader, offset); + NativeParser parser = new NativeParser(reader, signature.NativeLayoutOffset); if (parser.IsNull) return false; @@ -109,8 +108,7 @@ public bool TryGetMethodNameAndSignaturePointersFromNativeLayoutSignature(IntPtr methodSig = default(RuntimeSignature); NativeReader reader = GetNativeLayoutInfoReader(module); - uint offset = methodNameAndSigToken; - NativeParser parser = new NativeParser(reader, offset); + NativeParser parser = new NativeParser(reader, methodNameAndSigToken); if (parser.IsNull) return false;