示例#1
0
        protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType implType, out CORINFO_DEVIRTUALIZATION_DETAIL devirtualizationDetail)
        {
            devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_UNKNOWN;

            MethodDesc impl;

            if (declMethod.OwningType.IsInterface)
            {
                if (declMethod.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any) || implType.IsCanonicalSubtype(CanonicalFormKind.Any))
                {
                    DefType[] implTypeRuntimeInterfaces          = implType.RuntimeInterfaces;
                    int       canonicallyMatchingInterfacesFound = 0;
                    DefType   canonicalInterfaceType             = (DefType)declMethod.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific);
                    for (int i = 0; i < implTypeRuntimeInterfaces.Length; i++)
                    {
                        DefType runtimeInterface = implTypeRuntimeInterfaces[i];
                        if (canonicalInterfaceType.HasSameTypeDefinition(runtimeInterface) &&
                            runtimeInterface.ConvertToCanonForm(CanonicalFormKind.Specific) == canonicalInterfaceType)
                        {
                            canonicallyMatchingInterfacesFound++;
                            if (canonicallyMatchingInterfacesFound > 1)
                            {
                                // We cannot resolve the interface as we don't know with exact enough detail which interface
                                // of multiple possible interfaces is being called.
                                devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_MULTIPLE_IMPL;
                                return(null);
                            }
                        }
                    }
                }

                if (!implType.CanCastTo(declMethod.OwningType))
                {
                    devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CAST;
                    return(null);
                }

                impl = implType.ResolveInterfaceMethodTargetWithVariance(declMethod);
                if (impl != null)
                {
                    impl = implType.FindVirtualFunctionTargetMethodOnObjectType(impl);
                }
                else
                {
                    MethodDesc dimMethod = null;
                    // This isn't the correct lookup algorithm for variant default interface methods
                    // but as we will drop any results we find in any case, it doesn't matter much.
                    // Non-variant dispatch can simply use ResolveInterfaceMethodToDefaultImplementationOnType
                    // but that implementation currently cannot handle variance.

                    MethodDesc defaultInterfaceDispatchDeclMethod = null;
                    foreach (TypeDesc iface in implType.RuntimeInterfaces)
                    {
                        if (iface == declMethod.OwningType)
                        {
                            defaultInterfaceDispatchDeclMethod = declMethod;
                            break;
                        }
                        if (iface.HasSameTypeDefinition(declMethod.OwningType) && iface.CanCastTo(declMethod.OwningType))
                        {
                            defaultInterfaceDispatchDeclMethod = iface.FindMethodOnTypeWithMatchingTypicalMethod(declMethod);
                            // Prefer to find the exact match, so don't break immediately
                        }
                    }

                    if (defaultInterfaceDispatchDeclMethod != null)
                    {
                        switch (implType.ResolveInterfaceMethodToDefaultImplementationOnType(defaultInterfaceDispatchDeclMethod, out dimMethod))
                        {
                        case DefaultInterfaceMethodResolution.Diamond:
                        case DefaultInterfaceMethodResolution.Reabstraction:
                            devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DIM;
                            return(null);

                        case DefaultInterfaceMethodResolution.DefaultImplementation:
                            if (dimMethod.OwningType.HasInstantiation || (declMethod != defaultInterfaceDispatchDeclMethod))
                            {
                                // If we devirtualized into a default interface method on a generic type, we should actually return an
                                // instantiating stub but this is not happening.
                                // Making this work is tracked by https://github.com/dotnet/runtime/issues/9588

                                // In addition, we fail here for variant default interface dispatch
                                devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DIM;
                                return(null);
                            }
                            else
                            {
                                impl = dimMethod;
                            }
                            break;
                        }
                    }
                }
            }
            else
            {
                // The derived class should be a subclass of the base class.
                // this check is perfomed via typedef checking instead of casting, as we accept canon methods calling exact types
                TypeDesc checkType;
                for (checkType = implType; checkType != null && !checkType.HasSameTypeDefinition(declMethod.OwningType); checkType = checkType.BaseType)
                {
                }

                if ((checkType == null) || (checkType.ConvertToCanonForm(CanonicalFormKind.Specific) != declMethod.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific)))
                {
                    // The derived class should be a subclass of the base class.
                    devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_SUBCLASS;
                    return(null);
                }
                else
                {
                    // At this point, the decl method may be only canonically compatible, but not an exact match to a method in the type hierarchy
                    // Convert it to an exact match. (Or if it is an exact match, the FindMethodOnTypeWithMatchingTypicalMethod will be a no-op)
                    declMethod = checkType.FindMethodOnTypeWithMatchingTypicalMethod(declMethod);
                }

                impl = implType.FindVirtualFunctionTargetMethodOnObjectType(declMethod);
                if (impl != null && (impl != declMethod))
                {
                    MethodDesc slotDefiningMethodImpl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(impl);
                    MethodDesc slotDefiningMethodDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(declMethod);

                    if (slotDefiningMethodImpl != slotDefiningMethodDecl)
                    {
                        // If the derived method's slot does not match the vtable slot,
                        // bail on devirtualization, as the method was installed into
                        // the vtable slot via an explicit override and even if the
                        // method is final, the slot may not be.
                        //
                        // Note the jit could still safely devirtualize if it had an exact
                        // class, but such cases are likely rare.
                        devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_SLOT;
                        impl = null;
                    }
                }
            }

            return(impl);
        }
示例#2
0
 public MethodDesc ResolveVirtualMethod(MethodDesc declMethod, TypeDesc implType, out CORINFO_DEVIRTUALIZATION_DETAIL devirtualizationDetail)
 {
     return(_devirtualizationManager.ResolveVirtualMethod(declMethod, implType, out devirtualizationDetail));
 }
示例#3
0
        /// <summary>
        /// Attempts to resolve the <paramref name="declMethod"/> virtual method into
        /// a method on <paramref name="implType"/> that implements the declaring method.
        /// Returns null if this is not possible.
        /// </summary>
        /// <remarks>
        /// Note that if <paramref name="implType"/> is a value type, the result of the resolution
        /// might have to be treated as an unboxing thunk by the caller.
        /// </remarks>
        public MethodDesc ResolveVirtualMethod(MethodDesc declMethod, TypeDesc implType, out CORINFO_DEVIRTUALIZATION_DETAIL devirtualizationDetail)
        {
            Debug.Assert(declMethod.IsVirtual);

            // We're operating on virtual methods. This means that if implType is an array, we need
            // to get the type that has all the virtual methods provided by the class library.
            return(ResolveVirtualMethod(declMethod, implType.GetClosestDefType(), out devirtualizationDetail));
        }
示例#4
0
        protected override MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType implType, out CORINFO_DEVIRTUALIZATION_DETAIL devirtualizationDetail)
        {
            devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_UNKNOWN;

            // Versioning resiliency rules here are complex
            // Decl method checking
            // 1. If the declMethod is a class method, then we do not need to check if it is within the version bubble with a VersionsWithCode check
            //    but the metadata for the open definition must be within the bubble, or the decl method is in the direct parent type
            //    of a type which is in the version bubble relative to the implType.
            // 2. If the declMethod is an interface method, we can allow it if interface type is defined within the version
            //    bubble, or if the implementation type hierarchy is entirely within the version bubble (excluding System.Object and System.ValueType).
            // 3. At all times the declMethod must be representable as a token. That check is handled internally in the
            //    jit interface logic after the logic that executes here.
            //
            // ImplType checking
            // 1. At all times the metadata definition of the implementation type must version with the application.
            // 2. Additionally, the exact implementation type must be representable within the R2R image (this is checked via VersionsWithTypeReference
            //
            // Result method checking
            // 1. Ensure that the resolved result versions with the code, or is the decl method
            // 2. Devirtualizing to a default interface method is not currently considered to be useful, and how to check for version
            //    resilience has not yet been analyzed.
            // 3. When checking that the resolved result versions with the code, validate that all of the types
            //    From implType to the owning type of resolved result method also version with the code.

            bool declMethodCheckFailed;
            var  firstTypeInImplTypeHierarchyNotInVersionBubble = FindVersionBubbleEdge(_compilationModuleGroup, implType, out TypeDesc lastTypeInHierarchyInVersionBubble);

            if (!declMethod.OwningType.IsInterface)
            {
                if (_compilationModuleGroup.VersionsWithType(declMethod.OwningType.GetTypeDefinition()))
                {
                    declMethodCheckFailed = false;
                }
                else
                {
                    if (firstTypeInImplTypeHierarchyNotInVersionBubble != declMethod.OwningType)
                    {
                        devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_BUBBLE_CLASS_DECL;
                        declMethodCheckFailed  = true;
                    }
                    else
                    {
                        declMethodCheckFailed = false;
                    }
                }
            }
            else
            {
                if (_compilationModuleGroup.VersionsWithType(declMethod.OwningType.GetTypeDefinition()))
                {
                    declMethodCheckFailed = false;
                }
                else
                {
                    if (firstTypeInImplTypeHierarchyNotInVersionBubble == null || implType.IsValueType || firstTypeInImplTypeHierarchyNotInVersionBubble.IsObject)
                    {
                        declMethodCheckFailed = false;
                    }
                    else
                    {
                        devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_BUBBLE_INTERFACE_DECL;
                        declMethodCheckFailed  = true;
                    }
                }
            }

            if (declMethodCheckFailed)
            {
                return(null);
            }

            // Impl type check
            if (!_compilationModuleGroup.VersionsWithType(implType.GetTypeDefinition()))
            {
                devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_BUBBLE_IMPL;
                return(null);
            }

            if (!_compilationModuleGroup.VersionsWithTypeReference(implType))
            {
                devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_BUBBLE_IMPL_NOT_REFERENCEABLE;
                return(null);
            }

            /**
             * It is possible for us to hit a scenario where a type implements
             * the same interface more than once due to generic instantiations.
             *
             * In some instances of those cases, the VirtualMethodAlgorithm
             * does not produce identical output as CoreCLR would, leading to
             * behavioral differences in compiled outputs.
             *
             * Instead of fixing the algorithm (in which the work to fix it is
             * tracked in https://github.com/dotnet/corert/issues/208), the
             * following duplication detection algorithm will detect the case and
             * refuse to devirtualize for those scenarios.
             */
            if (declMethod.OwningType.IsInterface)
            {
                DefType[] implTypeRuntimeInterfaces = implType.RuntimeInterfaces;
                for (int i = 0; i < implTypeRuntimeInterfaces.Length; i++)
                {
                    for (int j = i + 1; j < implTypeRuntimeInterfaces.Length; j++)
                    {
                        if (implTypeRuntimeInterfaces[i] == implTypeRuntimeInterfaces[j])
                        {
                            devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DUPLICATE_INTERFACE;
                            return(null);
                        }
                    }
                }
            }


            if (declMethod.OwningType.IsInterface)
            {
                // Check for ComImport class, as we don't support devirtualization of ComImport classes
                // Run this check on all platforms, to avoid possible future versioning problems if we implement
                // COM on other architectures.
                if (!implType.IsObject)
                {
                    TypeDesc typeThatDerivesFromObject = implType;
                    while (!typeThatDerivesFromObject.BaseType.IsObject)
                    {
                        typeThatDerivesFromObject = typeThatDerivesFromObject.BaseType;
                    }

                    if (typeThatDerivesFromObject is Internal.TypeSystem.Ecma.EcmaType ecmaType)
                    {
                        if ((ecmaType.Attributes & System.Reflection.TypeAttributes.Import) != 0)
                        {
                            devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_COM;
                            return(null);
                        }
                    }
                }
            }

            MethodDesc resolvedVirtualMethod = base.ResolveVirtualMethod(declMethod, implType, out devirtualizationDetail);

            if (resolvedVirtualMethod != null)
            {
                // Validate that the inheritance chain for resolution is within version bubble
                // The rule is somewhat tricky here.
                // If the resolved method is the declMethod, then only types which derive from the
                // OwningType of the decl method need to be within the version bubble.
                //
                // If not, then all the types from the implType to the Owning type of the resolved
                // virtual method must be within the version bubble.
                if (firstTypeInImplTypeHierarchyNotInVersionBubble == null)
                {
                    // The entire type hierarchy of the implType is within the version bubble, and there is no more to check
                    return(resolvedVirtualMethod);
                }

                if (declMethod == resolvedVirtualMethod && firstTypeInImplTypeHierarchyNotInVersionBubble == declMethod.OwningType)
                {
                    // Exact match for use of decl method check
                    return(resolvedVirtualMethod);
                }

                // Ensure that declMethod is implemented on a type within the type hierarchy that is within the version bubble
                for (TypeDesc typeExamine = resolvedVirtualMethod.OwningType; typeExamine != null; typeExamine = typeExamine.BaseType)
                {
                    if (typeExamine == lastTypeInHierarchyInVersionBubble)
                    {
                        return(resolvedVirtualMethod);
                    }
                }
                devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_BUBBLE;
            }

            // Cannot devirtualize, as we can't resolve to a target.
            return(null);