From fb92fc21e193943daf9ba16fd702b732df5de13d Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 5 Oct 2015 17:43:33 -0700 Subject: [PATCH 1/2] Enhance virtual function resolution logic to be roughly correct. - Add new api surface to MetadataType to allow MethodImpls to be queried - Add new TypeSystemHelpers to allow finding of functions in similar type hierarchies (that only differ by instantiation - Update the compilation logic to use new virtual function discovery logic - Implement a first implementation of non-vtable based virtual function discovery. This implementation has been simulated in our pre-existing type system to be very similar to vtable based function discovery. --- src/ILToNative/src/Compiler/Compilation.cs | 14 +- src/ILToNative/src/ILToNative.csproj | 6 + src/NuGet.config | 9 +- .../src/Common/TypeDesc.MethodImpls.cs | 82 ++++ .../src/Common/TypeSystemHelpers.cs | 53 +++ .../src/Common/VirtualFunctionResolution.cs | 446 ++++++++++++++++++ src/TypeSystem/src/Ecma/EcmaType.cs | 107 +++++ 7 files changed, 699 insertions(+), 18 deletions(-) create mode 100644 src/TypeSystem/src/Common/TypeDesc.MethodImpls.cs create mode 100644 src/TypeSystem/src/Common/VirtualFunctionResolution.cs diff --git a/src/ILToNative/src/Compiler/Compilation.cs b/src/ILToNative/src/Compiler/Compilation.cs index 3e65a7a3964..18bb0486df9 100644 --- a/src/ILToNative/src/Compiler/Compilation.cs +++ b/src/ILToNative/src/Compiler/Compilation.cs @@ -339,19 +339,7 @@ public void AddField(FieldDesc field) internal MethodDesc ResolveVirtualMethod(TypeDesc implType, MethodDesc declMethod) { - // TODO: Proper virtual method resolution - string name = declMethod.Name; - MethodSignature sig = declMethod.Signature; - - MethodDesc implMethod; - TypeDesc t = implType; - for (;;) - { - implMethod = t.GetMethod(name, sig); - if (implMethod != null) - return implMethod; - t = t.BaseType; - } + return VirtualFunctionResolution.FindVirtualFunctionTargetMethodOnObjectType(declMethod, implType.GetClosestDefType()); } // Turn a name into a valid identifier diff --git a/src/ILToNative/src/ILToNative.csproj b/src/ILToNative/src/ILToNative.csproj index 47450a2d4fa..f72de7f6f64 100644 --- a/src/ILToNative/src/ILToNative.csproj +++ b/src/ILToNative/src/ILToNative.csproj @@ -127,6 +127,12 @@ TypeSystem\TypeDesc.cs + + TypeSystem\TypeDesc.MethodImpls.cs + + + TypeSystem\VirtualFunctionResolution.cs + diff --git a/src/NuGet.config b/src/NuGet.config index 2aeccd78e20..7f03e38b56d 100644 --- a/src/NuGet.config +++ b/src/NuGet.config @@ -1,16 +1,15 @@ - + + + + - - - - diff --git a/src/TypeSystem/src/Common/TypeDesc.MethodImpls.cs b/src/TypeSystem/src/Common/TypeDesc.MethodImpls.cs new file mode 100644 index 00000000000..d5b77bee640 --- /dev/null +++ b/src/TypeSystem/src/Common/TypeDesc.MethodImpls.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace Internal.TypeSystem +{ + public struct MethodImplRecord + { + public MethodDesc Decl; + public MethodDesc Body; + } + + // MethodImpl api surface for types. + public partial class MetadataType + { + /// + /// Compute an array of all MethodImpls that pertain to overriding virtual (non-interface methods) on this type. + /// May be expensive. + /// + protected abstract MethodImplRecord[] ComputeGetAllVirtualMethodImplsForType(); + + private MethodImplRecord[] _allVirtualMethodImplsForType; + /// + /// Get an array of all MethodImpls that pertain to overriding virtual (non-interface methods) on this type. + /// Expected to cache results so this api can be used repeatedly. + /// + public MethodImplRecord[] GetAllVirtualMethodImplsForType() + { + if (_allVirtualMethodImplsForType == null) + { + _allVirtualMethodImplsForType = ComputeGetAllVirtualMethodImplsForType(); + } + + return _allVirtualMethodImplsForType; + } + + /// + /// Get an array of MethodImpls where the Decl method matches by name with the specified name. + /// + /// + /// + public abstract MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string name); + } + + // Implementation of MethodImpl api surface implemented without metadata access. + public partial class InstantiatedType + { + /// + /// Instantiate a MethodImplRecord from uninstantiated form to instantiated form + /// + /// + /// + private MethodImplRecord[] InstantiateMethodImpls(MethodImplRecord[] uninstMethodImpls) + { + if (uninstMethodImpls.Length == 0) + return uninstMethodImpls; + + MethodImplRecord[] instMethodImpls = new MethodImplRecord[uninstMethodImpls.Length]; + + for (int i = 0; i < uninstMethodImpls.Length; i++) + { + instMethodImpls[i].Decl = _typeDef.Context.GetMethodForInstantiatedType(uninstMethodImpls[i].Decl, this); + instMethodImpls[i].Body = _typeDef.Context.GetMethodForInstantiatedType(uninstMethodImpls[i].Body, this); + } + + return instMethodImpls; + } + + protected override MethodImplRecord[] ComputeGetAllVirtualMethodImplsForType() + { + MethodImplRecord[] uninstMethodImpls = _typeDef.GetAllVirtualMethodImplsForType(); + return InstantiateMethodImpls(uninstMethodImpls); + } + + public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string name) + { + MethodImplRecord[] uninstMethodImpls = _typeDef.FindMethodsImplWithMatchingDeclName(name); + return InstantiateMethodImpls(uninstMethodImpls); + } + } +} diff --git a/src/TypeSystem/src/Common/TypeSystemHelpers.cs b/src/TypeSystem/src/Common/TypeSystemHelpers.cs index 578ac7dba2e..59ce16f3852 100644 --- a/src/TypeSystem/src/Common/TypeSystemHelpers.cs +++ b/src/TypeSystem/src/Common/TypeSystemHelpers.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Diagnostics; + namespace Internal.TypeSystem { static public class TypeSystemHelpers @@ -29,5 +31,56 @@ static public TypeDesc MakePointerType(this TypeDesc type) { return type.Context.GetPointerType(type); } + + static public MetadataType GetClosestDefType(this TypeDesc type) + { + if (type is MetadataType) + return (MetadataType)type; + else + return type.BaseType; + } + + + static private MethodDesc FindMethodOnExactTypeWithMatchingTypicalMethod(this TypeDesc type, MethodDesc method) + { + // Assert that either type is instantiated and its type definition is the type that defines the typical + // method definition of method, or that the owning type of the method typical definition is exactly type + System.Diagnostics.Debug.Assert((type is InstantiatedType) ? + ((InstantiatedType)type).GetTypeDefinition() == method.GetTypicalMethodDefinition().OwningType : + type == method.GetTypicalMethodDefinition().OwningType); + + foreach (MethodDesc methodToExamine in type.GetMethods()) + { + if (methodToExamine.GetTypicalMethodDefinition() == method.GetTypicalMethodDefinition()) + return methodToExamine; + } + + Debug.Assert(false, "Behavior of typical type not as expected."); + return null; + } + + static public MethodDesc FindMethodOnTypeWithMatchingTypicalMethod(this TypeDesc typeExamine, MethodDesc method) + { + TypeDesc typicalTypeInHierarchyOfTargetMethod = null; + typicalTypeInHierarchyOfTargetMethod = method.GetTypicalMethodDefinition().OwningType; + TypeDesc typeInHierarchyOfTypeExamine = typeExamine; + do + { + TypeDesc typicalTypeInHierarchyOfTypeExamine = typeInHierarchyOfTypeExamine; + if (typicalTypeInHierarchyOfTypeExamine is InstantiatedType) + { + typicalTypeInHierarchyOfTypeExamine = ((InstantiatedType)typicalTypeInHierarchyOfTypeExamine).GetTypeDefinition(); + } + if (typicalTypeInHierarchyOfTypeExamine == typicalTypeInHierarchyOfTargetMethod) + { + // set targetMethod to method on + return typeInHierarchyOfTypeExamine.FindMethodOnTypeWithMatchingTypicalMethod(method); + } + typeInHierarchyOfTypeExamine = typeInHierarchyOfTypeExamine.BaseType; + } while (typeInHierarchyOfTypeExamine != null); + + Debug.Assert(false, "method has no related type in the type hierarchy of type"); + return null; + } } } diff --git a/src/TypeSystem/src/Common/VirtualFunctionResolution.cs b/src/TypeSystem/src/Common/VirtualFunctionResolution.cs new file mode 100644 index 00000000000..e7b1d224f68 --- /dev/null +++ b/src/TypeSystem/src/Common/VirtualFunctionResolution.cs @@ -0,0 +1,446 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Internal.TypeSystem +{ + class VirtualFunctionResolution + { + class UnificationGroup + { + public UnificationGroup(MethodDesc definingMethod) + { + DefiningMethod = definingMethod; + // TODO! Add assertion that DefiningMethod is a slot defining method + } + + public MethodDesc DefiningMethod; + public List Members = new List(); + + public void SetDefiningMethod(MethodDesc newDefiningMethod) + { + // Do not change the defining method if its the same as + // one of the members, or it isn't a change at all + if (!Members.Contains(newDefiningMethod) && + DefiningMethod != newDefiningMethod) + { + DefiningMethod = newDefiningMethod; + // TODO! Add assertion that DefiningMethod is a slot defining method + } + } + + public void AddToGroup(MethodDesc method) + { + if (method == DefiningMethod) + return; + + if (!Members.Contains(method)) + Members.Add(method); + } + + public void RemoveFromGroup(MethodDesc method) + { + if (method == DefiningMethod) + throw new InvalidProgramException(); + + Members.Remove(method); + } + + public bool IsInGroupOrIsDefiningSlot(MethodDesc method) + { + if (DefiningMethod == method) + return true; + + return IsInGroup(method); + } + + public bool IsInGroup(MethodDesc method) + { + return Members.Contains(method); + } + } + + /// + /// Resolve a virtual function call (to a virtual method, not an interface method) + /// + /// + /// + /// The override of the virtual method that should be called + public static MethodDesc FindVirtualFunctionTargetMethodOnObjectType(MethodDesc targetMethod, MetadataType objectType) + { + // Step 1, convert objectType to uninstantiated form + MetadataType uninstantiatedType = objectType; + MethodDesc initialTargetMethod = targetMethod; + InstantiatedType initialInstantiatedType = objectType as InstantiatedType; + if (initialInstantiatedType != null) + { + uninstantiatedType = (MetadataType)initialInstantiatedType.GetTypeDefinition(); + } + + // Step 2, convert targetMethod to method in type hierarchy of uninstantiated form + targetMethod = targetMethod.GetMethodDefinition(); + if (uninstantiatedType != objectType) + { + targetMethod = uninstantiatedType.FindMethodOnTypeWithMatchingTypicalMethod(targetMethod); + } + + // Step 3, find unification group of target method + UnificationGroup group = new UnificationGroup(FindSlotDefiningMethodForVirtualMethod(targetMethod)); + FindBaseUnificationGroup(objectType, group); + + // Step 4, name/sig match virtual function resolve + MethodDesc resolutionTarget = FindNameSigOverrideForVirtualMethod(group.DefiningMethod, objectType); + + // Step 5, convert resolution target from uninstantiated form target to objecttype target, + // and instantiate as appropriate + if (uninstantiatedType != objectType) + { + resolutionTarget = objectType.FindMethodOnTypeWithMatchingTypicalMethod(resolutionTarget); + } + if (initialTargetMethod.HasInstantiation) + { + resolutionTarget = resolutionTarget.MakeInstantiatedMethod(initialTargetMethod.Instantiation); + } + + return resolutionTarget; + } + + private static bool IsInterfaceImplementedOnType(MetadataType type, MetadataType interfaceType) + { + // TODO! This function is the same as IsInterfaceExplicitlyImplementedOnType which isn't quite right. + // Fix the concept of implemented/explictly implemented interfaces and make these methods right. + foreach (TypeDesc iface in type.ImplementedInterfaces) + { + if (iface == interfaceType) + return true; + } + return false; + } + + private static MethodDesc FindImplFromDeclFromMethodImpls(MetadataType type, MethodDesc decl) + { + MethodImplRecord[] foundMethodImpls = type.FindMethodsImplWithMatchingDeclName(decl.Name); + + if (foundMethodImpls == null) + return null; + + bool interfaceDecl = decl.OwningType.IsInterface; + + foreach (MethodImplRecord record in foundMethodImpls) + { + MethodDesc recordDecl = record.Decl; + + if (interfaceDecl != recordDecl.OwningType.IsInterface) + continue; + + if (!interfaceDecl) + recordDecl = FindSlotDefiningMethodForVirtualMethod(recordDecl); + + if (recordDecl == decl) + { + return FindSlotDefiningMethodForVirtualMethod(record.Body); + } + } + + return null; + } + + private static bool IsInterfaceExplicitlyImplementedOnType(MetadataType type, MetadataType interfaceType) + { + // TODO! This function is the same as IsInterfaceImplementedOnType which isn't quite right. + // Fix the concept of implemented/explictly implemented interfaces and make these methods right. + foreach (TypeDesc iface in type.ImplementedInterfaces) + { + if (iface == interfaceType) + return true; + } + return false; + } + + private static MethodDesc FindMatchingVirtualMethodOnTypeByNameAndSig(MethodDesc targetMethod, MetadataType currentType) + { + string name = targetMethod.Name; + MethodSignature sig = targetMethod.Signature; + + MethodDesc implMethod = currentType.GetMethod(name, sig); + + // Only find virtual methods + if ((implMethod != null) && !implMethod.IsVirtual) + implMethod = null; + + return implMethod; + } + + // This function is used to find the name/sig based override for a given method. This method ignores all + // method impl's as it assumes they have been resolved. The algorithm is simple. Walk to the base type looking + // for overrides by name and signature. If one is found, return it as long as the newslot defining method + // for the found method matches that of the target method. + private static MethodDesc FindNameSigOverrideForVirtualMethod(MethodDesc targetMethod, MetadataType currentType) + { + while (currentType != null) + { + MethodDesc nameSigOverride = FindMatchingVirtualMethodOnTypeByNameAndSigWithSlotCheck(targetMethod, currentType); + + if (nameSigOverride != null) + { + return nameSigOverride; + } + + currentType = currentType.BaseType; + } + + return null; + } + + // This function looks for the base type method that defines the slot for a method + // This is either the newslot method most derived that is in the parent hierarchy of method + // or the least derived method that isn't newslot that matches by name and sig. + private static MethodDesc FindSlotDefiningMethodForVirtualMethod(MethodDesc method) + { + if (method == null) + return method; + + MetadataType currentType = method.OwningType.BaseType; + + // Loop until a newslot method is found + while ((currentType != null) && !method.IsNewSlot) + { + MethodDesc foundMethod = FindMatchingVirtualMethodOnTypeByNameAndSig(method, currentType); + if (foundMethod != null) + { + method = foundMethod; + } + + currentType = currentType.BaseType; + } + + // Newslot method found, or if not the least derived method that matches by name and + // sig is to be returned. + return method; + } + + private static MethodDesc FindMatchingVirtualMethodOnTypeByNameAndSigWithSlotCheck(MethodDesc method, MetadataType currentType) + { + MethodDesc foundMethod = FindMatchingVirtualMethodOnTypeByNameAndSig(method, currentType); + if (foundMethod != null) + { + if (VerifyMethodsHaveTheSameVirtualSlot(foundMethod, method)) + { + return foundMethod; + } + } + + return null; + } + + // Return true if the slot that defines methodToVerify matches slotDefiningMethod + private static bool VerifyMethodsHaveTheSameVirtualSlot(MethodDesc methodToVerify, MethodDesc slotDefiningMethod) + { + MethodDesc slotDefiningMethodOfMethodToVerify = FindSlotDefiningMethodForVirtualMethod(methodToVerify); + return slotDefiningMethodOfMethodToVerify == slotDefiningMethod; + } + + private static void FindBaseUnificationGroup(MetadataType currentType, UnificationGroup unificationGroup) + { + MethodDesc originalDefiningMethod = unificationGroup.DefiningMethod; + + MethodDesc methodImpl = FindImplFromDeclFromMethodImpls(currentType, unificationGroup.DefiningMethod); + if (methodImpl != null) + { + unificationGroup.SetDefiningMethod(methodImpl); + } + + MethodDesc nameSigMatchMethod = FindMatchingVirtualMethodOnTypeByNameAndSigWithSlotCheck(unificationGroup.DefiningMethod, currentType); + MetadataType baseType = currentType.BaseType; + + // Unless the current type has a name/sig match for the group, look to the base type to define the unification group further + if ((nameSigMatchMethod == null) && (baseType != null)) + { + FindBaseUnificationGroup(baseType, unificationGroup); + } + + Debug.Assert(unificationGroup.IsInGroupOrIsDefiningSlot(originalDefiningMethod)); + + // Now, we have the unification group from the type, or have discovered its defined on the current type. + // Adjust the group to contain all of the elements that are added to it on this type, remove the components that + // have seperated themselves from the group + + // Start with removing methods that seperated themselves from the group via name/sig matches + List seperatedMethods = null; + + foreach (MethodDesc memberMethod in unificationGroup.Members) + { + MethodDesc nameSigMatchMemberMethod = FindMatchingVirtualMethodOnTypeByNameAndSigWithSlotCheck(memberMethod, currentType); + if (nameSigMatchMemberMethod != null) + { + if (seperatedMethods == null) + seperatedMethods = new List(); + seperatedMethods.Add(memberMethod); + } + } + + if (seperatedMethods != null) + { + foreach (MethodDesc seperatedMethod in seperatedMethods) + { + unificationGroup.RemoveFromGroup(seperatedMethod); + } + } + + // Next find members which have seperated or added themselves to the group via MethodImpls + foreach (MethodImplRecord methodImplRecord in currentType.GetAllVirtualMethodImplsForType()) + { + MethodDesc declSlot = FindSlotDefiningMethodForVirtualMethod(methodImplRecord.Decl); + MethodDesc implSlot = FindSlotDefiningMethodForVirtualMethod(methodImplRecord.Body); + + if (unificationGroup.IsInGroup(declSlot) && !unificationGroup.IsInGroupOrIsDefiningSlot(implSlot)) + { + unificationGroup.RemoveFromGroup(declSlot); + seperatedMethods.Add(declSlot); + continue; + } + if (!unificationGroup.IsInGroupOrIsDefiningSlot(declSlot) && unificationGroup.IsInGroupOrIsDefiningSlot(implSlot)) + { + // Add decl to group. + + // To do so, we need to have the Unification Group of the decl slot, as it may have multiple members itself + UnificationGroup addDeclGroup = new UnificationGroup(declSlot); + FindBaseUnificationGroup(baseType, addDeclGroup); + Debug.Assert(addDeclGroup.IsInGroupOrIsDefiningSlot(declSlot)); + + // Add all members from the decl's unification group except for ones that have been seperated by name/sig matches + // or previously processed methodimpls. NOTE: This implies that method impls are order dependent. + if (!seperatedMethods.Contains(addDeclGroup.DefiningMethod)) + { + unificationGroup.AddToGroup(addDeclGroup.DefiningMethod); + } + + foreach (MethodDesc addDeclGroupMemberMethod in addDeclGroup.Members) + { + if (!seperatedMethods.Contains(addDeclGroupMemberMethod)) + { + unificationGroup.AddToGroup(addDeclGroupMemberMethod); + } + } + } + } + } + + //////////////////////// INTERFACE RESOLUTION + //Interface function resolution + // Interface function resolution follows the following rules + // 1. Apply any method impl that may exist, if once of these exists, resolve to target immediately. + // 2. If an interface is explicitly defined on a type, then attempt to perform a namesig match on the + // current type to resolve.If the interface isn’t resolved, if it isn’t implemented on a base type, + // scan all base types for name / sig matches. + // 3. If implicitly defined, attempt to perform a namesig match if the interface method implementation + // has not been found on some base type. + // The above will resolve an interface to a virtual method slot. From there perform virtual resolution + // to find out the actual target.Note, to preserve correct behavior in the presence of variance, this + // function returns null if the interface method implementation is not defined by the current type in + // the hierarchy.For variance to work correctly, this requires that interfaces be queried in correct order. + // See current interface call resolution for details on how that happens. + public static MethodDesc ResolveInterfaceMethodToVirtualMethodOnType(MethodDesc interfaceMethod, MetadataType currentType) + { + if (currentType.IsInterface) + return null; + + MethodDesc methodImpl = FindImplFromDeclFromMethodImpls(currentType, interfaceMethod); + if (methodImpl != null) + return methodImpl; + + MetadataType interfaceType = (MetadataType)interfaceMethod.OwningType; + + // If interface is explicitly defined on a type, search for a name/sig match. + bool foundExplicitInterface = IsInterfaceExplicitlyImplementedOnType(currentType, interfaceType); + MetadataType baseType = currentType.BaseType; + + if (foundExplicitInterface) + { + MethodDesc foundOnCurrentType = FindMatchingVirtualMethodOnTypeByNameAndSig(interfaceMethod, currentType); + foundOnCurrentType = FindSlotDefiningMethodForVirtualMethod(foundOnCurrentType); + + if (baseType == null) + return foundOnCurrentType; + + if (foundOnCurrentType == null && (ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod, baseType) == null)) + { + // TODO! Does this handle the case where the base type explicitly implements the interface, but is abstract + // and doesn't actually have an implementation? + if (!IsInterfaceImplementedOnType(baseType, interfaceType)) + { + return FindNameSigOverrideForInterfaceMethodRecursive(interfaceMethod, baseType); + } + } + return foundOnCurrentType; + } + else + { + // Implicit interface case + if (!IsInterfaceImplementedOnType(currentType, interfaceType)) + { + // If the interface isn't implemented on this type at all, don't go searching + return null; + } + + // This is an implicitly implemented interface method. Only return a vlaue if this is the first type in the class + // hierarchy that implements the interface. NOTE: If we pay attention to whether or not the parent type is + // abstract or not, we may be able to be more efficient here, but let's skip that for now + MethodDesc baseClassImplementationOfInterfaceMethod = ResolveInterfaceMethodToVirtualMethodOnTypeRecursive(interfaceMethod, baseType); + if (baseClassImplementationOfInterfaceMethod != null) + { + return null; + } + else + { + return FindNameSigOverrideForInterfaceMethodRecursive(interfaceMethod, currentType); + } + } + } + + // Helper routine used during implicit interface implementation discovery + private static MethodDesc ResolveInterfaceMethodToVirtualMethodOnTypeRecursive(MethodDesc interfaceMethod, MetadataType currentType) + { + while (true) + { + if (currentType == null) + return null; + + MetadataType interfaceType = (MetadataType)interfaceMethod.OwningType; + + if (!IsInterfaceImplementedOnType(currentType, interfaceType)) + { + // If the interface isn't implemented on this type at all, don't go searching + return null; + } + + MethodDesc currentTypeInterfaceResolution = ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod, currentType); + if (currentTypeInterfaceResolution != null) + return currentTypeInterfaceResolution; + + currentType = currentType.BaseType; + } + } + + // Perform a name/sig match for a virtual method across the specified types and all of the types parents. + private static MethodDesc FindNameSigOverrideForInterfaceMethodRecursive(MethodDesc interfaceMethod, MetadataType currentType) + { + while (true) + { + if (currentType == null) + return null; + + MethodDesc nameSigOverride = FindMatchingVirtualMethodOnTypeByNameAndSig(interfaceMethod, currentType); + if (nameSigOverride != null) + { + return FindSlotDefiningMethodForVirtualMethod(nameSigOverride); + } + + currentType = currentType.BaseType; + } + } + } +} diff --git a/src/TypeSystem/src/Ecma/EcmaType.cs b/src/TypeSystem/src/Ecma/EcmaType.cs index 5455a9a13f3..465e6cd1fa5 100644 --- a/src/TypeSystem/src/Ecma/EcmaType.cs +++ b/src/TypeSystem/src/Ecma/EcmaType.cs @@ -399,5 +399,112 @@ public override bool IsModuleType return Module.GetGlobalModuleType() == this; } } + + // Virtual function related functionality + public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string declName) + { + MetadataReader metadataReader = _module.MetadataReader; + var stringComparer = metadataReader.StringComparer; + List foundRecords = null; + + foreach (var methodImplHandle in _typeDefinition.GetMethodImplementations()) + { + MethodImplementation methodImpl = metadataReader.GetMethodImplementation(methodImplHandle); + + EntityHandle methodDeclCheckHandle = methodImpl.MethodDeclaration; + HandleKind methodDeclHandleKind = methodDeclCheckHandle.Kind; + + // We want to check that the method name matches before actually getting the MethodDesc. For MethodSpecifications + // we need to dereference that handle to the underlying member reference to look at name matching. + if (methodDeclHandleKind == HandleKind.MethodSpecification) + { + methodDeclCheckHandle = metadataReader.GetMethodSpecification((MethodSpecificationHandle)methodDeclCheckHandle).Method; + methodDeclHandleKind = methodDeclCheckHandle.Kind; + } + + bool foundRecord = false; + + switch (methodImpl.MethodDeclaration.Kind) + { + case HandleKind.MethodDefinition: + if (stringComparer.Equals(metadataReader.GetMethodDefinition((MethodDefinitionHandle)methodDeclCheckHandle).Name, declName)) + { + foundRecord = true; + } + break; + + case HandleKind.MemberReference: + if (stringComparer.Equals(metadataReader.GetMemberReference((MemberReferenceHandle)methodDeclCheckHandle).Name, declName)) + { + foundRecord = true; + } + break; + } + + if (foundRecord) + { + if (foundRecords == null) + foundRecords = new List(); + + MethodImplRecord newRecord = new MethodImplRecord(); + newRecord.Decl = (MethodDesc)_module.GetObject(methodImpl.MethodDeclaration); + newRecord.Body = (MethodDesc)_module.GetObject(methodImpl.MethodBody); + + foundRecords.Add(newRecord); + } + } + + if (foundRecords != null) + return foundRecords.ToArray(); + + return null; + } + + protected override MethodImplRecord[] ComputeGetAllVirtualMethodImplsForType() + { + List records = new List(); + + MetadataReader metadataReader = _module.MetadataReader; + var stringComparer = metadataReader.StringComparer; + + foreach (var methodImplHandle in _typeDefinition.GetMethodImplementations()) + { + MethodImplementation methodImpl = metadataReader.GetMethodImplementation(methodImplHandle); + + EntityHandle methodDeclCheckHandle = methodImpl.MethodDeclaration; + HandleKind methodDeclHandleKind = methodDeclCheckHandle.Kind; + + // We want to check that the method name matches before actually getting the MethodDesc. For MethodSpecifications + // we need to dereference that handle to the underlying member reference to look at name matching. + if (methodDeclHandleKind == HandleKind.MethodSpecification) + { + methodDeclCheckHandle = metadataReader.GetMethodSpecification((MethodSpecificationHandle)methodDeclCheckHandle).Method; + methodDeclHandleKind = methodDeclCheckHandle.Kind; + } + + MetadataType owningType = null; + switch (methodImpl.MethodDeclaration.Kind) + { + case HandleKind.MethodDefinition: + owningType = ((MethodDesc)_module.GetObject(methodDeclCheckHandle)).OwningType as MetadataType; + break; + + case HandleKind.MemberReference: + EntityHandle owningTypeHandle = metadataReader.GetMemberReference((MemberReferenceHandle)methodImpl.MethodDeclaration).Parent; + owningType = _module.GetObject(owningTypeHandle) as MetadataType; + break; + } + + if (!owningType.IsInterface) + { + MethodImplRecord newRecord = new MethodImplRecord(); + newRecord.Decl = (MethodDesc)_module.GetObject(methodImpl.MethodDeclaration); + newRecord.Body = (MethodDesc)_module.GetObject(methodImpl.MethodBody); + records.Add(newRecord); + } + } + + return records.ToArray(); + } } } From 6edbdbed032020e36c0bbb5c1b80e56fe5f17731 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 6 Oct 2015 14:31:15 -0700 Subject: [PATCH 2/2] Respond to code review feedback --- src/ILToNative/src/Compiler/AsmWriter.cs | 2 +- src/ILToNative/src/Compiler/Compilation.cs | 7 +------ src/ILToNative/src/CppCodeGen/CppWriter.cs | 3 +-- .../src/Common/TypeDesc.MethodImpls.cs | 19 +++++++++++-------- .../src/Common/TypeSystemHelpers.cs | 11 ++++++----- .../src/Common/VirtualFunctionResolution.cs | 8 ++++---- src/TypeSystem/src/Ecma/EcmaType.cs | 7 +++---- 7 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/ILToNative/src/Compiler/AsmWriter.cs b/src/ILToNative/src/Compiler/AsmWriter.cs index 4026c828d33..fecfb27ea9c 100644 --- a/src/ILToNative/src/Compiler/AsmWriter.cs +++ b/src/ILToNative/src/Compiler/AsmWriter.cs @@ -312,7 +312,7 @@ void OutputVirtualSlots(TypeDesc implType, TypeDesc declType) { MethodDesc declMethod = reg.VirtualSlots[i]; - MethodDesc implMethod = ResolveVirtualMethod(implType, declMethod); + MethodDesc implMethod = VirtualFunctionResolution.FindVirtualFunctionTargetMethodOnObjectType(declMethod, implType.GetClosestDefType()); Out.Write(".quad "); Out.WriteLine(GetMangledMethodName(implMethod)); diff --git a/src/ILToNative/src/Compiler/Compilation.cs b/src/ILToNative/src/Compiler/Compilation.cs index 18bb0486df9..a6e15918823 100644 --- a/src/ILToNative/src/Compiler/Compilation.cs +++ b/src/ILToNative/src/Compiler/Compilation.cs @@ -221,7 +221,7 @@ void ExpandVirtualMethods() { MethodDesc declMethod = declReg.VirtualSlots[i]; - AddMethod(ResolveVirtualMethod(reg.Type, declMethod)); + AddMethod(VirtualFunctionResolution.FindVirtualFunctionTargetMethodOnObjectType(declMethod, reg.Type.GetClosestDefType())); } } @@ -337,11 +337,6 @@ public void AddField(FieldDesc field) } } - internal MethodDesc ResolveVirtualMethod(TypeDesc implType, MethodDesc declMethod) - { - return VirtualFunctionResolution.FindVirtualFunctionTargetMethodOnObjectType(declMethod, implType.GetClosestDefType()); - } - // Turn a name into a valid identifier private static string SanitizeName(string s) { diff --git a/src/ILToNative/src/CppCodeGen/CppWriter.cs b/src/ILToNative/src/CppCodeGen/CppWriter.cs index cff27b84eb4..e2d41620e0e 100644 --- a/src/ILToNative/src/CppCodeGen/CppWriter.cs +++ b/src/ILToNative/src/CppCodeGen/CppWriter.cs @@ -722,8 +722,7 @@ void AppendVirtualSlots(StringBuilder sb, TypeDesc implType, TypeDesc declType) for (int i = 0; i < reg.VirtualSlots.Count; i++) { MethodDesc declMethod = reg.VirtualSlots[i]; - - MethodDesc implMethod = _compilation.ResolveVirtualMethod(implType, declMethod); + MethodDesc implMethod = VirtualFunctionResolution.FindVirtualFunctionTargetMethodOnObjectType(declMethod, implType.GetClosestDefType()); sb.Append("(void*)&"); sb.Append(GetCppTypeName(implMethod.OwningType)); diff --git a/src/TypeSystem/src/Common/TypeDesc.MethodImpls.cs b/src/TypeSystem/src/Common/TypeDesc.MethodImpls.cs index d5b77bee640..8ffa53e53b0 100644 --- a/src/TypeSystem/src/Common/TypeDesc.MethodImpls.cs +++ b/src/TypeSystem/src/Common/TypeDesc.MethodImpls.cs @@ -18,21 +18,24 @@ public partial class MetadataType /// Compute an array of all MethodImpls that pertain to overriding virtual (non-interface methods) on this type. /// May be expensive. /// - protected abstract MethodImplRecord[] ComputeGetAllVirtualMethodImplsForType(); + protected abstract MethodImplRecord[] ComputeVirtualMethodImplsForType(); private MethodImplRecord[] _allVirtualMethodImplsForType; /// /// Get an array of all MethodImpls that pertain to overriding virtual (non-interface methods) on this type. /// Expected to cache results so this api can be used repeatedly. /// - public MethodImplRecord[] GetAllVirtualMethodImplsForType() + public MethodImplRecord[] VirtualMethodImplsForType { - if (_allVirtualMethodImplsForType == null) + get { - _allVirtualMethodImplsForType = ComputeGetAllVirtualMethodImplsForType(); - } + if (_allVirtualMethodImplsForType == null) + { + _allVirtualMethodImplsForType = ComputeVirtualMethodImplsForType(); + } - return _allVirtualMethodImplsForType; + return _allVirtualMethodImplsForType; + } } /// @@ -67,9 +70,9 @@ private MethodImplRecord[] InstantiateMethodImpls(MethodImplRecord[] uninstMetho return instMethodImpls; } - protected override MethodImplRecord[] ComputeGetAllVirtualMethodImplsForType() + protected override MethodImplRecord[] ComputeVirtualMethodImplsForType() { - MethodImplRecord[] uninstMethodImpls = _typeDef.GetAllVirtualMethodImplsForType(); + MethodImplRecord[] uninstMethodImpls = _typeDef.VirtualMethodImplsForType; return InstantiateMethodImpls(uninstMethodImpls); } diff --git a/src/TypeSystem/src/Common/TypeSystemHelpers.cs b/src/TypeSystem/src/Common/TypeSystemHelpers.cs index 59ce16f3852..8da2f8d71aa 100644 --- a/src/TypeSystem/src/Common/TypeSystemHelpers.cs +++ b/src/TypeSystem/src/Common/TypeSystemHelpers.cs @@ -45,13 +45,15 @@ static private MethodDesc FindMethodOnExactTypeWithMatchingTypicalMethod(this Ty { // Assert that either type is instantiated and its type definition is the type that defines the typical // method definition of method, or that the owning type of the method typical definition is exactly type - System.Diagnostics.Debug.Assert((type is InstantiatedType) ? + Debug.Assert((type is InstantiatedType) ? ((InstantiatedType)type).GetTypeDefinition() == method.GetTypicalMethodDefinition().OwningType : type == method.GetTypicalMethodDefinition().OwningType); + MethodDesc methodTypicalDefinition = method.GetTypicalMethodDefinition(); + foreach (MethodDesc methodToExamine in type.GetMethods()) { - if (methodToExamine.GetTypicalMethodDefinition() == method.GetTypicalMethodDefinition()) + if (methodToExamine.GetTypicalMethodDefinition() == methodTypicalDefinition) return methodToExamine; } @@ -61,15 +63,14 @@ static private MethodDesc FindMethodOnExactTypeWithMatchingTypicalMethod(this Ty static public MethodDesc FindMethodOnTypeWithMatchingTypicalMethod(this TypeDesc typeExamine, MethodDesc method) { - TypeDesc typicalTypeInHierarchyOfTargetMethod = null; - typicalTypeInHierarchyOfTargetMethod = method.GetTypicalMethodDefinition().OwningType; + TypeDesc typicalTypeInHierarchyOfTargetMethod = method.GetTypicalMethodDefinition().OwningType; TypeDesc typeInHierarchyOfTypeExamine = typeExamine; do { TypeDesc typicalTypeInHierarchyOfTypeExamine = typeInHierarchyOfTypeExamine; if (typicalTypeInHierarchyOfTypeExamine is InstantiatedType) { - typicalTypeInHierarchyOfTypeExamine = ((InstantiatedType)typicalTypeInHierarchyOfTypeExamine).GetTypeDefinition(); + typicalTypeInHierarchyOfTypeExamine = typicalTypeInHierarchyOfTypeExamine.GetTypeDefinition(); } if (typicalTypeInHierarchyOfTypeExamine == typicalTypeInHierarchyOfTargetMethod) { diff --git a/src/TypeSystem/src/Common/VirtualFunctionResolution.cs b/src/TypeSystem/src/Common/VirtualFunctionResolution.cs index e7b1d224f68..56a2589af96 100644 --- a/src/TypeSystem/src/Common/VirtualFunctionResolution.cs +++ b/src/TypeSystem/src/Common/VirtualFunctionResolution.cs @@ -7,7 +7,7 @@ namespace Internal.TypeSystem { - class VirtualFunctionResolution + static class VirtualFunctionResolution { class UnificationGroup { @@ -72,12 +72,12 @@ public bool IsInGroup(MethodDesc method) public static MethodDesc FindVirtualFunctionTargetMethodOnObjectType(MethodDesc targetMethod, MetadataType objectType) { // Step 1, convert objectType to uninstantiated form - MetadataType uninstantiatedType = objectType; + TypeDesc uninstantiatedType = objectType; MethodDesc initialTargetMethod = targetMethod; InstantiatedType initialInstantiatedType = objectType as InstantiatedType; if (initialInstantiatedType != null) { - uninstantiatedType = (MetadataType)initialInstantiatedType.GetTypeDefinition(); + uninstantiatedType = initialInstantiatedType.GetTypeDefinition(); } // Step 2, convert targetMethod to method in type hierarchy of uninstantiated form @@ -291,7 +291,7 @@ private static void FindBaseUnificationGroup(MetadataType currentType, Unificati } // Next find members which have seperated or added themselves to the group via MethodImpls - foreach (MethodImplRecord methodImplRecord in currentType.GetAllVirtualMethodImplsForType()) + foreach (MethodImplRecord methodImplRecord in currentType.VirtualMethodImplsForType) { MethodDesc declSlot = FindSlotDefiningMethodForVirtualMethod(methodImplRecord.Decl); MethodDesc implSlot = FindSlotDefiningMethodForVirtualMethod(methodImplRecord.Body); diff --git a/src/TypeSystem/src/Ecma/EcmaType.cs b/src/TypeSystem/src/Ecma/EcmaType.cs index 465e6cd1fa5..498a4e33c24 100644 --- a/src/TypeSystem/src/Ecma/EcmaType.cs +++ b/src/TypeSystem/src/Ecma/EcmaType.cs @@ -460,12 +460,11 @@ public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string de return null; } - protected override MethodImplRecord[] ComputeGetAllVirtualMethodImplsForType() + protected override MethodImplRecord[] ComputeVirtualMethodImplsForType() { List records = new List(); MetadataReader metadataReader = _module.MetadataReader; - var stringComparer = metadataReader.StringComparer; foreach (var methodImplHandle in _typeDefinition.GetMethodImplementations()) { @@ -483,14 +482,14 @@ protected override MethodImplRecord[] ComputeGetAllVirtualMethodImplsForType() } MetadataType owningType = null; - switch (methodImpl.MethodDeclaration.Kind) + switch (methodDeclHandleKind) { case HandleKind.MethodDefinition: owningType = ((MethodDesc)_module.GetObject(methodDeclCheckHandle)).OwningType as MetadataType; break; case HandleKind.MemberReference: - EntityHandle owningTypeHandle = metadataReader.GetMemberReference((MemberReferenceHandle)methodImpl.MethodDeclaration).Parent; + EntityHandle owningTypeHandle = metadataReader.GetMemberReference((MemberReferenceHandle)methodDeclCheckHandle).Parent; owningType = _module.GetObject(owningTypeHandle) as MetadataType; break; }