// Returns an address in the module most closely associated with this EEType that can be handed to // EH.GetClasslibException and use to locate the compute the correct exception type. In most cases // this is just the EEType pointer itself, but when this type represents a generic that has been // unified at runtime (and thus the EEType pointer resides in the process heap rather than a specific // module) we need to do some work. internal unsafe IntPtr GetAssociatedModuleAddress() { fixed(EEType *pThis = &this) { if (!IsRuntimeAllocated && !IsDynamicType) { return((IntPtr)pThis); } // There are currently three types of runtime allocated EETypes, arrays, pointers, and generic types. // Arrays/Pointers can be handled by looking at their element type. if (IsParameterizedType) { return(pThis->RelatedParameterType->GetAssociatedModuleAddress()); } // Generic types are trickier. Often we could look at the parent type (since eventually it // would derive from the class library's System.Object which is definitely not runtime // allocated). But this breaks down for generic interfaces. Instead we fetch the generic // instantiation information and use the generic type definition, which will always be module // local. We know this lookup will succeed since we're dealing with a unified generic type // and the unification process requires this metadata. EETypeRef * pInstantiation; int arity; GenericVariance *pVarianceInfo; EEType * pGenericType = InternalCalls.RhGetGenericInstantiation(pThis, &arity, &pInstantiation, &pVarianceInfo); Debug.Assert(pGenericType != null, "Generic type expected"); return((IntPtr)pGenericType); } }
// 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); }
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); }
private static bool FindImplSlotInSimpleMap(EEType *pTgtType, EEType *pItfType, UInt32 itfSlotNumber, UInt16 *pImplSlotNumber, bool actuallyCheckVariance) { Debug.Assert(pTgtType->HasDispatchMap, "Missing dispatch map"); EEType * pItfOpenGenericType = null; EETypeRef * pItfInstantiation = null; int itfArity = 0; GenericVariance *pItfVarianceInfo = null; bool fCheckVariance = false; bool fArrayCovariance = false; if (actuallyCheckVariance) { fCheckVariance = pItfType->HasGenericVariance; fArrayCovariance = pTgtType->IsArray; // Non-arrays can follow array variance rules iff // 1. They have one generic parameter // 2. That generic parameter is array covariant. // // This special case is to allow array enumerators to work if (!fArrayCovariance && pTgtType->HasGenericVariance) { EETypeRef * pTgtInstantiation; int tgtEntryArity; GenericVariance *pTgtVarianceInfo; EEType * pTgtEntryGenericType = InternalCalls.RhGetGenericInstantiation(pTgtType, &tgtEntryArity, &pTgtInstantiation, &pTgtVarianceInfo); if ((tgtEntryArity == 1) && pTgtVarianceInfo[0] == GenericVariance.ArrayCovariant) { fArrayCovariance = true; } } // Arrays are covariant even though you can both get and set elements (type safety is maintained by // runtime type checks during set operations). This extends to generic interfaces implemented on those // arrays. We handle this by forcing all generic interfaces on arrays to behave as though they were // covariant (over their one type parameter corresponding to the array element type). if (fArrayCovariance && pItfType->IsGeneric) { fCheckVariance = true; } // If there is no variance checking, there is no operation to perform. (The non-variance check loop // has already completed) if (!fCheckVariance) { return(false); } } DispatchMap * pMap = pTgtType->DispatchMap; DispatchMapEntry *i = &pMap->_dispatchMap; DispatchMapEntry *iEnd = (&pMap->_dispatchMap) + pMap->_entryCount; for (; i != iEnd; ++i) { if (i->_usInterfaceMethodSlot == itfSlotNumber) { EEType *pCurEntryType = pTgtType->InterfaceMap[i->_usInterfaceIndex].InterfaceType; if (pCurEntryType->IsCloned) { pCurEntryType = pCurEntryType->CanonicalEEType; } if (pCurEntryType == pItfType) { *pImplSlotNumber = i->_usImplMethodSlot; return(true); } else if (fCheckVariance && ((fArrayCovariance && pCurEntryType->IsGeneric) || pCurEntryType->HasGenericVariance)) { // Interface types don't match exactly but both the target interface and the current interface // in the map are marked as being generic with at least one co- or contra- variant type // parameter. So we might still have a compatible match. // Retrieve the unified generic instance for the callsite interface if we haven't already (we // lazily get this then cache the result since the lookup isn't necessarily cheap). if (pItfOpenGenericType == null) { pItfOpenGenericType = InternalCalls.RhGetGenericInstantiation(pItfType, &itfArity, &pItfInstantiation, &pItfVarianceInfo); } // Retrieve the unified generic instance for the interface we're looking at in the map. // Grab instantiation details for the candidate interface. EETypeRef * pCurEntryInstantiation; int curEntryArity; GenericVariance *pCurEntryVarianceInfo; EEType * pCurEntryGenericType = InternalCalls.RhGetGenericInstantiation(pCurEntryType, &curEntryArity, &pCurEntryInstantiation, &pCurEntryVarianceInfo); // If the generic types aren't the same then the types aren't compatible. if (pItfOpenGenericType != pCurEntryGenericType) { continue; } // The types represent different instantiations of the same generic type. The // arity of both had better be the same. Debug.Assert(itfArity == curEntryArity, "arity mismatch betweeen generic instantiations"); if (TypeCast.TypeParametersAreCompatible(itfArity, pCurEntryInstantiation, pItfInstantiation, pItfVarianceInfo, fArrayCovariance)) { *pImplSlotNumber = i->_usImplMethodSlot; return(true); } } } } return(false); }