Beispiel #1
0
        public bool IsCanonicallyEquivalent(RuntimeTypeHandle other)
        {
            if (_defType != null)
            {
                TypeDesc typeToFindAsCanon   = _defType.ConvertToCanonForm(_canonKind);
                TypeDesc otherTypeAsTypeDesc = _defType.Context.ResolveRuntimeTypeHandle(other);
                TypeDesc otherTypeAsCanon    = otherTypeAsTypeDesc.ConvertToCanonForm(_canonKind);
                return(typeToFindAsCanon == otherTypeAsCanon);
            }

            if (!_genericDefinition.IsNull())
            {
                if (RuntimeAugments.IsGenericType(other))
                {
                    RuntimeTypeHandle   otherGenericDefinition;
                    RuntimeTypeHandle[] otherGenericArgs;
                    otherGenericDefinition = RuntimeAugments.GetGenericInstantiation(other, out otherGenericArgs);

                    return(_genericDefinition.Equals(otherGenericDefinition) && TypeLoaderEnvironment.Instance.CanInstantiationsShareCode(_genericArgs, otherGenericArgs, _canonKind));
                }
                else
                {
                    return(false);
                }
            }
            else
            {
                return(_typeToFind.Equals(other));
            }
        }
        protected override void GetDependenciesDueToEETypePresence(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
        {
            if (!ConstructedEETypeNode.CreationAllowed(type))
            {
                // Both EETypeNode and ConstructedEETypeNode call into this logic. EETypeNode will only call for unconstructable
                // EETypes. We don't have templates for those.
                return;
            }

            DefType closestDefType = type.GetClosestDefType();

            // TODO-SIZE: this is overly generous in the templates we create
            if (closestDefType.HasInstantiation)
            {
                TypeDesc canonType           = type.ConvertToCanonForm(CanonicalFormKind.Specific);
                TypeDesc canonClosestDefType = closestDefType.ConvertToCanonForm(CanonicalFormKind.Specific);

                // Add a dependency on the template for this type, if the canonical type should be generated into this binary.
                // If the type is an array type, the check should be on its underlying Array<T> type. This is because a copy of
                // an array type gets placed into each module but the Array<T> type only exists in the defining module and only
                // one template is needed for the Array<T> type by the dynamic type loader.
                if (canonType.IsCanonicalSubtype(CanonicalFormKind.Any) && !factory.NecessaryTypeSymbol(canonClosestDefType).RepresentsIndirectionCell)
                {
                    dependencies.Add(factory.NativeLayout.TemplateTypeLayout(canonType), "Template Type Layout");
                }
            }
        }
Beispiel #3
0
        protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType implType)
        {
            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.
                                return(null);
                            }
                        }
                    }
                }

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

                    if (slotDefiningMethodImpl != slotDefiningMethodDecl)
                    {
                        // We cannot resolve virtual method in case the impl is a different slot from the declMethod
                        impl = null;
                    }
                }
            }

            return(impl);
        }
Beispiel #4
0
        /// <summary>
        /// Gets a fully canonicalized base type if base type is canonical, or unmodified base type otherwise.
        /// </summary>
        public static DefType NormalizedBaseType(this TypeDesc type)
        {
            // Base type for Foo<__Canon> where Foo is defined as
            // class Foo<T> : Bar<T, string> { }
            // is Bar<__Canon, string>. This method normalizes it to Bar<__Canon, __Canon>.
            DefType baseType = type.BaseType;

            if (baseType != null && baseType.IsCanonicalSubtype(CanonicalFormKind.Any))
            {
                baseType = (DefType)baseType.ConvertToCanonForm(CanonicalFormKind.Specific);
            }
            return(baseType);
        }
Beispiel #5
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);
        }
        protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
        {
            DependencyList dependencyList = base.ComputeNonRelocationBasedDependencies(factory);

            // Ensure that we track the necessary type symbol if we are working with a constructed type symbol.
            // The emitter will ensure we don't emit both, but this allows us assert that we only generate
            // relocs to nodes we emit.
            dependencyList.Add(factory.NecessaryTypeSymbol(_type), "NecessaryType for constructed type");

            DefType closestDefType = _type.GetClosestDefType();

            if (InterfaceDispatchMapNode.MightHaveInterfaceDispatchMap(_type, factory))
            {
                dependencyList.Add(factory.InterfaceDispatchMap(_type), "Interface dispatch map");
            }

            if (_type.IsArray)
            {
                // Array EEType depends on System.Array's virtuals. Array EETypes don't point to
                // their base type (i.e. there's no reloc based dependency making this "just work").
                dependencyList.Add(factory.ConstructedTypeSymbol(_type.BaseType), "Array base type");

                ArrayType arrayType = (ArrayType)_type;
                if (arrayType.IsMdArray && arrayType.Rank == 1)
                {
                    // Allocating an MDArray of Rank 1 with zero lower bounds results in allocating
                    // an SzArray instead. Make sure the type loader can find the SzArray type.
                    dependencyList.Add(factory.ConstructedTypeSymbol(arrayType.ElementType.MakeArrayType()), "Rank 1 array");
                }
            }

            dependencyList.Add(factory.VTable(closestDefType), "VTable");

            if (closestDefType.HasInstantiation && factory.MetadataManager.SupportsReflection)
            {
                TypeDesc canonType           = _type.ConvertToCanonForm(CanonicalFormKind.Specific);
                TypeDesc canonClosestDefType = closestDefType.ConvertToCanonForm(CanonicalFormKind.Specific);

                // Add a dependency on the template for this type, if the canonical type should be generated into this binary.
                // If the type is an array type, the check should be on its underlying Array<T> type. This is because a copy of
                // an array type gets placed into each module but the Array<T> type only exists in the defining module and only
                // one template is needed for the Array<T> type by the dynamic type loader.
                if (canonType.IsCanonicalSubtype(CanonicalFormKind.Any) && !factory.NecessaryTypeSymbol(canonClosestDefType).RepresentsIndirectionCell)
                {
                    dependencyList.Add(factory.NativeLayout.TemplateTypeLayout(canonType), "Template Type Layout");
                }
            }

            // 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"));

                AddDependenciesForUniversalGVMSupport(factory, _type, ref dependencyList);
            }

            if (factory.TypeSystemContext.HasLazyStaticConstructor(_type))
            {
                // The fact that we generated an EEType means that someone can call RuntimeHelpers.RunClassConstructor.
                // We need to make sure this is possible.
                dependencyList.Add(new DependencyListEntry(factory.TypeNonGCStaticsSymbol((MetadataType)_type), "Class constructor"));
            }

            // Ask the metadata manager if we have any dependencies due to reflectability.
            factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencyList, factory, _type);

            factory.InteropStubManager.AddInterestingInteropConstructedTypeDependencies(ref dependencyList, factory, _type);

            return(dependencyList);
        }