public Entry(EEType eet) { etype = new EType(eet); fields = new List <Field>(); changedFlag = false; }
// cache must be a size which is a power of two. internal static unsafe object CastableTargetLookup(CastableObjectCacheEntry[] cache, EEType* interfaceType) { uint cacheMask = (uint)cache.Length - 1; uint bucket = interfaceType->HashCode & cacheMask; uint curbucket = bucket; // hash algorithm is open addressing with linear probing while (curbucket < cache.Length) { if (cache[curbucket].Type == interfaceType) return cache[curbucket].InstanceObjectForType; if (cache[curbucket].Type == null) return null; curbucket++; } // Handle wrap-around case curbucket = 0; while (curbucket < bucket) { if (cache[curbucket].Type == interfaceType) return cache[curbucket].InstanceObjectForType; if (cache[curbucket].Type == null) return null; curbucket++; } return null; }
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 void button2_Click_1(object sender, EventArgs e) { resetPanels(); EEType eet = (comboBoxEntrySelector.SelectedItem as EType).etype; project.entries[(int)eet] = new Entry(eet); //need to add the method to drop the flag "changed" in the corresponding entry }
#pragma warning restore public static IntPtr FindInterfaceMethodImplementationTarget(EEType* pTgtType, EEType* pItfType, ushort itfSlotNumber) { DynamicModule* dynamicModule = pTgtType->DynamicModule; // Use the dynamic module resolver if it's present if ((dynamicModule != null) && (dynamicModule->DynamicTypeSlotDispatchResolve != IntPtr.Zero)) { return CalliIntrinsics.Call<IntPtr>(dynamicModule->DynamicTypeSlotDispatchResolve, (IntPtr)pTgtType, (IntPtr)pItfType, 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->GetArrayEEType(); else pCur = pCur->NonArrayBaseType; iCurInheritanceChainDelta++; } return IntPtr.Zero; }
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; }
internal extern static unsafe IntPtr RhpGetICastableIsInstanceOfInterfaceMethod(EEType* pEEType);
internal extern static unsafe byte RhpGetNullableEETypeValueOffset(EEType* pEEType);
internal unsafe extern static IntPtr RhpUpdateDispatchCellCache(IntPtr pCell, IntPtr pTargetCode, EEType* pInstanceType, ref DispatchCellInfo newCellInfo);
internal unsafe extern static IntPtr RhpGetSealedVirtualSlot(EEType* pEEType, ushort slot);
internal unsafe extern static bool RhpHasDispatchMap(EEType* pEETypen);
internal unsafe extern static void RhUnbox(object obj, ref byte data, EEType* pUnboxToEEType);
private static bool ShouldTypedClauseCatchThisException(object exception, EEType* pClauseType) { if (TypeCast.IsInstanceOfClass(exception, pClauseType) != null) return true; if (s_pLowLevelObjectType == null) { // TODO: Avoid allocating here as that may fail s_pLowLevelObjectType = new System.Object().EEType; } // This allows the typical try { } catch { }--which expands to a typed catch of System.Object--to work on // all objects when the clause is in the low level runtime code. This special case is needed because // objects from foreign type systems are sometimes throw back up at runtime code and this is the only way // to catch them outside of having a filter with no type check in it, which isn't currently possible to // write in C#. See https://github.com/dotnet/roslyn/issues/4388 if (pClauseType->IsEquivalentTo(s_pLowLevelObjectType)) return true; return false; }
private static void CreateEETypeWorker(EEType *pTemplateEEType, UInt32 hashCodeOfNewType, int arity, bool requireVtableSlotMapping, TypeBuilderState state) { bool successful = false; IntPtr eeTypePtrPlusGCDesc = IntPtr.Zero; IntPtr dynamicDispatchMapPtr = IntPtr.Zero; DynamicModule *dynamicModulePtr = null; try { Debug.Assert((pTemplateEEType != null) || (state.TypeBeingBuilt as MetadataType != null)); // In some situations involving arrays we can find as a template a dynamically generated type. // In that case, the correct template would be the template used to create the dynamic type in the first // place. if (pTemplateEEType != null && pTemplateEEType->IsDynamicType) { pTemplateEEType = pTemplateEEType->DynamicTemplateType; } ModuleInfo moduleInfo = TypeLoaderEnvironment.GetModuleInfoForType(state.TypeBeingBuilt); dynamicModulePtr = moduleInfo.DynamicModulePtr; Debug.Assert(dynamicModulePtr != null); bool requiresDynamicDispatchMap = requireVtableSlotMapping && (pTemplateEEType != null) && pTemplateEEType->HasDispatchMap; uint valueTypeFieldPaddingEncoded = 0; int baseSize = 0; bool isValueType; bool hasFinalizer; bool isNullable; bool isArray; bool isGeneric; ushort componentSize = 0; ushort flags; ushort runtimeInterfacesLength = 0; bool isGenericEETypeDef = false; if (state.RuntimeInterfaces != null) { runtimeInterfacesLength = checked ((ushort)state.RuntimeInterfaces.Length); } if (pTemplateEEType != null) { valueTypeFieldPaddingEncoded = EEType.ComputeValueTypeFieldPaddingFieldValue( pTemplateEEType->ValueTypeFieldPadding, (uint)pTemplateEEType->FieldAlignmentRequirement); baseSize = (int)pTemplateEEType->BaseSize; isValueType = pTemplateEEType->IsValueType; hasFinalizer = pTemplateEEType->IsFinalizable; isNullable = pTemplateEEType->IsNullable; componentSize = pTemplateEEType->ComponentSize; flags = pTemplateEEType->Flags; isArray = pTemplateEEType->IsArray; isGeneric = pTemplateEEType->IsGeneric; Debug.Assert(pTemplateEEType->NumInterfaces == runtimeInterfacesLength); } else if (state.TypeBeingBuilt.IsGenericDefinition) { flags = (ushort)EETypeKind.GenericTypeDefEEType; isValueType = state.TypeBeingBuilt.IsValueType; if (isValueType) { flags |= (ushort)EETypeFlags.ValueTypeFlag; } if (state.TypeBeingBuilt.IsInterface) { flags |= (ushort)EETypeFlags.IsInterfaceFlag; } hasFinalizer = false; isArray = false; isNullable = false; isGeneric = false; isGenericEETypeDef = true; componentSize = checked ((ushort)state.TypeBeingBuilt.Instantiation.Length); baseSize = 0; } else { isValueType = state.TypeBeingBuilt.IsValueType; hasFinalizer = state.TypeBeingBuilt.HasFinalizer; isNullable = state.TypeBeingBuilt.GetTypeDefinition().IsNullable; flags = EETypeBuilderHelpers.ComputeFlags(state.TypeBeingBuilt); isArray = false; isGeneric = state.TypeBeingBuilt.HasInstantiation; if (state.TypeBeingBuilt.HasVariance) { state.GenericVarianceFlags = new int[state.TypeBeingBuilt.Instantiation.Length]; int i = 0; foreach (GenericParameterDesc gpd in state.TypeBeingBuilt.GetTypeDefinition().Instantiation) { state.GenericVarianceFlags[i] = (int)gpd.Variance; i++; } Debug.Assert(i == state.GenericVarianceFlags.Length); } } // TODO! Change to if template is Universal or non-Existent if (state.TypeSize.HasValue) { baseSize = state.TypeSize.Value; int baseSizeBeforeAlignment = baseSize; baseSize = MemoryHelpers.AlignUp(baseSize, IntPtr.Size); if (isValueType) { // Compute the valuetype padding size based on size before adding the object type pointer field to the size uint cbValueTypeFieldPadding = (uint)(baseSize - baseSizeBeforeAlignment); // Add Object type pointer field to base size baseSize += IntPtr.Size; valueTypeFieldPaddingEncoded = (uint)EEType.ComputeValueTypeFieldPaddingFieldValue(cbValueTypeFieldPadding, (uint)state.FieldAlignment.Value); } // Minimum base size is 3 pointers, and requires us to bump the size of an empty class type if (baseSize <= IntPtr.Size) { // ValueTypes should already have had their size bumped up by the normal type layout process Debug.Assert(!isValueType); baseSize += IntPtr.Size; } // Add sync block skew baseSize += IntPtr.Size; // Minimum basesize is 3 pointers Debug.Assert(baseSize >= (IntPtr.Size * 3)); } // Optional fields encoding int cbOptionalFieldsSize; OptionalFieldsRuntimeBuilder optionalFields; { optionalFields = new OptionalFieldsRuntimeBuilder(pTemplateEEType != null ? pTemplateEEType->OptionalFieldsPtr : null); UInt32 rareFlags = optionalFields.GetFieldValue(EETypeOptionalFieldTag.RareFlags, 0); rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeFlag; // Set the IsDynamicTypeFlag rareFlags &= ~(uint)EETypeRareFlags.NullableTypeViaIATFlag; // Remove the NullableTypeViaIATFlag flag rareFlags &= ~(uint)EETypeRareFlags.HasSealedVTableEntriesFlag; // Remove the HasSealedVTableEntriesFlag // we'll set IsDynamicTypeWithSealedVTableEntriesFlag instead // Set the IsDynamicTypeWithSealedVTableEntriesFlag if needed if (state.NumSealedVTableEntries > 0) { rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeWithSealedVTableEntriesFlag; } if (requiresDynamicDispatchMap) { rareFlags |= (uint)EETypeRareFlags.HasDynamicallyAllocatedDispatchMapFlag; } if (state.NonGcDataSize != 0) { rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeWithNonGcStatics; } if (state.GcDataSize != 0) { rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeWithGcStatics; } if (state.ThreadDataSize != 0) { rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeWithThreadStatics; } #if ARM if (state.FieldAlignment == 8) { rareFlags |= (uint)EETypeRareFlags.RequiresAlign8Flag; } else { rareFlags &= ~(uint)EETypeRareFlags.RequiresAlign8Flag; } if (state.IsHFA) { rareFlags |= (uint)EETypeRareFlags.IsHFAFlag; } else { rareFlags &= ~(uint)EETypeRareFlags.IsHFAFlag; } #endif if (state.HasStaticConstructor) { rareFlags |= (uint)EETypeRareFlags.HasCctorFlag; } else { rareFlags &= ~(uint)EETypeRareFlags.HasCctorFlag; } rareFlags |= (uint)EETypeRareFlags.HasDynamicModuleFlag; optionalFields.SetFieldValue(EETypeOptionalFieldTag.RareFlags, rareFlags); // Dispatch map is fetched either from template type, or from the dynamically allocated DispatchMap field optionalFields.ClearField(EETypeOptionalFieldTag.DispatchMap); optionalFields.ClearField(EETypeOptionalFieldTag.ValueTypeFieldPadding); if (valueTypeFieldPaddingEncoded != 0) { optionalFields.SetFieldValue(EETypeOptionalFieldTag.ValueTypeFieldPadding, valueTypeFieldPaddingEncoded); } // Compute size of optional fields encoding cbOptionalFieldsSize = optionalFields.Encode(); Debug.Assert(cbOptionalFieldsSize > 0); } // Note: The number of vtable slots on the EEType to create is not necessary equal to the number of // vtable slots on the template type for universal generics (see ComputeVTableLayout) ushort numVtableSlots = state.NumVTableSlots; // Compute the EEType size and allocate it EEType *pEEType; { // In order to get the size of the EEType to allocate we need the following information // 1) The number of VTable slots (from the TypeBuilderState) // 2) The number of Interfaces (from the template) // 3) Whether or not there is a finalizer (from the template) // 4) Optional fields size // 5) Whether or not the type is nullable (from the template) // 6) Whether or not the type has sealed virtuals (from the TypeBuilderState) int cbEEType = (int)EEType.GetSizeofEEType( numVtableSlots, runtimeInterfacesLength, hasFinalizer, true, isNullable, state.NumSealedVTableEntries > 0, isGeneric, state.NonGcDataSize != 0, state.GcDataSize != 0, state.ThreadDataSize != 0); // Dynamic types have an extra pointer-sized field that contains a pointer to their template type cbEEType += IntPtr.Size; // Check if we need another pointer sized field for a dynamic DispatchMap cbEEType += (requiresDynamicDispatchMap ? IntPtr.Size : 0); // Add another pointer sized field for a DynamicModule cbEEType += IntPtr.Size; int cbGCDesc = GetInstanceGCDescSize(state, pTemplateEEType, isValueType, isArray); int cbGCDescAligned = MemoryHelpers.AlignUp(cbGCDesc, IntPtr.Size); // Allocate enough space for the EEType + gcDescSize eeTypePtrPlusGCDesc = MemoryHelpers.AllocateMemory(cbGCDescAligned + cbEEType + cbOptionalFieldsSize); // Get the EEType pointer, and the template EEType pointer pEEType = (EEType *)(eeTypePtrPlusGCDesc + cbGCDescAligned); state.HalfBakedRuntimeTypeHandle = pEEType->ToRuntimeTypeHandle(); // Set basic EEType fields pEEType->ComponentSize = componentSize; pEEType->Flags = flags; pEEType->BaseSize = (uint)baseSize; pEEType->NumVtableSlots = numVtableSlots; pEEType->NumInterfaces = runtimeInterfacesLength; pEEType->HashCode = hashCodeOfNewType; // Write the GCDesc bool isSzArray = isArray ? state.ArrayRank < 1 : false; int arrayRank = isArray ? state.ArrayRank.Value : 0; CreateInstanceGCDesc(state, pTemplateEEType, pEEType, baseSize, cbGCDesc, isValueType, isArray, isSzArray, arrayRank); Debug.Assert(pEEType->HasGCPointers == (cbGCDesc != 0)); #if GENERICS_FORCE_USG if (state.NonUniversalTemplateType != null) { Debug.Assert(state.NonUniversalInstanceGCDescSize == cbGCDesc, "Non-universal instance GCDesc size not matching with universal GCDesc size!"); Debug.Assert(cbGCDesc == 0 || pEEType->HasGCPointers); // The TestGCDescsForEquality helper will compare 2 GCDescs for equality, 4 bytes at a time (GCDesc contents treated as integers), and will read the // GCDesc data in *reverse* order for instance GCDescs (subtracts 4 from the pointer values at each iteration). // - For the first GCDesc, we use (pEEType - 4) to point to the first 4-byte integer directly preceeding the EEType // - For the second GCDesc, given that the state.NonUniversalInstanceGCDesc already points to the first byte preceeding the template EEType, we // subtract 3 to point to the first 4-byte integer directly preceeding the template EEtype TestGCDescsForEquality(new IntPtr((byte *)pEEType - 4), state.NonUniversalInstanceGCDesc - 3, cbGCDesc, true); } #endif // Copy the encoded optional fields buffer to the newly allocated memory, and update the OptionalFields field on the EEType // It is important to set the optional fields first on the newly created EEType, because all other 'setters' // will assert that the type is dynamic, just to make sure we are not making any changes to statically compiled types pEEType->OptionalFieldsPtr = (byte *)pEEType + cbEEType; optionalFields.WriteToEEType(pEEType, cbOptionalFieldsSize); #if CORERT pEEType->PointerToTypeManager = PermanentAllocatedMemoryBlobs.GetPointerToIntPtr(moduleInfo.Handle); #endif pEEType->DynamicModule = dynamicModulePtr; // Copy VTable entries from template type int numSlotsFilled = 0; IntPtr *pVtable = (IntPtr *)((byte *)pEEType + sizeof(EEType)); if (pTemplateEEType != null) { IntPtr *pTemplateVtable = (IntPtr *)((byte *)pTemplateEEType + sizeof(EEType)); for (int i = 0; i < pTemplateEEType->NumVtableSlots; i++) { int vtableSlotInDynamicType = requireVtableSlotMapping ? state.VTableSlotsMapping.GetVTableSlotInTargetType(i) : i; if (vtableSlotInDynamicType != -1) { Debug.Assert(vtableSlotInDynamicType < numVtableSlots); IntPtr dictionaryPtrValue; if (requireVtableSlotMapping && state.VTableSlotsMapping.IsDictionarySlot(i, out dictionaryPtrValue)) { // This must be the dictionary pointer value of one of the base types of the // current universal generic type being constructed. pVtable[vtableSlotInDynamicType] = dictionaryPtrValue; // Assert that the current template vtable slot is also a NULL value since all // universal generic template types have NULL dictionary slot values in their vtables Debug.Assert(pTemplateVtable[i] == IntPtr.Zero); } else { pVtable[vtableSlotInDynamicType] = pTemplateVtable[i]; } numSlotsFilled++; } } } else if (isGenericEETypeDef) { // If creating a Generic Type Definition Debug.Assert(pEEType->NumVtableSlots == 0); } else { #if SUPPORTS_NATIVE_METADATA_TYPE_LOADING // Dynamically loaded type // Fill the vtable with vtable resolution thunks in all slots except for // the dictionary slots, which should be filled with dictionary pointers if those // dictionaries are already published. TypeDesc nextTypeToExamineForDictionarySlot = state.TypeBeingBuilt; TypeDesc typeWithDictionary; int nextDictionarySlot = GetMostDerivedDictionarySlot(ref nextTypeToExamineForDictionarySlot, out typeWithDictionary); for (int iSlot = pEEType->NumVtableSlots - 1; iSlot >= 0; iSlot--) { bool isDictionary = iSlot == nextDictionarySlot; if (!isDictionary) { pVtable[iSlot] = LazyVTableResolver.GetThunkForSlot(iSlot); } else { if (typeWithDictionary.RetrieveRuntimeTypeHandleIfPossible()) { pVtable[iSlot] = typeWithDictionary.RuntimeTypeHandle.GetDictionary(); } nextDictionarySlot = GetMostDerivedDictionarySlot(ref nextTypeToExamineForDictionarySlot, out typeWithDictionary); } numSlotsFilled++; } #else Environment.FailFast("Template type loader is null, but metadata based type loader is not in use"); #endif } Debug.Assert(numSlotsFilled == numVtableSlots); // Copy Pointer to finalizer method from the template type if (hasFinalizer) { if (pTemplateEEType != null) { pEEType->FinalizerCode = pTemplateEEType->FinalizerCode; } else { #if SUPPORTS_NATIVE_METADATA_TYPE_LOADING pEEType->FinalizerCode = LazyVTableResolver.GetFinalizerThunk(); #else Environment.FailFast("Template type loader is null, but metadata based type loader is not in use"); #endif } } } // Copy the sealed vtable entries if they exist on the template type if (state.NumSealedVTableEntries > 0) { state.HalfBakedSealedVTable = MemoryHelpers.AllocateMemory((int)state.NumSealedVTableEntries * IntPtr.Size); UInt32 cbSealedVirtualSlotsTypeOffset = pEEType->GetFieldOffset(EETypeField.ETF_SealedVirtualSlots); *((IntPtr *)((byte *)pEEType + cbSealedVirtualSlotsTypeOffset)) = state.HalfBakedSealedVTable; for (UInt16 i = 0; i < state.NumSealedVTableEntries; i++) { IntPtr value = pTemplateEEType->GetSealedVirtualSlot(i); pEEType->SetSealedVirtualSlot(value, i); } } // Create a new DispatchMap for the type if (requiresDynamicDispatchMap) { DispatchMap *pTemplateDispatchMap = (DispatchMap *)RuntimeAugments.GetDispatchMapForType(pTemplateEEType->ToRuntimeTypeHandle()); dynamicDispatchMapPtr = MemoryHelpers.AllocateMemory(pTemplateDispatchMap->Size); UInt32 cbDynamicDispatchMapOffset = pEEType->GetFieldOffset(EETypeField.ETF_DynamicDispatchMap); *((IntPtr *)((byte *)pEEType + cbDynamicDispatchMapOffset)) = dynamicDispatchMapPtr; DispatchMap *pDynamicDispatchMap = (DispatchMap *)dynamicDispatchMapPtr; pDynamicDispatchMap->NumEntries = pTemplateDispatchMap->NumEntries; for (int i = 0; i < pTemplateDispatchMap->NumEntries; i++) { DispatchMap.DispatchMapEntry *pTemplateEntry = (*pTemplateDispatchMap)[i]; DispatchMap.DispatchMapEntry *pDynamicEntry = (*pDynamicDispatchMap)[i]; pDynamicEntry->_usInterfaceIndex = pTemplateEntry->_usInterfaceIndex; pDynamicEntry->_usInterfaceMethodSlot = pTemplateEntry->_usInterfaceMethodSlot; if (pTemplateEntry->_usImplMethodSlot < pTemplateEEType->NumVtableSlots) { pDynamicEntry->_usImplMethodSlot = (ushort)state.VTableSlotsMapping.GetVTableSlotInTargetType(pTemplateEntry->_usImplMethodSlot); Debug.Assert(pDynamicEntry->_usImplMethodSlot < numVtableSlots); } else { // This is an entry in the sealed vtable. We need to adjust the slot number based on the number of vtable slots // in the dynamic EEType pDynamicEntry->_usImplMethodSlot = (ushort)(pTemplateEntry->_usImplMethodSlot - pTemplateEEType->NumVtableSlots + numVtableSlots); Debug.Assert(state.NumSealedVTableEntries > 0 && pDynamicEntry->_usImplMethodSlot >= numVtableSlots && (pDynamicEntry->_usImplMethodSlot - numVtableSlots) < state.NumSealedVTableEntries); } } } if (pTemplateEEType != null) { pEEType->DynamicTemplateType = pTemplateEEType; } else { // Use object as the template type for non-template based EETypes. This will // allow correct Module identification for types. if (state.TypeBeingBuilt.HasVariance) { // TODO! We need to have a variant EEType here if the type has variance, as the // CreateGenericInstanceDescForType requires it. However, this is a ridiculous api surface // When we remove GenericInstanceDescs from the product, get rid of this weird special // case pEEType->DynamicTemplateType = typeof(IEnumerable <int>).TypeHandle.ToEETypePtr(); } else { pEEType->DynamicTemplateType = typeof(object).TypeHandle.ToEETypePtr(); } } int nonGCStaticDataOffset = 0; if (!isArray && !isGenericEETypeDef) { nonGCStaticDataOffset = state.HasStaticConstructor ? -TypeBuilder.ClassConstructorOffset : 0; // create GC desc if (state.GcDataSize != 0 && state.GcStaticDesc == IntPtr.Zero) { int cbStaticGCDesc; state.GcStaticDesc = CreateStaticGCDesc(state.StaticGCLayout, out state.AllocatedStaticGCDesc, out cbStaticGCDesc); #if GENERICS_FORCE_USG TestGCDescsForEquality(state.GcStaticDesc, state.NonUniversalStaticGCDesc, cbStaticGCDesc, false); #endif } if (state.ThreadDataSize != 0 && state.ThreadStaticDesc == IntPtr.Zero) { int cbThreadStaticGCDesc; state.ThreadStaticDesc = CreateStaticGCDesc(state.ThreadStaticGCLayout, out state.AllocatedThreadStaticGCDesc, out cbThreadStaticGCDesc); #if GENERICS_FORCE_USG TestGCDescsForEquality(state.ThreadStaticDesc, state.NonUniversalThreadStaticGCDesc, cbThreadStaticGCDesc, false); #endif } // If we have a class constructor, our NonGcDataSize MUST be non-zero Debug.Assert(!state.HasStaticConstructor || (state.NonGcDataSize != 0)); } if (isGeneric) { if (!RuntimeAugments.CreateGenericInstanceDescForType(*(RuntimeTypeHandle *)&pEEType, arity, state.NonGcDataSize, nonGCStaticDataOffset, state.GcDataSize, (int)state.ThreadStaticOffset, state.GcStaticDesc, state.ThreadStaticDesc, state.GenericVarianceFlags)) { throw new OutOfMemoryException(); } } else { Debug.Assert(arity == 0 || isGenericEETypeDef); // We don't need to report the non-gc and gc static data regions and allocate them for non-generics, // as we currently place these fields directly into the image if (!isGenericEETypeDef && state.ThreadDataSize != 0) { // Types with thread static fields ALWAYS get a GID. The GID is used to perform GC // and lifetime management of the thread static data. However, these GIDs are only used for that // so the specified GcDataSize, etc are 0 if (!RuntimeAugments.CreateGenericInstanceDescForType(*(RuntimeTypeHandle *)&pEEType, 0, 0, 0, 0, (int)state.ThreadStaticOffset, IntPtr.Zero, state.ThreadStaticDesc, null)) { throw new OutOfMemoryException(); } } } if (state.Dictionary != null) { state.HalfBakedDictionary = state.Dictionary.Allocate(); } Debug.Assert(!state.HalfBakedRuntimeTypeHandle.IsNull()); Debug.Assert((state.NumSealedVTableEntries == 0 && state.HalfBakedSealedVTable == IntPtr.Zero) || (state.NumSealedVTableEntries > 0 && state.HalfBakedSealedVTable != IntPtr.Zero)); Debug.Assert((state.Dictionary == null && state.HalfBakedDictionary == IntPtr.Zero) || (state.Dictionary != null && state.HalfBakedDictionary != IntPtr.Zero)); successful = true; } finally { if (!successful) { if (eeTypePtrPlusGCDesc != IntPtr.Zero) { MemoryHelpers.FreeMemory(eeTypePtrPlusGCDesc); } if (dynamicDispatchMapPtr != IntPtr.Zero) { MemoryHelpers.FreeMemory(dynamicDispatchMapPtr); } if (state.HalfBakedSealedVTable != IntPtr.Zero) { MemoryHelpers.FreeMemory(state.HalfBakedSealedVTable); } if (state.HalfBakedDictionary != IntPtr.Zero) { MemoryHelpers.FreeMemory(state.HalfBakedDictionary); } if (state.AllocatedStaticGCDesc) { MemoryHelpers.FreeMemory(state.GcStaticDesc); } if (state.AllocatedThreadStaticGCDesc) { MemoryHelpers.FreeMemory(state.ThreadStaticDesc); } } } }
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; }
// Compare two types to see if they are compatible via generic variance. static private unsafe bool TypesAreCompatibleViaGenericVariance(EEType* pSourceType, EEType* pTargetType) { EEType* pTargetGenericType = pTargetType->GenericDefinition; EEType* pSourceGenericType = pSourceType->GenericDefinition; // If the generic types aren't the same then the types aren't compatible. if (pSourceGenericType == pTargetGenericType) { // Get generic instantiation metadata for both types. EETypeRef* pTargetInstantiation = pTargetType->GenericArguments; int targetArity = (int)pTargetType->GenericArity; GenericVariance* pTargetVarianceInfo = pTargetType->GenericVariance; Debug.Assert(pTargetVarianceInfo != null, "did not expect empty variance info"); EETypeRef* pSourceInstantiation = pSourceType->GenericArguments; int sourceArity = (int)pSourceType->GenericArity; GenericVariance* pSourceVarianceInfo = pSourceType->GenericVariance; Debug.Assert(pSourceVarianceInfo != null, "did not expect empty variance info"); // 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; }
public EType() { etype = EEType.article; }
internal unsafe extern static object RhpNewFastMisalign(EEType * pEEType);
public EType(EEType et) { etype = et; }
internal unsafe extern static EEType* RhpGetArrayBaseType(EEType* pEEType);
/// <summary> /// Return true if both types are good for simple casting: canonical, no related type via IAT, no generic variance /// </summary> internal static bool BothSimpleCasting(EEType* pThis, EEType* pOther) { return ((pThis->_usFlags | pOther->_usFlags) & (ushort)EETypeFlags.ComplexCastingMask) == (ushort)EETypeKind.CanonicalEEType; }
internal unsafe extern static DispatchResolve.DispatchMap* RhpGetDispatchMap(EEType* pEEType);
internal bool IsEquivalentTo(EEType* pOtherEEType) { fixed (EEType* pThis = &this) { if (pThis == pOtherEEType) return true; EEType* pThisEEType = pThis; if (pThisEEType->IsCloned) pThisEEType = pThisEEType->CanonicalEEType; if (pOtherEEType->IsCloned) pOtherEEType = pOtherEEType->CanonicalEEType; if (pThisEEType == pOtherEEType) return true; if (pThisEEType->IsParameterizedType && pOtherEEType->IsParameterizedType) { return pThisEEType->RelatedParameterType->IsEquivalentTo(pOtherEEType->RelatedParameterType) && pThisEEType->ParameterizedTypeShape == pOtherEEType->ParameterizedTypeShape; } } return false; }
internal unsafe extern static IntPtr RhpSearchDispatchCellCache(IntPtr pCell, EEType* pInstanceType);
// Returns true if the passed in EEType is the EEType for System.Object // This is recognized by the fact that System.Object and interfaces are the only ones without a base type internal static unsafe bool IsSystemObject(EEType* pEEType) { if (pEEType->IsArray) return false; return (pEEType->NonArrayBaseType == null) && !pEEType->IsInterface; }
internal extern static unsafe UInt32 RhpGetEETypeRareFlags(EEType* pEEType);
// Returns true if the passed in EEType is the EEType for System.Array. // The binder sets a special CorElementType for this well known type internal static unsafe bool IsSystemArray(EEType* pEEType) { return (pEEType->CorElementType == CorElementType.ELEMENT_TYPE_ARRAY); }
internal extern static unsafe EEType* RhpGetNullableEEType(EEType* pEEType);
internal void SetToCloneOf(EEType *pOrigType) { Debug.Assert((_usFlags & (ushort)EETypeFlags.EETypeKindMask) == 0, "should be a canonical type"); this._usFlags |= (ushort)EETypeKind.ClonedEEType; this._relatedType._pCanonicalType = pOrigType; }
internal extern static unsafe IntPtr RhpGetICastableGetImplTypeMethod(EEType* pEEType);
internal unsafe extern static object RhpNewFastAlign8(EEType * pEEType); // BEWARE: not for finalizable objects!
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. // // Interfaces which are only variant for arrays have the HasGenericVariance flag set even if they // are not variant. bool fArrayCovariance = pObjType->IsArray; if (pTargetType->HasGenericVariance) { // Grab details about the instantiation of the target generic interface. EEType* pTargetGenericType = pTargetType->GenericDefinition; EETypeRef* pTargetInstantiation = pTargetType->GenericArguments; int targetArity = (int)pTargetType->GenericArity; GenericVariance* pTargetVarianceInfo = pTargetType->GenericVariance; 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. // // Interfaces which are only variant for arrays have the HasGenericVariance flag set even if they // are not variant. if (pInterfaceType->HasGenericVariance) { EEType* pInterfaceGenericType = pInterfaceType->GenericDefinition; // If the generic types aren't the same then the types aren't compatible. if (pInterfaceGenericType != pTargetGenericType) continue; // Grab instantiation details for the candidate interface. EETypeRef* pInterfaceInstantiation = pInterfaceType->GenericArguments; int interfaceArity = (int)pInterfaceType->GenericArity; GenericVariance* pInterfaceVarianceInfo = pInterfaceType->GenericVariance; Debug.Assert(pInterfaceVarianceInfo != null, "did not expect empty variance info"); // 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; }
internal unsafe extern static object RhpNewFinalizableAlign8(EEType* pEEType);
// Internally callable version of the export method above. Has two additional flags: // 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, AssignmentVariation variation) { bool fBoxedSource = ((variation & AssignmentVariation.BoxedSource) == AssignmentVariation.BoxedSource); bool fAllowSizeEquivalence = ((variation & AssignmentVariation.AllowSizeEquivalence ) == AssignmentVariation.AllowSizeEquivalence); // // 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? if (pSourceType->RelatedParameterType->IsPointerType) { // 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 { // Note that using AreTypesAssignableInternal with AssignmentVariation.AllowSizeEquivalence // here handles array covariance as well as IFoo[] -> Foo[] etc. We are not using // AssignmentVariation.BoxedSource because int[] is not assignable to object[]. return CastCache.AreTypesAssignableInternal(pSourceType->RelatedParameterType, pTargetType->RelatedParameterType, AssignmentVariation.AllowSizeEquivalence); } } // 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; }
internal unsafe extern static object RhpNewArrayAlign8(EEType* pEEType, int length);