Beispiel #1
0
        /// <summary>
        /// For a shared (canonical) default interface method, gets a method that can be used to call the
        /// method on a specific implementing class.
        /// </summary>
        public MethodDesc GetDefaultInterfaceMethodImplementationThunk(MethodDesc targetMethod, TypeDesc implementingClass, DefType interfaceOnDefinition)
        {
            Debug.Assert(targetMethod.IsSharedByGenericInstantiations);
            Debug.Assert(!targetMethod.Signature.IsStatic);
            Debug.Assert(!targetMethod.HasInstantiation);
            Debug.Assert(interfaceOnDefinition.GetTypeDefinition() == targetMethod.OwningType.GetTypeDefinition());
            Debug.Assert(targetMethod.OwningType.IsInterface);

            int interfaceIndex;

            if (implementingClass.IsInterface)
            {
                Debug.Assert(((MetadataType)implementingClass).IsDynamicInterfaceCastableImplementation());
                interfaceIndex = UseContextFromRuntime;
            }
            else
            {
                interfaceIndex = Array.IndexOf(implementingClass.GetTypeDefinition().RuntimeInterfaces, interfaceOnDefinition);
                Debug.Assert(interfaceIndex >= 0);
            }

            // Get a method that will inject the appropriate instantiation context to the
            // target default interface method.
            var        methodKey = new DefaultInterfaceMethodImplementationInstantiationThunkHashtableKey(targetMethod, interfaceIndex);
            MethodDesc thunk     = _dimThunkHashtable.GetOrCreateValue(methodKey);

            return(thunk);
        }
Beispiel #2
0
        public override ComputedStaticFieldLayout ComputeStaticFieldLayout(DefType defType, StaticLayoutKind layoutKind)
        {
            ComputedStaticFieldLayout layout = new ComputedStaticFieldLayout();

            if (defType.GetTypeDefinition() is EcmaType ecmaType)
            {
                // ECMA types are the only ones that can have statics
                ModuleFieldLayout moduleFieldLayout = _moduleFieldLayoutMap.GetOrCreateValue(ecmaType.EcmaModule);
                layout.GcStatics          = moduleFieldLayout.GcStatics;
                layout.NonGcStatics       = moduleFieldLayout.NonGcStatics;
                layout.ThreadGcStatics    = moduleFieldLayout.ThreadGcStatics;
                layout.ThreadNonGcStatics = moduleFieldLayout.ThreadNonGcStatics;
                if (defType is EcmaType nonGenericType)
                {
                    OffsetsForType offsetsForType;
                    if (moduleFieldLayout.TypeOffsets.TryGetValue(nonGenericType.Handle, out offsetsForType))
                    {
                        layout.Offsets = _moduleFieldLayoutMap.CalculateTypeLayout(defType, moduleFieldLayout.Module, offsetsForType);
                    }
                }
                else if (defType is InstantiatedType instantiatedType)
                {
                    layout.Offsets = _moduleFieldLayoutMap.GetOrAddDynamicLayout(defType, moduleFieldLayout);
                }
                else
                {
                    throw new NotImplementedException();
                }
            }
            return(layout);
        }
Beispiel #3
0
            public bool Matches(MethodDesc method, DefType interfaceDefinition)
            {
                Debug.Assert(method.GetTypicalMethodDefinition().OwningType == interfaceDefinition.GetTypeDefinition());
                Debug.Assert(interfaceDefinition.IsInterface);

                if (_method == method && _interfaceDefinition == interfaceDefinition)
                {
                    return(true);
                }

                return(false);
            }
Beispiel #4
0
        private DefType GetSimilarVector(DefType vectorOfTType)
        {
            if (_similarVectorOpenType == null)
            {
                if (_similarVectorName == "Unknown")
                {
                    return(null);
                }

                _similarVectorOpenType = ((MetadataType)vectorOfTType.GetTypeDefinition()).Module.GetType("System.Runtime.Intrinsics", _similarVectorName);
            }

            return(((MetadataType)_similarVectorOpenType).MakeInstantiatedType(vectorOfTType.Instantiation));
        }
        public static bool IsVariantInterfaceImplementation(NodeFactory factory, TypeDesc providingType, DefType implementedInterface)
        {
            Debug.Assert(implementedInterface.IsInterface);
            Debug.Assert(!implementedInterface.IsGenericDefinition);

            // If any of the implemented interfaces have variance, calls against compatible interface methods
            // could result in interface methods of this type being used (e.g. IEnumerable<object>.GetEnumerator()
            // can dispatch to an implementation of IEnumerable<string>.GetEnumerator()).
            bool result = false;

            if (implementedInterface.HasVariance)
            {
                TypeDesc interfaceDefinition = implementedInterface.GetTypeDefinition();
                for (int i = 0; i < interfaceDefinition.Instantiation.Length; i++)
                {
                    var variantParameter = (GenericParameterDesc)interfaceDefinition.Instantiation[i];
                    if (variantParameter.Variance != 0)
                    {
                        // Variant interface parameters that are instantiated over valuetypes are
                        // not actually variant. Neither are contravariant parameters instantiated
                        // over sealed types (there won't be another interface castable to it
                        // through variance on that parameter).
                        TypeDesc variantArgument = implementedInterface.Instantiation[i];
                        if (!variantArgument.IsValueType &&
                            (!variantArgument.IsSealed() || variantParameter.IsCovariant))
                        {
                            result = true;
                            break;
                        }
                    }
                }
            }

            if (!result &&
                (providingType.IsArray || providingType.GetTypeDefinition() == factory.ArrayOfTEnumeratorType) &&
                implementedInterface.HasInstantiation)
            {
                // We need to also do this for generic interfaces on arrays because they have a weird casting rule
                // that doesn't require the implemented interface to be variant to consider it castable.
                // For value types, we only need this when the array is castable by size (int[] and ICollection<uint>),
                // or it's a reference type (Derived[] and ICollection<Base>).
                TypeDesc elementType = providingType.IsArray ? ((ArrayType)providingType).ElementType : providingType.Instantiation[0];
                result =
                    CastingHelper.IsArrayElementTypeCastableBySize(elementType) ||
                    (elementType.IsDefType && !elementType.IsValueType);
            }

            return(result);
        }
Beispiel #6
0
            protected override void AppendNameForInstantiatedType(StringBuilder sb, DefType type)
            {
                AppendName(sb, type.GetTypeDefinition());
                sb.Append('<');

                for (int i = 0; i < type.Instantiation.Length; i++)
                {
                    if (i > 0)
                    {
                        sb.Append(", ");
                    }
                    AppendNameWithValueClassPrefix(sb, type.Instantiation[i]);
                }

                sb.Append('>');
            }
        private bool ContainsTypeLayoutUncached(TypeDesc type, HashSet <TypeDesc> recursionGuard)
        {
            if (type.IsValueType ||
                type.IsObject ||
                type.IsPrimitive ||
                type.IsEnum ||
                type.IsPointer ||
                type.IsFunctionPointer ||
                type.IsByRefLike ||
                type.IsCanonicalDefinitionType(CanonicalFormKind.Any))
            {
                return(true);
            }
            DefType defType = type.GetClosestDefType();

            if (!ContainsType(defType.GetTypeDefinition()))
            {
                return(false);
            }
            if (defType.BaseType != null && !ContainsTypeLayout(defType.BaseType, recursionGuard))
            {
                return(false);
            }
            foreach (TypeDesc genericArg in defType.Instantiation)
            {
                if (!ContainsTypeLayout(genericArg, recursionGuard))
                {
                    return(false);
                }
            }
            foreach (FieldDesc field in defType.GetFields())
            {
                if (!field.IsLiteral &&
                    !field.IsStatic &&
                    !field.HasRva &&
                    !ContainsTypeLayout(field.FieldType, recursionGuard))
                {
                    return(false);
                }
            }

            return(true);
        }
Beispiel #8
0
        public static int GetDefaultInterfaceMethodSlot(NodeFactory factory, MethodDesc method, TypeDesc implType, DefType interfaceOnDefinition, bool countDictionarySlots = true)
        {
            Debug.Assert(method.GetTypicalMethodDefinition().OwningType == interfaceOnDefinition.GetTypeDefinition());

            SealedVTableNode sealedVTable = factory.SealedVTable(implType);

            // Ensure the sealed vtable is built before computing the slot
            sealedVTable.BuildSealedVTableSlots(factory, relocsOnly: false /* GetVirtualMethodSlot is called in the final emission phase */);

            int sealedVTableSlot = sealedVTable.ComputeDefaultInterfaceMethodSlot(method, interfaceOnDefinition);

            if (sealedVTableSlot == -1)
            {
                return(-1);
            }

            int numVTableSlots = GetNumberOfSlotsInCurrentType(factory, implType, countDictionarySlots);

            return(numVTableSlots + sealedVTableSlot);
        }
Beispiel #9
0
        internal static bool TryComputeHasInstantiationDeterminedSize(DefType type, out bool hasInstantiationDeterminedSize)
        {
            Debug.Assert(type.HasInstantiation);

            NativeLayoutInfoLoadContext loadContextUniversal;
            NativeLayoutInfo            universalLayoutInfo;
            NativeParser parser = type.GetOrCreateTypeBuilderState().GetParserForUniversalNativeLayoutInfo(out loadContextUniversal, out universalLayoutInfo);

            if (parser.IsNull)
            {
                hasInstantiationDeterminedSize = false;
#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING
                MetadataType typeDefinition = type.GetTypeDefinition() as MetadataType;
                if (typeDefinition != null)
                {
                    TypeDesc []       universalCanonInstantiation = new TypeDesc[type.Instantiation.Length];
                    TypeSystemContext context            = type.Context;
                    TypeDesc          universalCanonType = context.UniversalCanonType;
                    for (int i = 0; i < universalCanonInstantiation.Length; i++)
                    {
                        universalCanonInstantiation[i] = universalCanonType;
                    }

                    DefType universalCanonForm = typeDefinition.MakeInstantiatedType(universalCanonInstantiation);
                    hasInstantiationDeterminedSize = universalCanonForm.InstanceFieldSize.IsIndeterminate;
                    return(true);
                }
#endif
                return(false);
            }

            int?flags = (int?)parser.GetUnsignedForBagElementKind(BagElementKind.TypeFlags);

            hasInstantiationDeterminedSize = flags.HasValue ?
                                             (((NativeFormat.TypeFlags)flags) & NativeFormat.TypeFlags.HasInstantiationDeterminedSize) != 0 :
                                             false;

            return(true);
        }
Beispiel #10
0
        /// <summary>
        /// Validates that it will be possible to create an EEType for '<paramref name="type"/>'.
        /// </summary>
        public static void CheckCanGenerateEEType(NodeFactory factory, TypeDesc type)
        {
            // Don't validate generic definitons
            if (type.IsGenericDefinition)
            {
                return;
            }

            // It must be possible to create an EEType for the base type of this type
            TypeDesc baseType = type.BaseType;

            if (baseType != null)
            {
                // Make sure EEType can be created for this.
                factory.NecessaryTypeSymbol(baseType);
            }

            // We need EETypes for interfaces
            foreach (var intf in type.RuntimeInterfaces)
            {
                // Make sure EEType can be created for this.
                factory.NecessaryTypeSymbol(intf);
            }

            // Validate classes, structs, enums, interfaces, and delegates
            DefType defType = type as DefType;

            if (defType != null)
            {
                // Ensure we can compute the type layout
                defType.ComputeInstanceLayout(InstanceLayoutKind.TypeAndFields);

                //
                // The fact that we generated an EEType means that someone can call RuntimeHelpers.RunClassConstructor.
                // We need to make sure this is possible.
                //
                if (factory.TypeSystemContext.HasLazyStaticConstructor(defType))
                {
                    defType.ComputeStaticFieldLayout(StaticLayoutKind.StaticRegionSizesAndFields);
                }

                // Make sure instantiation length matches the expectation
                // TODO: it might be more resonable for the type system to enforce this (also for methods)
                if (defType.Instantiation.Length != defType.GetTypeDefinition().Instantiation.Length)
                {
                    throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
                }

                foreach (TypeDesc typeArg in defType.Instantiation)
                {
                    // ByRefs, pointers, function pointers, and System.Void are never valid instantiation arguments
                    if (typeArg.IsByRef || typeArg.IsPointer || typeArg.IsFunctionPointer || typeArg.IsVoid)
                    {
                        throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
                    }

                    // TODO: validate constraints
                }
            }

            // Validate parameterized types
            ParameterizedType parameterizedType = type as ParameterizedType;

            if (parameterizedType != null)
            {
                TypeDesc parameterType = parameterizedType.ParameterType;

                // Make sure EEType can be created for this.
                factory.NecessaryTypeSymbol(parameterType);

                if (parameterizedType.IsArray)
                {
                    if (parameterType.IsPointer || parameterType.IsFunctionPointer)
                    {
                        // Arrays of pointers and function pointers are not currently supported
                        throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
                    }

                    int elementSize = parameterType.GetElementSize();
                    if (elementSize >= ushort.MaxValue)
                    {
                        // Element size over 64k can't be encoded in the GCDesc
                        throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadValueClassTooLarge, parameterType);
                    }
                }

                // Validate we're not constructing a type over a ByRef
                if (parameterType.IsByRef)
                {
                    // CLR compat note: "ldtoken int32&&" will actually fail with a message about int32&; "ldtoken int32&[]"
                    // will fail with a message about being unable to create an array of int32&. This is a middle ground.
                    throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
                }

                // It might seem reasonable to disallow array of void, but the CLR doesn't prevent that too hard.
                // E.g. "newarr void" will fail, but "newarr void[]" or "ldtoken void[]" will succeed.
            }

            // Function pointer EETypes are not currently supported
            if (type.IsFunctionPointer)
            {
                throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
            }
        }
Beispiel #11
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);
Beispiel #12
0
 private SealedVTableEntry(MethodDesc method, DefType interfaceDefinition)
 {
     Debug.Assert(interfaceDefinition == null || method.GetTypicalMethodDefinition().OwningType == interfaceDefinition.GetTypeDefinition());
     (_method, _interfaceDefinition) = (method, interfaceDefinition);
 }