/// <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); }
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); }
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); }
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); }
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); }
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); }
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); }
/// <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); } }
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);
private SealedVTableEntry(MethodDesc method, DefType interfaceDefinition) { Debug.Assert(interfaceDefinition == null || method.GetTypicalMethodDefinition().OwningType == interfaceDefinition.GetTypeDefinition()); (_method, _interfaceDefinition) = (method, interfaceDefinition); }