private static IntPtr RhResolveDispatchWorker(object pObject, EEType* pInterfaceType, ushort slot) { // Type of object we're dispatching on. EEType* pInstanceType = pObject.EEType; // Type whose DispatchMap is used. Usually the same as the above but for types which implement ICastable // we may repeat this process with an alternate type. EEType* pResolvingInstanceType = pInstanceType; IntPtr pTargetCode = DispatchResolve.FindInterfaceMethodImplementationTarget(pResolvingInstanceType, pInterfaceType, slot); if (pTargetCode == IntPtr.Zero && pInstanceType->IsICastable) { // Dispatch not resolved through normal dispatch map, try using the ICastable IntPtr pfnGetImplTypeMethod = pInstanceType->ICastableGetImplTypeMethod; pResolvingInstanceType = (EEType*)CalliIntrinsics.Call<IntPtr>(pfnGetImplTypeMethod, pObject, new IntPtr(pInterfaceType)); pTargetCode = DispatchResolve.FindInterfaceMethodImplementationTarget(pResolvingInstanceType, pInterfaceType, slot); } return pTargetCode; }
private static bool FindImplSlotForCurrentType(EEType* pTgtType, EEType* pItfType, UInt16 itfSlotNumber, UInt16* pImplSlotNumber) { bool fRes = false; // If making a call and doing virtual resolution don't look into the dispatch map, // take the slot number directly. if (!pItfType->IsInterface) { *pImplSlotNumber = itfSlotNumber; // Only notice matches if the target type and search types are the same // This will make dispatch to sealed slots work correctly return pTgtType == pItfType; } if (pTgtType->HasDispatchMap) { // For variant interface dispatch, the algorithm is to walk the parent hierarchy, and at each level // attempt to dispatch exactly first, and then if that fails attempt to dispatch variantly. This can // result in interesting behavior such as a derived type only overriding one particular instantiation // and funneling all the dispatches to it, but its the algorithm. bool fDoVariantLookup = false; // do not check variance for first scan of dispatch map fRes = FindImplSlotInSimpleMap( pTgtType, pItfType, itfSlotNumber, pImplSlotNumber, fDoVariantLookup); if (!fRes) { fDoVariantLookup = true; // check variance for second scan of dispatch map fRes = FindImplSlotInSimpleMap( pTgtType, pItfType, itfSlotNumber, pImplSlotNumber, fDoVariantLookup); } } return fRes; }
#pragma warning restore public static IntPtr FindInterfaceMethodImplementationTarget(EEType* pTgtType, EEType* pItfType, ushort itfSlotNumber) { // Start at the current type and work up the inheritance chain EEType* pCur = pTgtType; UInt32 iCurInheritanceChainDelta = 0; if (pItfType->IsCloned) pItfType = pItfType->CanonicalEEType; while (pCur != null) { UInt16 implSlotNumber; if (FindImplSlotForCurrentType( pCur, pItfType, itfSlotNumber, &implSlotNumber)) { IntPtr targetMethod; if (implSlotNumber < pCur->NumVTableSlots) { // true virtual - need to get the slot from the target type in case it got overridden targetMethod = pTgtType->GetVTableStartAddress()[implSlotNumber]; } else { // sealed virtual - need to get the slot form the implementing type, because // it's not present on the target type targetMethod = pCur->GetSealedVirtualSlot((ushort)(implSlotNumber - pCur->NumVTableSlots)); } return targetMethod; } if (pCur->IsArray) pCur = pCur->ArrayBaseType; else pCur = pCur->NonArrayBaseType; iCurInheritanceChainDelta++; } return IntPtr.Zero; }
// Method to compare two types pointers for type equality // We cannot just compare the pointers as there can be duplicate type instances // for cloned and constructed types. // There are three separate cases here // 1. The pointers are Equal => true // 2. Either one or both the types are CLONED, follow to the canonical EEType and check // 3. For Arrays/Pointers, we have to further check for rank and element type equality static private unsafe bool AreTypesEquivalentInternal(EEType* pType1, EEType* pType2) { if (pType1 == pType2) return true; if (pType1->IsCloned) pType1 = pType1->CanonicalEEType; if (pType2->IsCloned) pType2 = pType2->CanonicalEEType; if (pType1 == pType2) return true; if (pType1->IsParameterizedType && pType2->IsParameterizedType) return AreTypesEquivalentInternal(pType1->RelatedParameterType, pType2->RelatedParameterType) && pType1->ParameterizedTypeShape == pType2->ParameterizedTypeShape; return false; }
// Internally callable version of the export method above. Has two additional parameters: // fBoxedSource : assume the source type is boxed so that value types and enums are // compatible with Object, ValueType and Enum (if applicable) // fAllowSizeEquivalence : allow identically sized integral types and enums to be considered // equivalent (currently used only for array element types) static internal unsafe bool AreTypesAssignableInternal(EEType* pSourceType, EEType* pTargetType, bool fBoxedSource, bool fAllowSizeEquivalence) { // // Are the types identical? // if (AreTypesEquivalentInternal(pSourceType, pTargetType)) return true; // // Handle cast to interface cases. // if (pTargetType->IsInterface) { // Value types can only be cast to interfaces if they're boxed. if (!fBoxedSource && pSourceType->IsValueType) return false; if (ImplementsInterface(pSourceType, pTargetType)) return true; // Are the types compatible due to generic variance? if (pTargetType->HasGenericVariance && pSourceType->HasGenericVariance) return TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType); return false; } if (pSourceType->IsInterface) { // The only non-interface type an interface can be cast to is Object. return WellKnownEETypes.IsSystemObject(pTargetType); } // // Handle cast to array or pointer cases. // if (pTargetType->IsParameterizedType) { if (pSourceType->IsParameterizedType && (pTargetType->ParameterizedTypeShape == pSourceType->ParameterizedTypeShape)) { // Source type is also a parameterized type. Are the parameter types compatible? Note that using // AreTypesAssignableInternal here handles array covariance as well as IFoo[] -> Foo[] // etc. Pass false for fBoxedSource since int[] is not assignable to object[]. if (pSourceType->RelatedParameterType->IsPointerTypeDefinition) { // If the parameter types are pointers, then only exact matches are correct. // As we've already called AreTypesEquivalent at the start of this function, // return false as the exact match case has already been handled. // int** is not compatible with uint**, nor is int*[] oompatible with uint*[]. return false; } else { return AreTypesAssignableInternal(pSourceType->RelatedParameterType, pTargetType->RelatedParameterType, false, true); } } // Can't cast a non-parameter type to a parameter type or a parameter type of different shape to a parameter type return false; } if (pSourceType->IsArray) { // Target type is not an array. But we can still cast arrays to Object or System.Array. return WellKnownEETypes.IsSystemObject(pTargetType) || WellKnownEETypes.IsSystemArray(pTargetType); } else if (pSourceType->IsParameterizedType) { return false; } // // Handle cast to other (non-interface, non-array) cases. // if (pSourceType->IsValueType) { // Certain value types of the same size are treated as equivalent when the comparison is // between array element types (indicated by fAllowSizeEquivalence). These are integer types // of the same size (e.g. int and uint) and the base type of enums vs all integer types of the // same size. if (fAllowSizeEquivalence && pTargetType->IsValueType) { if (ArePrimitveTypesEquivalentSize(pSourceType, pTargetType)) return true; // Non-identical value types aren't equivalent in any other case (since value types are // sealed). return false; } // If the source type is a value type but it's not boxed then we've run out of options: the types // are not identical, the target type isn't an interface and we're not allowed to check whether // the target type is a parent of this one since value types are sealed and thus the only matches // would be against Object, ValueType or Enum, all of which are reference types and not compatible // with non-boxed value types. if (!fBoxedSource) return false; } // Sub case of casting between two instantiations of the same delegate type where one or more of // the type parameters have variance. Only interfaces and delegate types can have variance over // their type parameters and we know that neither type is an interface due to checks above. if (pTargetType->HasGenericVariance && pSourceType->HasGenericVariance) { // We've dealt with the identical case at the start of this method. And the regular path below // will handle casting to Object, Delegate and MulticastDelegate. Since we don't support // deriving from user delegate classes any further all we have to check here is that the // uninstantiated generic delegate definitions are the same and the type parameters are // compatible. return TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType); } // Is the source type derived from the target type? if (IsDerived(pSourceType, pTargetType)) return true; return false; }
static internal unsafe bool ImplementsInterface(EEType* pObjType, EEType* pTargetType) { Debug.Assert(!pTargetType->IsParameterizedType, "did not expect paramterized type"); Debug.Assert(pTargetType->IsInterface, "IsInstanceOfInterface called with non-interface EEType"); // This can happen with generic interface types // Debug.Assert(!pTargetType->IsCloned, "cloned interface types are disallowed"); // canonicalize target type if (pTargetType->IsCloned) pTargetType = pTargetType->CanonicalEEType; int numInterfaces = pObjType->NumInterfaces; EEInterfaceInfo* interfaceMap = pObjType->InterfaceMap; for (int i = 0; i < numInterfaces; i++) { EEType* pInterfaceType = interfaceMap[i].InterfaceType; // canonicalize the interface type if (pInterfaceType->IsCloned) pInterfaceType = pInterfaceType->CanonicalEEType; if (pInterfaceType == pTargetType) { return true; } } // We did not find the interface type in the list of supported interfaces. There's still one // chance left: if the target interface is generic and one or more of its type parameters is co or // contra variant then the object can still match if it implements a different instantiation of // the interface with type compatible generic arguments. // // An additional edge case occurs because of array covariance. This forces us to treat any generic // interfaces implemented by arrays as covariant over their one type parameter. bool fArrayCovariance = pObjType->IsArray; if (pTargetType->HasGenericVariance || (fArrayCovariance && pTargetType->IsGeneric)) { // Grab details about the instantiation of the target generic interface. EETypeRef* pTargetInstantiation; int targetArity; GenericVariance* pTargetVarianceInfo; EEType* pTargetGenericType = InternalCalls.RhGetGenericInstantiation(pTargetType, &targetArity, &pTargetInstantiation, &pTargetVarianceInfo); Debug.Assert(pTargetVarianceInfo != null, "did not expect empty variance info"); for (int i = 0; i < numInterfaces; i++) { EEType* pInterfaceType = interfaceMap[i].InterfaceType; // We can ignore interfaces which are not also marked as having generic variance // unless we're dealing with array covariance. if (pInterfaceType->HasGenericVariance || (fArrayCovariance && pInterfaceType->IsGeneric)) { // Grab instantiation details for the candidate interface. EETypeRef* pInterfaceInstantiation; int interfaceArity; GenericVariance* pInterfaceVarianceInfo; EEType* pInterfaceGenericType = InternalCalls.RhGetGenericInstantiation(pInterfaceType, &interfaceArity, &pInterfaceInstantiation, &pInterfaceVarianceInfo); Debug.Assert(pInterfaceVarianceInfo != null, "did not expect empty variance info"); // If the generic types aren't the same then the types aren't compatible. if (pInterfaceGenericType != pTargetGenericType) continue; // The types represent different instantiations of the same generic type. The // arity of both had better be the same. Debug.Assert(targetArity == interfaceArity, "arity mismatch betweeen generic instantiations"); // Compare the instantiations to see if they're compatible taking variance into account. if (TypeParametersAreCompatible(targetArity, pInterfaceInstantiation, pTargetInstantiation, pTargetVarianceInfo, fArrayCovariance)) return true; } } } return false; }
public unsafe static bool AreTypesAssignableInternal(EEType* pSourceType, EEType* pTargetType, AssignmentVariation variation) { // Important special case -- it breaks infinite recursion in CastCache itself! if (pSourceType == pTargetType) return true; Key key = new Key(pSourceType, pTargetType, variation); Entry entry = LookupInCache(s_cache, ref key); if (entry == null) return CacheMiss(ref key); return entry.Result; }
internal unsafe extern static object RhpNewFastMisalign(EEType * pEEType);
internal unsafe extern static object RhpNewFinalizableAlign8(EEType* pEEType);
internal unsafe extern static void RhpGetDispatchCellInfo(IntPtr pCell, EEType** pInterfaceType, ushort* slot);
internal unsafe extern static IntPtr RhpGetSealedVirtualSlot(EEType* pEEType, ushort slot);
internal unsafe extern static DispatchResolve.DispatchMap* RhpGetDispatchMap(EEType* pEEType);
internal unsafe extern static bool RhpHasDispatchMap(EEType* pEETypen);
internal unsafe extern static EEType* RhpGetArrayBaseType(EEType* pEEType);
internal unsafe extern static void RhUnbox(object obj, void* pData, EEType* pUnboxToEEType);
internal extern static unsafe IntPtr RhpGetICastableGetImplTypeMethod(EEType* pEEType);
internal unsafe extern static object RhpNewFastAlign8(EEType * pEEType); // BEWARE: not for finalizable objects!
internal unsafe extern static IntPtr RhpSearchDispatchCellCache(IntPtr pCell, EEType* pInstanceType);
internal unsafe extern static object RhpNewArrayAlign8(EEType* pEEType, int length);
internal unsafe extern static IntPtr RhpUpdateDispatchCellCache(IntPtr pCell, IntPtr pTargetCode, EEType* pInstanceType);
public Key(EEType* pSourceType, EEType* pTargetType, AssignmentVariation variation) { Debug.Assert((((long)pSourceType) & 3) == 0, "misaligned EEType!"); Debug.Assert(((uint)variation) <= 3, "variation enum has an unexpectedly large value!"); _sourceTypeAndVariation = (IntPtr)(((byte*)pSourceType) + ((int)variation)); _targetType = (IntPtr)pTargetType; }
internal extern static unsafe EEType* RhGetGenericInstantiation(EEType* pEEType, int* pArity, EETypeRef** ppInstantiation, GenericVariance** ppVarianceInfo);
unsafe static bool IsInstanceOfInterfaceViaICastable(object obj, EEType* pTargetType) { // Call the ICastable.IsInstanceOfInterface method directly rather than via an interface // dispatch since we know the method address statically. We ignore any cast error exception // object passed back on failure (result == false) since IsInstanceOfInterface never throws. IntPtr pfnIsInstanceOfInterface = obj.EEType->ICastableIsInstanceOfInterfaceMethod; Exception castError = null; if (CalliIntrinsics.Call<bool>(pfnIsInstanceOfInterface, obj, pTargetType, out castError)) return true; return false; }
internal extern static unsafe UInt32 RhpGetEETypeRareFlags(EEType* pEEType);
// Compare two types to see if they are compatible via generic variance. static private unsafe bool TypesAreCompatibleViaGenericVariance(EEType* pSourceType, EEType* pTargetType) { // Get generic instantiation metadata for both types. EETypeRef* pTargetInstantiation; int targetArity; GenericVariance* pTargetVarianceInfo; EEType* pTargetGenericType = InternalCalls.RhGetGenericInstantiation(pTargetType, &targetArity, &pTargetInstantiation, &pTargetVarianceInfo); Debug.Assert(pTargetVarianceInfo != null, "did not expect empty variance info"); EETypeRef* pSourceInstantiation; int sourceArity; GenericVariance* pSourceVarianceInfo; EEType* pSourceGenericType = InternalCalls.RhGetGenericInstantiation(pSourceType, &sourceArity, &pSourceInstantiation, &pSourceVarianceInfo); Debug.Assert(pSourceVarianceInfo != null, "did not expect empty variance info"); // If the generic types aren't the same then the types aren't compatible. if (pSourceGenericType == pTargetGenericType) { // The types represent different instantiations of the same generic type. The // arity of both had better be the same. Debug.Assert(targetArity == sourceArity, "arity mismatch betweeen generic instantiations"); // Compare the instantiations to see if they're compatible taking variance into account. if (TypeParametersAreCompatible(targetArity, pSourceInstantiation, pTargetInstantiation, pTargetVarianceInfo, false)) { return true; } } return false; }
internal extern static unsafe byte RhpGetNullableEETypeValueOffset(EEType* pEEType);
static internal unsafe bool IsDerived(EEType* pDerivedType, EEType* pBaseType) { Debug.Assert(!pDerivedType->IsArray, "did not expect array type"); Debug.Assert(!pDerivedType->IsParameterizedType, "did not expect parameterType"); Debug.Assert(!pBaseType->IsArray, "did not expect array type"); Debug.Assert(!pBaseType->IsInterface, "did not expect interface type"); Debug.Assert(!pBaseType->IsParameterizedType, "did not expect parameterType"); Debug.Assert(pBaseType->IsCanonical || pBaseType->IsCloned || pBaseType->IsGenericTypeDefinition, "unexpected eetype"); Debug.Assert(pDerivedType->IsCanonical || pDerivedType->IsCloned || pDerivedType->IsGenericTypeDefinition, "unexpected eetype"); // If a generic type definition reaches this function, then the function should return false unless the types are equivalent. // This works as the NonClonedNonArrayBaseType of a GenericTypeDefinition is always null. if (pBaseType->IsCloned) pBaseType = pBaseType->CanonicalEEType; do { if (pDerivedType->IsCloned) pDerivedType = pDerivedType->CanonicalEEType; if (pDerivedType == pBaseType) return true; pDerivedType = pDerivedType->NonClonedNonArrayBaseType; } while (pDerivedType != null); return false; }
internal extern static unsafe EEType* RhpGetNullableEEType(EEType* pEEType);
// Returns true of the two types are equivalent primitive types. Used by array casts. static private unsafe bool ArePrimitveTypesEquivalentSize(EEType* pType1, EEType* pType2) { CorElementType sourceCorType = pType1->CorElementType; int sourcePrimitiveTypeEquivalenceSize = GetIntegralTypeMatchSize(sourceCorType); // Quick check to see if the first type is even primitive. if (sourcePrimitiveTypeEquivalenceSize == 0) return false; CorElementType targetCorType = pType2->CorElementType; int targetPrimitiveTypeEquivalenceSize = GetIntegralTypeMatchSize(targetCorType); return sourcePrimitiveTypeEquivalenceSize == targetPrimitiveTypeEquivalenceSize; }
internal extern static unsafe IntPtr RhpGetICastableIsInstanceOfInterfaceMethod(EEType* pEEType);