// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // ==--== // // File: CLASS.H // // // NOTE: Even though EEClass is considered to contain cold data (relative to MethodTable), these data // structures *are* touched (especially during startup as part of soft-binding). As a result, and given the // number of EEClasses allocated for large assemblies, the size of this structure can have a direct impact on // performance, especially startup performance. // // Given that the data itself is touched infrequently, we can trade off space reduction against cpu-usage to // good effect here. A fair amount of work has gone into reducing the size of each EEClass instance (see // EEClassOptionalFields) at the expense of somewhat more convoluted runtime access. // // Please consider this (and measure the impact of your changes against startup scenarios) before adding // fields to EEClass or otherwise increasing its size. // // ============================================================================ #ifndef CLASS_H #define CLASS_H /* * Include Files */ #include "eecontract.h" #include "argslot.h" #include "vars.hpp" #include "cor.h" #include "clrex.h" #include "hash.h" #include "crst.h" #include "cgensys.h" #ifdef FEATURE_COMINTEROP #include "stdinterfaces.h" #endif #include "slist.h" #include "spinlock.h" #include "typehandle.h" #include "methodtable.h" #include "eeconfig.h" #include "typectxt.h" #include "iterator_util.h" #include "array.h" VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr); /* * Macro definitions */ #define MAX_LOG2_PRIMITIVE_FIELD_SIZE 3 #define MAX_PRIMITIVE_FIELD_SIZE (1 << MAX_LOG2_PRIMITIVE_FIELD_SIZE) /* * Forward declarations */ class AppDomain; class ArrayClass; class ArrayMethodDesc; class Assembly; class ClassLoader; class DictionaryLayout; class FCallMethodDesc; class EEClass; class EnCFieldDesc; class FieldDesc; class NativeFieldDescriptor; class EEClassNativeLayoutInfo; struct LayoutRawFieldInfo; class MetaSig; class MethodDesc; class MethodDescChunk; class MethodTable; class Module; class Object; class Stub; class Substitution; class SystemDomain; class TypeHandle; class StackingAllocator; class AllocMemTracker; class InteropMethodTableSlotDataMap; class LoadingEntry_LockHolder; class DispatchMapBuilder; class LoaderAllocator; class ComCallWrapperTemplate; enum class ParseNativeTypeFlags : int; typedef DPTR(DictionaryLayout) PTR_DictionaryLayout; typedef DPTR(NativeFieldDescriptor) PTR_NativeFieldDescriptor; typedef DPTR(NativeFieldDescriptor const) PTR_ConstNativeFieldDescriptor; typedef DPTR(EEClassNativeLayoutInfo) PTR_EEClassNativeLayoutInfo; //--------------------------------------------------------------------------------- // Fields in an explicit-layout class present varying degrees of risk depending // on how they overlap. // // Each level is a superset of the lower (in numerical value) level - i.e. // all kVerifiable fields are also kLegal, but not vice-versa. //--------------------------------------------------------------------------------- class ExplicitFieldTrust { public: enum TrustLevel { // Note: order is important here - each guarantee also implicitly guarantees all promises // made by values lower in number. // What's guaranteed. What the loader does. //----- ----------------------- ------------------------------- kNone = 0, // no guarantees at all - Type refuses to load at all. kLegal = 1, // guarantees no objref <-> scalar overlap and no unaligned objref - Type loads but field access won't verify kVerifiable = 2, // guarantees no objref <-> objref overlap and all guarantees above - Type loads and field access will verify kNonOverlaid = 3, // guarantees no overlap at all and all guarantees above - Type loads, field access verifies and Equals() may be optimized if structure is tightly packed kMaxTrust = kNonOverlaid, }; }; //---------------------------------------------------------------------------------------------- // This class is a helper for HandleExplicitLayout. To make it harder to introduce security holes // into this function, we will manage all updates to the class's trust level through the ExplicitClassTrust // class. This abstraction enforces the rule that the overall class is only as trustworthy as // the least trustworthy field. //---------------------------------------------------------------------------------------------- class ExplicitClassTrust : private ExplicitFieldTrust { public: ExplicitClassTrust() { LIMITED_METHOD_CONTRACT; m_trust = kMaxTrust; // Yes, we start out with maximal trust. This reflects that explicit layout structures with no fields do represent no risk. } VOID AddField(TrustLevel fieldTrust) { LIMITED_METHOD_CONTRACT; m_trust = min(m_trust, fieldTrust); } BOOL IsLegal() { LIMITED_METHOD_CONTRACT; return m_trust >= kLegal; } BOOL IsVerifiable() { LIMITED_METHOD_CONTRACT; return m_trust >= kVerifiable; } BOOL IsNonOverlaid() { LIMITED_METHOD_CONTRACT; return m_trust >= kNonOverlaid; } TrustLevel GetTrustLevel() { LIMITED_METHOD_CONTRACT; return m_trust; } private: TrustLevel m_trust; }; //---------------------------------------------------------------------------------------------- // This class is a helper for HandleExplicitLayout. To make it harder to introduce security holes // into this function, this class will collect trust information about individual fields to be later // aggregated into the overall class level. // // This abstraction enforces the rule that all fields are presumed guilty until explicitly declared // safe by calling SetTrust(). If you fail to call SetTrust before leaving the block, the destructor // will automatically cause the entire class to be declared illegal (and you will get an assert // telling you to fix this bug.) //---------------------------------------------------------------------------------------------- class ExplicitFieldTrustHolder : private ExplicitFieldTrust { public: ExplicitFieldTrustHolder(ExplicitClassTrust *pExplicitClassTrust) { LIMITED_METHOD_CONTRACT; m_pExplicitClassTrust = pExplicitClassTrust; #ifdef _DEBUG m_trustDeclared = FALSE; #endif m_fieldTrust = kNone; } VOID SetTrust(TrustLevel fieldTrust) { LIMITED_METHOD_CONTRACT; _ASSERTE(fieldTrust >= kNone && fieldTrust <= kMaxTrust); _ASSERTE(!m_trustDeclared && "You should not set the trust value more than once."); #ifdef _DEBUG m_trustDeclared = TRUE; #endif m_fieldTrust = fieldTrust; } ~ExplicitFieldTrustHolder() { LIMITED_METHOD_CONTRACT; // If no SetTrust() was ever called, we will default to kNone (i.e. declare the entire type // illegal.) It'd be nice to assert here but since this case can be legitimately reached // on exception unwind, we cannot. m_pExplicitClassTrust->AddField(m_fieldTrust); } private: ExplicitClassTrust* m_pExplicitClassTrust; TrustLevel m_fieldTrust; #ifdef _DEBUG BOOL m_trustDeclared; // Debug flag to detect multiple Sets. (Which we treat as a bug as this shouldn't be necessary.) #endif }; //******************************************************************************* // Enumerator to traverse the interface declarations of a type, automatically building // a substitution chain on the stack. class InterfaceImplEnum { Module* m_pModule; HENUMInternalHolder hEnumInterfaceImpl; const Substitution *m_pSubstChain; Substitution m_CurrSubst; mdTypeDef m_CurrTok; public: InterfaceImplEnum(Module *pModule, mdTypeDef cl, const Substitution *pSubstChain) : hEnumInterfaceImpl(pModule->GetMDImport()) { WRAPPER_NO_CONTRACT; m_pModule = pModule; hEnumInterfaceImpl.EnumInit(mdtInterfaceImpl, cl); m_pSubstChain = pSubstChain; } // Returns: // S_OK ... if has next (TRUE) // S_FALSE ... if does not have next (FALSE) // error code. HRESULT Next() { WRAPPER_NO_CONTRACT; HRESULT hr; mdInterfaceImpl ii; if (!m_pModule->GetMDImport()->EnumNext(&hEnumInterfaceImpl, &ii)) { return S_FALSE; } IfFailRet(m_pModule->GetMDImport()->GetTypeOfInterfaceImpl(ii, &m_CurrTok)); m_CurrSubst = Substitution(m_CurrTok, m_pModule, m_pSubstChain); return S_OK; } const Substitution *CurrentSubst() const { LIMITED_METHOD_CONTRACT; return &m_CurrSubst; } mdTypeDef CurrentToken() const { LIMITED_METHOD_CONTRACT; return m_CurrTok; } }; #ifdef FEATURE_COMINTEROP // // Class used to map MethodTable slot numbers to COM vtable slots numbers // (either for calling a classic COM component or for constructing a classic COM // vtable via which COM components can call managed classes). This structure is // embedded in the EEClass but the mapping list itself is only allocated if the // COM vtable is sparse. // class SparseVTableMap { public: SparseVTableMap(); ~SparseVTableMap(); // First run through MT slots calling RecordGap wherever a gap in VT slots // occurs. void RecordGap(WORD StartMTSlot, WORD NumSkipSlots); // Then call FinalizeMapping to create the actual mapping list. void FinalizeMapping(WORD TotalMTSlots); // Map MT to VT slot. WORD LookupVTSlot(WORD MTSlot); // Retrieve the number of slots in the vtable (both empty and full). WORD GetNumVTableSlots(); const void* GetMapList() { LIMITED_METHOD_CONTRACT; return (void*)m_MapList; } private: enum { MapGrow = 4 }; struct Entry { WORD m_Start; // Starting MT slot number WORD m_Span; // # of consecutive slots that map linearly WORD m_MapTo; // Starting VT slot number }; Entry *m_MapList; // Pointer to array of Entry structures WORD m_MapEntries; // Number of entries in above WORD m_Allocated; // Number of entries allocated WORD m_LastUsed; // Index of last entry used in successful lookup WORD m_VTSlot; // Current VT slot number, used during list build WORD m_MTSlot; // Current MT slot number, used during list build void AllocOrExpand(); // Allocate or expand the mapping list for a new entry }; #endif // FEATURE_COMINTEROP //======================================================================= // Adjunct to the EEClass structure for classes w/ layout //======================================================================= class EEClassLayoutInfo { static VOID CollectLayoutFieldMetadataThrowing( mdTypeDef cl, // cl of the NStruct being loaded BYTE packingSize, // packing size (from @dll.struct) BYTE nlType, // nltype (from @dll.struct) BOOL fExplicitOffsets, // explicit offsets? MethodTable *pParentMT, // the loaded superclass ULONG cTotalFields, // total number of fields (instance and static) HENUMInternal *phEnumField, // enumerator for fields Module* pModule, // Module that defines the scope, loader and heap (for allocate FieldMarshalers) const SigTypeContext *pTypeContext, // Type parameters for NStruct being loaded EEClassLayoutInfo *pEEClassLayoutInfoOut, // caller-allocated structure to fill in. LayoutRawFieldInfo *pInfoArrayOut, // caller-allocated array to fill in. Needs room for cTotalFields+1 elements LoaderAllocator * pAllocator, AllocMemTracker *pamTracker ); friend class ClassLoader; friend class EEClass; friend class MethodTableBuilder; UINT32 m_cbManagedSize; public: BYTE m_ManagedLargestAlignmentRequirementOfAllMembers; private: enum { // TRUE if the GC layout of the class is bit-for-bit identical // to its unmanaged counterpart with the runtime marshalling system // (i.e. no internal reference fields, no ansi-unicode char conversions required, etc.) // Used to optimize marshaling. e_BLITTABLE = 0x01, // Is this type also sequential in managed memory? e_MANAGED_SEQUENTIAL = 0x02, // When a sequential/explicit type has no fields, it is conceptually // zero-sized, but actually is 1 byte in length. This holds onto this // fact and allows us to revert the 1 byte of padding when another // explicit type inherits from this type. e_ZERO_SIZED = 0x04, // The size of the struct is explicitly specified in the meta-data. e_HAS_EXPLICIT_SIZE = 0x08, // The type recursively has a field that is LayoutKind.Auto and not an enum. e_HAS_AUTO_LAYOUT_FIELD_IN_LAYOUT = 0x10, // Type type recursively has a field which is an Int128 e_IS_OR_HAS_INT128_FIELD = 0x20, }; BYTE m_bFlags; // Packing size in bytes (1, 2, 4, 8 etc.) BYTE m_cbPackingSize; public: UINT32 GetManagedSize() const { LIMITED_METHOD_CONTRACT; return m_cbManagedSize; } BOOL IsBlittable() const { LIMITED_METHOD_CONTRACT; return (m_bFlags & e_BLITTABLE) == e_BLITTABLE; } BOOL IsManagedSequential() const { LIMITED_METHOD_CONTRACT; return (m_bFlags & e_MANAGED_SEQUENTIAL) == e_MANAGED_SEQUENTIAL; } // If true, this says that the type was originally zero-sized // and the native size was bumped up to one for similar behaviour // to C++ structs. However, it is necessary to keep track of this // so that we can ignore the one byte padding if other types derive // from this type, that we can BOOL IsZeroSized() const { LIMITED_METHOD_CONTRACT; return (m_bFlags & e_ZERO_SIZED) == e_ZERO_SIZED; } BOOL HasExplicitSize() const { LIMITED_METHOD_CONTRACT; return (m_bFlags & e_HAS_EXPLICIT_SIZE) == e_HAS_EXPLICIT_SIZE; } BOOL HasAutoLayoutField() const { LIMITED_METHOD_CONTRACT; return (m_bFlags & e_HAS_AUTO_LAYOUT_FIELD_IN_LAYOUT) == e_HAS_AUTO_LAYOUT_FIELD_IN_LAYOUT; } BOOL IsInt128OrHasInt128Fields() const { LIMITED_METHOD_CONTRACT; return (m_bFlags & e_IS_OR_HAS_INT128_FIELD) == e_IS_OR_HAS_INT128_FIELD; } BYTE GetPackingSize() const { LIMITED_METHOD_CONTRACT; return m_cbPackingSize; } private: void SetIsBlittable(BOOL isBlittable) { LIMITED_METHOD_CONTRACT; m_bFlags = isBlittable ? (m_bFlags | e_BLITTABLE) : (m_bFlags & ~e_BLITTABLE); } void SetIsManagedSequential(BOOL isManagedSequential) { LIMITED_METHOD_CONTRACT; m_bFlags = isManagedSequential ? (m_bFlags | e_MANAGED_SEQUENTIAL) : (m_bFlags & ~e_MANAGED_SEQUENTIAL); } void SetIsZeroSized(BOOL isZeroSized) { LIMITED_METHOD_CONTRACT; m_bFlags = isZeroSized ? (m_bFlags | e_ZERO_SIZED) : (m_bFlags & ~e_ZERO_SIZED); } void SetHasExplicitSize(BOOL hasExplicitSize) { LIMITED_METHOD_CONTRACT; m_bFlags = hasExplicitSize ? (m_bFlags | e_HAS_EXPLICIT_SIZE) : (m_bFlags & ~e_HAS_EXPLICIT_SIZE); } void SetHasAutoLayoutField(BOOL hasAutoLayoutField) { LIMITED_METHOD_CONTRACT; m_bFlags = hasAutoLayoutField ? (m_bFlags | e_HAS_AUTO_LAYOUT_FIELD_IN_LAYOUT) : (m_bFlags & ~e_HAS_AUTO_LAYOUT_FIELD_IN_LAYOUT); } void SetIsInt128OrHasInt128Fields(BOOL hasInt128Field) { LIMITED_METHOD_CONTRACT; m_bFlags = hasInt128Field ? (m_bFlags | e_IS_OR_HAS_INT128_FIELD) : (m_bFlags & ~e_IS_OR_HAS_INT128_FIELD); } }; // // This structure is used only when the classloader is building the interface map. Before the class // is resolved, the EEClass contains an array of these, which are all interfaces *directly* declared // for this class/interface by the metadata - inherited interfaces will not be present if they are // not specifically declared. // // This structure is destroyed after resolving has completed. // typedef struct { // The interface method table; for instantiated interfaces, this is the generic interface MethodTable *m_pMethodTable; } BuildingInterfaceInfo_t; // // We should not need to touch anything in here once the classes are all loaded, unless we // are doing reflection. Try to avoid paging this data structure in. // // Size of hash bitmap for method names #define METHOD_HASH_BYTES 8 // Hash table size - prime number #define METHOD_HASH_BITS 61 // These are some macros for forming fully qualified class names for a class. // These are abstracted so that we can decide later if a max length for a // class name is acceptable. // It doesn't make any sense not to have a small but usually quite capable // stack buffer to build class names into. Most class names that I can think // of would fit in 128 characters, and that's a pretty small amount of stack // to use in exchange for not having to new and delete the memory. #define DEFAULT_NONSTACK_CLASSNAME_SIZE (MAX_CLASSNAME_LENGTH/4) #define DefineFullyQualifiedNameForClass() \ InlineSString _ssclsname8_; \ InlineSString _ssclsname_; #define DefineFullyQualifiedNameForClassOnStack() \ InlineSString _ssclsname8_; \ InlineSString _ssclsname_; #define DefineFullyQualifiedNameForClassW() \ InlineSString _ssclsname_w_; #define DefineFullyQualifiedNameForClassWOnStack() \ InlineSString _ssclsname_w_; #define GetFullyQualifiedNameForClassNestedAware(pClass) \ (pClass->_GetFullyQualifiedNameForClassNestedAware(_ssclsname_), _ssclsname8_.SetAndConvertToUTF8(_ssclsname_), _ssclsname8_.GetUTF8()) #define GetFullyQualifiedNameForClassNestedAwareW(pClass) \ pClass->_GetFullyQualifiedNameForClassNestedAware(_ssclsname_w_).GetUnicode() #define GetFullyQualifiedNameForClass(pClass) \ (pClass->_GetFullyQualifiedNameForClass(_ssclsname_), _ssclsname8_.SetAndConvertToUTF8(_ssclsname_), _ssclsname8_.GetUTF8()) #define GetFullyQualifiedNameForClassW(pClass) \ pClass->_GetFullyQualifiedNameForClass(_ssclsname_w_).GetUnicode() // Structure containing EEClass fields used by a minority of EEClass instances. This separation allows us to // save memory and improve the density of accessed fields in the EEClasses themselves. This class is reached // via the m_rpOptionalFields field EEClass (use the GetOptionalFields() accessor rather than the field // itself). class EEClassOptionalFields { // All fields here are intentionally private. Use the corresponding accessor on EEClass instead (this // makes it easier to add and remove fields from the optional section in the future). We make exceptions // for MethodTableBuilder and NativeImageDumper, which need raw field-level access. friend class EEClass; friend class MethodTableBuilder; // // GENERICS RELATED FIELDS. // // If IsSharedByGenericInstantiations(), layout of handle dictionary for generic type // (the last dictionary pointed to from PerInstInfo). Otherwise NULL. PTR_DictionaryLayout m_pDictLayout; // Variance info for each type parameter (gpNonVariant, gpCovariant, or gpContravariant) // If NULL, this type has no type parameters that are co/contravariant PTR_BYTE m_pVarianceInfo; // // COM RELATED FIELDS. // #ifdef FEATURE_COMINTEROP SparseVTableMap *m_pSparseVTableMap; TypeHandle m_pCoClassForIntf; // @TODO: Coclass for an interface #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION // Points to activation information if the type is an activatable COM class. ClassFactoryBase *m_pClassFactory; #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION #endif // FEATURE_COMINTEROP // // MISC FIELDS // #if defined(UNIX_AMD64_ABI) // Number of eightBytes in the following arrays int m_numberEightBytes; // Classification of the eightBytes SystemVClassificationType m_eightByteClassifications[CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS]; // Size of data the eightBytes unsigned int m_eightByteSizes[CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS]; #endif // UNIX_AMD64_ABI // Required alignment for this fields of this type (only set in auto-layout structures when different from pointer alignment) BYTE m_requiredFieldAlignment; // Set default values for optional fields. inline void Init(); PTR_BYTE GetVarianceInfo() { LIMITED_METHOD_DAC_CONTRACT; return m_pVarianceInfo; } }; typedef DPTR(EEClassOptionalFields) PTR_EEClassOptionalFields; //@GENERICS: // For most types there is a one-to-one mapping between MethodTable* and EEClass* // However this is not the case for instantiated types where code and representation // are shared between compatible instantiations (e.g. List and List) // Then a single EEClass structure is shared between multiple MethodTable structures // Uninstantiated generic types (e.g. List) have their own EEClass and MethodTable, // used (a) as a representative for the generic type itself, (b) for static fields and // methods, which aren't present in the instantiations, and (c) to hold some information // (e.g. formal instantiations of superclass and implemented interfaces) that is common // to all instantiations and isn't stored in the EEClass structures for instantiated types // // // ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE // // A word about EEClass vs. MethodTable // ------------------------------------ // // At compile-time, we are happy to touch both MethodTable and EEClass. However, // at runtime we want to restrict ourselves to the MethodTable. This is critical // for common code paths, where we want to keep the EEClass out of our working // set. For uncommon code paths, like throwing exceptions or strange Contexts // issues, it's okay to access the EEClass. // // To this end, the TypeHandle (CLASS_HANDLE) abstraction is now based on the // MethodTable pointer instead of the EEClass pointer. If you are writing a // runtime helper that calls GetClass() to access the associated EEClass, please // stop to wonder if you are making a mistake. // // ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE // An code:EEClass is a representation of the part of a managed type that is not used very frequently (it is // cold), and thus is segregated from the hot portion (which lives in code:MethodTable). As noted above an // it is also the case that EEClass is SHARED among all instantiations of a generic type, so anything that // is specific to a paritcular type can not live off the EEClass. // // From here you can get to // code:MethodTable - The representation of the hot portion of a type. // code:MethodDesc - The representation of a method // code:FieldDesc - The representation of a field. // // EEClasses hold the following important fields // * code:EEClass.m_pMethodTable - Points a MethodTable associated with // * code:EEClass.m_pChunks - a list of code:MethodDescChunk which is simply a list of code:MethodDesc // which represent the methods. // * code:EEClass.m_pFieldDescList - a list of fields in the type. // class EEClass // DO NOT CREATE A NEW EEClass USING NEW! { /************************************ * FRIEND FUNCTIONS ************************************/ // DO NOT ADD FRIENDS UNLESS ABSOLUTELY NECESSARY // USE ACCESSORS TO READ/WRITE private field members // To access bmt stuff friend class MethodTable; friend class MethodTableBuilder; friend class FieldDesc; friend class CheckAsmOffsets; friend class ClrDataAccess; /************************************ * PUBLIC INSTANCE METHODS ************************************/ public: DWORD IsSealed() { LIMITED_METHOD_CONTRACT; return IsTdSealed(m_dwAttrClass); } inline DWORD IsInterface() { WRAPPER_NO_CONTRACT; return IsTdInterface(m_dwAttrClass); } inline DWORD IsAbstract() { WRAPPER_NO_CONTRACT; return IsTdAbstract(m_dwAttrClass); } BOOL HasExplicitFieldOffsetLayout() { WRAPPER_NO_CONTRACT; return IsTdExplicitLayout(GetAttrClass()) && HasLayout(); } BOOL HasSequentialLayout() { WRAPPER_NO_CONTRACT; return IsTdSequentialLayout(GetAttrClass()); } BOOL IsBeforeFieldInit() { WRAPPER_NO_CONTRACT; return IsTdBeforeFieldInit(GetAttrClass()); } DWORD GetProtection() { WRAPPER_NO_CONTRACT; return (m_dwAttrClass & tdVisibilityMask); } // class is blittable BOOL IsBlittable(); #ifndef DACCESS_COMPILE void *operator new(size_t size, LoaderHeap* pHeap, AllocMemTracker *pamTracker); void Destruct(MethodTable * pMT); static EEClass * CreateMinimalClass(LoaderHeap *pHeap, AllocMemTracker *pamTracker); #endif // !DACCESS_COMPILE #ifdef FEATURE_METADATA_UPDATER // Add a new method to an already loaded type for EnC static HRESULT AddMethod(MethodTable* pMT, mdMethodDef methodDef, MethodDesc** ppMethod); private: static HRESULT AddMethodDesc( MethodTable* pMT, mdMethodDef methodDef, DWORD dwImplFlags, DWORD dwMemberAttrs, MethodDesc** ppNewMD); public: // Add a new field to an already loaded type for EnC static HRESULT AddField(MethodTable* pMT, mdFieldDef fieldDesc, FieldDesc** pAddedField); private: static HRESULT AddFieldDesc( MethodTable* pMT, mdMethodDef fieldDef, DWORD dwFieldAttrs, FieldDesc** ppNewFD); public: static VOID FixupFieldDescForEnC(MethodTable * pMT, EnCFieldDesc *pFD, mdFieldDef fieldDef); #endif // FEATURE_METADATA_UPDATER inline DWORD IsComImport() { WRAPPER_NO_CONTRACT; return IsTdImport(m_dwAttrClass); } EEClassLayoutInfo *GetLayoutInfo(); PTR_EEClassNativeLayoutInfo GetNativeLayoutInfo(); #ifdef DACCESS_COMPILE void EnumMemoryRegions(CLRDataEnumMemoryFlags flags, MethodTable *pMT); #endif /************************************ * INSTANCE MEMBER VARIABLES ************************************/ #ifdef _DEBUG public: inline LPCUTF8 GetDebugClassName () { LIMITED_METHOD_CONTRACT; return m_szDebugClassName; } inline void SetDebugClassName (LPCUTF8 szDebugClassName) { LIMITED_METHOD_CONTRACT; m_szDebugClassName = szDebugClassName; } /* * Controls debugging breaks and output if a method class * is mentioned in the registry ("BreakOnClassBuild") * Method layout within this class can cause a debug * break by setting "BreakOnMethodName". Not accessible * outside the class. */ #endif // _DEBUG #ifdef FEATURE_COMINTEROP /* * Used to map MethodTable slots to VTable slots */ inline SparseVTableMap* GetSparseCOMInteropVTableMap () { LIMITED_METHOD_CONTRACT; return HasOptionalFields() ? GetOptionalFields()->m_pSparseVTableMap : NULL; } inline void SetSparseCOMInteropVTableMap (SparseVTableMap *map) { LIMITED_METHOD_CONTRACT; _ASSERTE(HasOptionalFields()); GetOptionalFields()->m_pSparseVTableMap = map; } #endif // FEATURE_COMINTEROP public: /* * Maintain back pointer to statcally hot portion of EEClass. * For an EEClass representing multiple instantiations of a generic type, this is the method table * for the first instantiation requested and is the only one containing entries for non-virtual instance methods * (i.e. non-vtable entries). */ // Note that EEClass structures may be shared between generic instantiations // (see IsSharedByGenericInstantiations). In these cases EEClass::GetMethodTable // will return the method table pointer corresponding to the "canonical" // instantiation, as defined in typehandle.h. // inline PTR_MethodTable GetMethodTable() { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; return m_pMethodTable; } // DO NOT ADD ANY ASSERTS TO THIS METHOD. // DO NOT USE THIS METHOD. // Yes folks, for better or worse the debugger pokes supposed object addresses // to try to see if objects are valid, possibly firing an AccessViolation or worse, // and then catches the AV and reports a failure to the debug client. This makes // the debugger slightly more robust should any corrupted object references appear // in a session. Thus it is "correct" behaviour for this to AV when used with // an invalid object pointer, and incorrect behaviour for it to // assert. inline PTR_MethodTable GetMethodTableWithPossibleAV() { CANNOT_HAVE_CONTRACT; SUPPORTS_DAC; return m_pMethodTable; } #ifndef DACCESS_COMPILE inline void SetMethodTable(MethodTable* pMT) { LIMITED_METHOD_CONTRACT; m_pMethodTable = pMT; } #endif // !DACCESS_COMPILE /* * Number of fields in the class, including inherited fields. * Does not include fields added from EnC. */ inline WORD GetNumInstanceFields() { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; return m_NumInstanceFields; } inline void SetNumInstanceFields (WORD wNumInstanceFields) { LIMITED_METHOD_CONTRACT; m_NumInstanceFields = wNumInstanceFields; } /* * Number of static fields declared in this class. * Implementation Note: Static values are laid out at the end of the MethodTable vtable. */ inline WORD GetNumStaticFields() { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; return m_NumStaticFields; } inline void SetNumStaticFields (WORD wNumStaticFields) { LIMITED_METHOD_CONTRACT; m_NumStaticFields = wNumStaticFields; } inline WORD GetNumThreadStaticFields() { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; return m_NumThreadStaticFields; } inline void SetNumThreadStaticFields (WORD wNumThreadStaticFields) { LIMITED_METHOD_CONTRACT; m_NumThreadStaticFields = wNumThreadStaticFields; } /* * Difference between the InterfaceMap ptr and Vtable in the * MethodTable used to indicate the number of static bytes * Now interfaceMap ptr can be optional hence we store it here */ inline DWORD GetNonGCRegularStaticFieldBytes() { LIMITED_METHOD_CONTRACT; return m_NonGCStaticFieldBytes; } inline void SetNonGCRegularStaticFieldBytes (DWORD cbNonGCStaticFieldBytes) { LIMITED_METHOD_CONTRACT; m_NonGCStaticFieldBytes = cbNonGCStaticFieldBytes; } inline DWORD GetNonGCThreadStaticFieldBytes() { LIMITED_METHOD_CONTRACT; return m_NonGCThreadStaticFieldBytes; } inline void SetNonGCThreadStaticFieldBytes (DWORD cbNonGCStaticFieldBytes) { LIMITED_METHOD_CONTRACT; m_NonGCThreadStaticFieldBytes = cbNonGCStaticFieldBytes; } inline WORD GetNumNonVirtualSlots() { LIMITED_METHOD_CONTRACT; return m_NumNonVirtualSlots; } inline void SetNumNonVirtualSlots(WORD wNumNonVirtualSlots) { LIMITED_METHOD_CONTRACT; m_NumNonVirtualSlots = wNumNonVirtualSlots; } inline BOOL IsEquivalentType() { LIMITED_METHOD_CONTRACT; return m_VMFlags & VMFLAG_IS_EQUIVALENT_TYPE; } #ifdef FEATURE_TYPEEQUIVALENCE inline void SetIsEquivalentType() { LIMITED_METHOD_CONTRACT; m_VMFlags |= VMFLAG_IS_EQUIVALENT_TYPE; } #endif // FEATURE_TYPEEQUIVALENCE /* * Number of static handles allocated */ inline WORD GetNumHandleRegularStatics () { LIMITED_METHOD_CONTRACT; return m_NumHandleStatics; } inline void SetNumHandleRegularStatics (WORD wNumHandleRegularStatics) { LIMITED_METHOD_CONTRACT; m_NumHandleStatics = wNumHandleRegularStatics; } /* * Number of static handles allocated for ThreadStatics */ inline WORD GetNumHandleThreadStatics () { LIMITED_METHOD_CONTRACT; return m_NumHandleThreadStatics; } inline void SetNumHandleThreadStatics (WORD wNumHandleThreadStatics) { LIMITED_METHOD_CONTRACT; m_NumHandleThreadStatics = wNumHandleThreadStatics; } /* * Number of bytes to subract from code:MethodTable::GetBaseSize() to get the actual number of bytes * of instance fields stored in the object on the GC heap. */ inline DWORD GetBaseSizePadding() { LIMITED_METHOD_DAC_CONTRACT; return m_cbBaseSizePadding; } inline void SetBaseSizePadding(DWORD dwPadding) { LIMITED_METHOD_CONTRACT; _ASSERTE(FitsIn(dwPadding)); m_cbBaseSizePadding = static_cast(dwPadding); } inline DWORD GetUnboxedNumInstanceFieldBytes() { DWORD cbBoxedSize = GetMethodTable()->GetNumInstanceFieldBytes(); _ASSERTE(GetMethodTable()->IsValueType() || GetMethodTable()->IsEnum()); return cbBoxedSize; } /* * Pointer to a list of FieldDescs declared in this class * There are (m_wNumInstanceFields - GetParentClass()->m_wNumInstanceFields + m_wNumStaticFields) entries * in this array */ inline PTR_FieldDesc GetFieldDescList() { LIMITED_METHOD_DAC_CONTRACT; // Careful about using this method. If it's possible that fields may have been added via EnC, then // must use the FieldDescIterator as any fields added via EnC won't be in the raw list return m_pFieldDescList; } PTR_FieldDesc GetFieldDescByIndex(DWORD fieldIndex); #ifndef DACCESS_COMPILE inline void SetFieldDescList (FieldDesc* pFieldDescList) { LIMITED_METHOD_CONTRACT; m_pFieldDescList = pFieldDescList; } #endif // !DACCESS_COMPILE inline WORD GetNumMethods() { LIMITED_METHOD_DAC_CONTRACT; return m_NumMethods; } inline void SetNumMethods (WORD wNumMethods) { LIMITED_METHOD_CONTRACT; m_NumMethods = wNumMethods; } /* * Cached metadata for this class (GetTypeDefProps) */ inline DWORD GetAttrClass() { LIMITED_METHOD_CONTRACT; return m_dwAttrClass; } inline void SetAttrClass (DWORD dwAttrClass) { LIMITED_METHOD_CONTRACT; m_dwAttrClass = dwAttrClass; } #ifdef FEATURE_COMINTEROP inline DWORD IsComClassInterface() { LIMITED_METHOD_CONTRACT; return (m_VMFlags & VMFLAG_HASCOCLASSATTRIB); } inline VOID SetIsComClassInterface() { LIMITED_METHOD_CONTRACT; m_VMFlags |= VMFLAG_HASCOCLASSATTRIB; } inline void SetComEventItfType() { LIMITED_METHOD_CONTRACT; _ASSERTE(IsInterface()); m_VMFlags |= VMFLAG_COMEVENTITFMASK; } // class is a special COM event interface inline BOOL IsComEventItfType() { LIMITED_METHOD_CONTRACT; return (m_VMFlags & VMFLAG_COMEVENTITFMASK); } #endif // FEATURE_COMINTEROP inline void SetHasVTableMethodImpl() { LIMITED_METHOD_CONTRACT; m_VMFlags |= VMFLAG_VTABLEMETHODIMPL; } inline BOOL HasVTableMethodImpl() { LIMITED_METHOD_CONTRACT; return (m_VMFlags & VMFLAG_VTABLEMETHODIMPL); } inline void SetHasCovariantOverride() { LIMITED_METHOD_CONTRACT; m_VMFlags |= VMFLAG_COVARIANTOVERRIDE; } inline BOOL HasCovariantOverride() { LIMITED_METHOD_CONTRACT; return (m_VMFlags & VMFLAG_COVARIANTOVERRIDE); } inline DWORD IsUnsafeValueClass() { LIMITED_METHOD_CONTRACT; return (m_VMFlags & VMFLAG_UNSAFEVALUETYPE); } private: inline void SetUnsafeValueClass() { LIMITED_METHOD_CONTRACT; m_VMFlags |= VMFLAG_UNSAFEVALUETYPE; } public: inline BOOL HasNoGuid() { LIMITED_METHOD_CONTRACT; return (m_VMFlags & VMFLAG_NO_GUID); } inline void SetHasNoGuid() { WRAPPER_NO_CONTRACT; InterlockedOr((LONG*)&m_VMFlags, VMFLAG_NO_GUID); } public: inline BOOL IsAlign8Candidate() { LIMITED_METHOD_CONTRACT; return (m_VMFlags & VMFLAG_PREFER_ALIGN8); } inline void SetAlign8Candidate() { LIMITED_METHOD_CONTRACT; m_VMFlags |= VMFLAG_PREFER_ALIGN8; } inline BOOL HasCustomFieldAlignment() { LIMITED_METHOD_CONTRACT; return (m_VMFlags & VMFLAG_HAS_CUSTOM_FIELD_ALIGNMENT); } inline void SetHasCustomFieldAlignment() { LIMITED_METHOD_CONTRACT; m_VMFlags |= VMFLAG_HAS_CUSTOM_FIELD_ALIGNMENT; } inline void SetHasFixedAddressVTStatics() { LIMITED_METHOD_CONTRACT; m_VMFlags |= (DWORD) VMFLAG_FIXED_ADDRESS_VT_STATICS; } void SetHasOnlyAbstractMethods() { LIMITED_METHOD_CONTRACT; m_VMFlags |= (DWORD) VMFLAG_ONLY_ABSTRACT_METHODS; } #ifdef FEATURE_COMINTEROP void SetSparseForCOMInterop() { LIMITED_METHOD_CONTRACT; m_VMFlags |= (DWORD) VMFLAG_SPARSE_FOR_COMINTEROP; } inline void SetMarshalingType(UINT32 mType) { LIMITED_METHOD_CONTRACT; _ASSERTE(mType !=0); _ASSERTE((m_VMFlags & VMFLAG_MARSHALINGTYPE_MASK) == 0); switch(mType) { case 1: m_VMFlags |= VMFLAG_MARSHALINGTYPE_INHIBIT; break; case 2: m_VMFlags |= VMFLAG_MARSHALINGTYPE_FREETHREADED; break; case 3: m_VMFlags |= VMFLAG_MARSHALINGTYPE_STANDARD; break; default: _ASSERTE(!"Invalid MarshalingBehaviorAttribute value"); } } #endif // FEATURE_COMINTEROP inline void SetHasLayout() { LIMITED_METHOD_CONTRACT; m_VMFlags |= (DWORD) VMFLAG_HASLAYOUT; //modified before the class is published } inline void SetHasOverlaidFields() { LIMITED_METHOD_CONTRACT; m_VMFlags |= VMFLAG_HASOVERLAIDFIELDS; } inline void SetIsNested() { LIMITED_METHOD_CONTRACT; m_VMFlags |= VMFLAG_ISNESTED; } #ifdef FEATURE_READYTORUN inline BOOL HasLayoutDependsOnOtherModules() { LIMITED_METHOD_CONTRACT; return m_VMFlags & VMFLAG_LAYOUT_DEPENDS_ON_OTHER_MODULES; } inline void SetHasLayoutDependsOnOtherModules() { LIMITED_METHOD_CONTRACT; m_VMFlags |= VMFLAG_LAYOUT_DEPENDS_ON_OTHER_MODULES; } #endif // Is this delegate? Returns false for System.Delegate and System.MulticastDelegate. inline BOOL IsDelegate() { LIMITED_METHOD_CONTRACT; return m_VMFlags & VMFLAG_DELEGATE; } inline void SetIsDelegate() { LIMITED_METHOD_CONTRACT; m_VMFlags |= VMFLAG_DELEGATE; } #ifdef FEATURE_METADATA_UPDATER inline BOOL HasEnCStaticFields() { LIMITED_METHOD_CONTRACT; return m_VMFlags & VMFLAG_ENC_STATIC_FIELDS; } inline void SetHasEnCStaticFields() { LIMITED_METHOD_CONTRACT; m_VMFlags |= VMFLAG_ENC_STATIC_FIELDS; } #endif // FEATURE_METADATA_UPDATER BOOL HasFixedAddressVTStatics() { LIMITED_METHOD_CONTRACT; return m_VMFlags & VMFLAG_FIXED_ADDRESS_VT_STATICS; } BOOL HasOnlyAbstractMethods() { LIMITED_METHOD_CONTRACT; return m_VMFlags & VMFLAG_ONLY_ABSTRACT_METHODS; } #ifdef FEATURE_COMINTEROP BOOL IsSparseForCOMInterop() { LIMITED_METHOD_CONTRACT; return m_VMFlags & VMFLAG_SPARSE_FOR_COMINTEROP; } BOOL IsMarshalingTypeSet() { LIMITED_METHOD_CONTRACT; return (m_VMFlags & VMFLAG_MARSHALINGTYPE_MASK); } BOOL IsMarshalingTypeFreeThreaded() { LIMITED_METHOD_CONTRACT; return ((m_VMFlags & VMFLAG_MARSHALINGTYPE_MASK) == VMFLAG_MARSHALINGTYPE_FREETHREADED); } BOOL IsMarshalingTypeInhibit() { LIMITED_METHOD_CONTRACT; return ((m_VMFlags & VMFLAG_MARSHALINGTYPE_MASK) == VMFLAG_MARSHALINGTYPE_INHIBIT); } BOOL IsMarshalingTypeStandard() { LIMITED_METHOD_CONTRACT; return ((m_VMFlags & VMFLAG_MARSHALINGTYPE_MASK) == VMFLAG_MARSHALINGTYPE_STANDARD); } #endif // FEATURE_COMINTEROP BOOL HasLayout() { LIMITED_METHOD_CONTRACT; return m_VMFlags & VMFLAG_HASLAYOUT; } BOOL HasOverlaidField() { LIMITED_METHOD_CONTRACT; return m_VMFlags & VMFLAG_HASOVERLAIDFIELDS; } BOOL IsNested() { LIMITED_METHOD_CONTRACT; return m_VMFlags & VMFLAG_ISNESTED; } BOOL HasFieldsWhichMustBeInited() { LIMITED_METHOD_CONTRACT; return (m_VMFlags & VMFLAG_HAS_FIELDS_WHICH_MUST_BE_INITED); } void SetHasFieldsWhichMustBeInited() { LIMITED_METHOD_CONTRACT; m_VMFlags |= (DWORD)VMFLAG_HAS_FIELDS_WHICH_MUST_BE_INITED; } DWORD IsInlineArray() { LIMITED_METHOD_CONTRACT; return (m_VMFlags & VMFLAG_INLINE_ARRAY); } void SetIsInlineArray() { LIMITED_METHOD_CONTRACT; m_VMFlags |= (DWORD)VMFLAG_INLINE_ARRAY; } DWORD HasNonPublicFields() { LIMITED_METHOD_CONTRACT; return (m_VMFlags & VMFLAG_HASNONPUBLICFIELDS); } void SetHasNonPublicFields() { LIMITED_METHOD_CONTRACT; m_VMFlags |= (DWORD)VMFLAG_HASNONPUBLICFIELDS; } DWORD IsNotTightlyPacked() { LIMITED_METHOD_CONTRACT; return (m_VMFlags & VMFLAG_NOT_TIGHTLY_PACKED); } void SetIsNotTightlyPacked() { LIMITED_METHOD_CONTRACT; m_VMFlags |= (DWORD)VMFLAG_NOT_TIGHTLY_PACKED; } DWORD ContainsMethodImpls() { LIMITED_METHOD_CONTRACT; return (m_VMFlags & VMFLAG_CONTAINS_METHODIMPLS); } void SetContainsMethodImpls() { LIMITED_METHOD_CONTRACT; m_VMFlags |= (DWORD)VMFLAG_CONTAINS_METHODIMPLS; } BOOL IsManagedSequential(); BOOL HasExplicitSize(); BOOL IsAutoLayoutOrHasAutoLayoutField(); // Only accurate on non-auto layout types BOOL IsInt128OrHasInt128Fields(); static void GetBestFitMapping(MethodTable * pMT, BOOL *pfBestFitMapping, BOOL *pfThrowOnUnmappableChar); /* * The CorElementType for this class (most classes = ELEMENT_TYPE_CLASS) */ public: // This is what would be used in the calling convention for this type. CorElementType GetInternalCorElementType() { LIMITED_METHOD_DAC_CONTRACT; return CorElementType(m_NormType); } void SetInternalCorElementType (CorElementType _NormType) { LIMITED_METHOD_CONTRACT; m_NormType = static_cast(_NormType); } /* * Chain of MethodDesc chunks for the MethodTable */ public: inline PTR_MethodDescChunk GetChunks(); #ifndef DACCESS_COMPILE inline void SetChunks (MethodDescChunk* pChunks) { LIMITED_METHOD_CONTRACT; m_pChunks = pChunks; } #endif // !DACCESS_COMPILE void AddChunk (MethodDescChunk* pNewChunk); void AddChunkIfItHasNotBeenAdded (MethodDescChunk* pNewChunk); inline PTR_GuidInfo GetGuidInfo() { LIMITED_METHOD_DAC_CONTRACT; return m_pGuidInfo; } inline void SetGuidInfo(GuidInfo* pGuidInfo) { WRAPPER_NO_CONTRACT; #ifndef DACCESS_COMPILE m_pGuidInfo = pGuidInfo; #endif // DACCESS_COMPILE } #if defined(UNIX_AMD64_ABI) // Get number of eightbytes used by a struct passed in registers. inline int GetNumberEightBytes() { LIMITED_METHOD_CONTRACT; _ASSERTE(HasOptionalFields()); return GetOptionalFields()->m_numberEightBytes; } // Get eightbyte classification for the eightbyte with the specified index. inline SystemVClassificationType GetEightByteClassification(int index) { LIMITED_METHOD_CONTRACT; _ASSERTE(HasOptionalFields()); return GetOptionalFields()->m_eightByteClassifications[index]; } // Get size of the data in the eightbyte with the specified index. inline unsigned int GetEightByteSize(int index) { LIMITED_METHOD_CONTRACT; _ASSERTE(HasOptionalFields()); return GetOptionalFields()->m_eightByteSizes[index]; } // Set the eightByte classification inline void SetEightByteClassification(int eightByteCount, SystemVClassificationType *eightByteClassifications, unsigned int *eightByteSizes) { LIMITED_METHOD_CONTRACT; _ASSERTE(HasOptionalFields()); GetOptionalFields()->m_numberEightBytes = eightByteCount; for (int i = 0; i < eightByteCount; i++) { GetOptionalFields()->m_eightByteClassifications[i] = eightByteClassifications[i]; GetOptionalFields()->m_eightByteSizes[i] = eightByteSizes[i]; } } #endif // UNIX_AMD64_ABI #if defined(FEATURE_HFA) bool CheckForHFA(MethodTable ** pByValueClassCache); #else // !FEATURE_HFA bool CheckForHFA(); #endif // FEATURE_HFA inline int GetOverriddenFieldAlignmentRequirement() { LIMITED_METHOD_CONTRACT; _ASSERTE(HasOptionalFields()); return GetOptionalFields()->m_requiredFieldAlignment; } #ifdef FEATURE_COMINTEROP inline TypeHandle GetCoClassForInterface() { LIMITED_METHOD_CONTRACT; _ASSERTE(HasOptionalFields()); return GetOptionalFields()->m_pCoClassForIntf; } inline void SetCoClassForInterface(TypeHandle th) { WRAPPER_NO_CONTRACT; _ASSERTE(HasOptionalFields()); GetOptionalFields()->m_pCoClassForIntf = th; } OBJECTHANDLE GetOHDelegate() { LIMITED_METHOD_CONTRACT; return m_ohDelegate; } void SetOHDelegate (OBJECTHANDLE _ohDelegate) { LIMITED_METHOD_CONTRACT; m_ohDelegate = _ohDelegate; } // Set the COM interface type. CorIfaceAttr GetComInterfaceType() { LIMITED_METHOD_CONTRACT; return m_ComInterfaceType; } void SetComInterfaceType(CorIfaceAttr ItfType) { WRAPPER_NO_CONTRACT; _ASSERTE(IsInterface()); m_ComInterfaceType = ItfType; } inline ComCallWrapperTemplate *GetComCallWrapperTemplate() { LIMITED_METHOD_CONTRACT; return m_pccwTemplate; } inline BOOL SetComCallWrapperTemplate(ComCallWrapperTemplate *pTemplate) { WRAPPER_NO_CONTRACT; return (InterlockedCompareExchangeT(&m_pccwTemplate, pTemplate, NULL) == NULL); } #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION inline ClassFactoryBase *GetComClassFactory() { LIMITED_METHOD_CONTRACT; return HasOptionalFields() ? GetOptionalFields()->m_pClassFactory : NULL; } inline BOOL SetComClassFactory(ClassFactoryBase *pFactory) { WRAPPER_NO_CONTRACT; _ASSERTE(HasOptionalFields()); return (InterlockedCompareExchangeT(&GetOptionalFields()->m_pClassFactory, pFactory, NULL) == NULL); } #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION #endif // FEATURE_COMINTEROP public: PTR_DictionaryLayout GetDictionaryLayout() { SUPPORTS_DAC; WRAPPER_NO_CONTRACT; return HasOptionalFields() ? GetOptionalFields()->m_pDictLayout : NULL; } void SetDictionaryLayout(PTR_DictionaryLayout pLayout) { SUPPORTS_DAC; WRAPPER_NO_CONTRACT; _ASSERTE(HasOptionalFields()); GetOptionalFields()->m_pDictLayout = pLayout; } #ifndef DACCESS_COMPILE static CorGenericParamAttr GetVarianceOfTypeParameter(BYTE * pbVarianceInfo, DWORD i) { LIMITED_METHOD_CONTRACT; if (pbVarianceInfo == NULL) return gpNonVariant; else return (CorGenericParamAttr) (pbVarianceInfo[i]); } CorGenericParamAttr GetVarianceOfTypeParameter(DWORD i) { WRAPPER_NO_CONTRACT; return GetVarianceOfTypeParameter(GetVarianceInfo(), i); } BYTE* GetVarianceInfo() { LIMITED_METHOD_CONTRACT; return HasOptionalFields() ? GetOptionalFields()->GetVarianceInfo() : NULL; } void SetVarianceInfo(BYTE *pVarianceInfo) { LIMITED_METHOD_CONTRACT; _ASSERTE(HasOptionalFields()); GetOptionalFields()->m_pVarianceInfo = pVarianceInfo; } #endif // !DACCESS_COMPILE // Check that a signature blob uses type parameters correctly // in accordance with the variance annotations specified by this class // The position parameter indicates the variance of the context we're in // (result type is gpCovariant, argument types are gpContravariant, deeper in a signature // we might be gpNonvariant e.g. in a pointer type or non-variant generic type) static BOOL CheckVarianceInSig( DWORD numGenericArgs, BYTE *pVarianceInfo, Module * pModule, SigPointer sp, CorGenericParamAttr position); //------------------------------------------------------------- // CONCRETE DATA LAYOUT // // Although accessed far less frequently than MethodTables, EEClasses are still // pulled into working set, especially at startup. This has motivated several space // optimizations in field layout where each is balanced against the need to access // a particular field efficiently. // // Currently, the following strategy is used: // // - Any field that has a default value for the vast majority of EEClass instances // should be stored in the EEClassOptionalFields (see header comment) // // If none of these categories apply - such as for always-meaningful pointer members or // sets of flags - a full field is used. Please avoid adding such members if possible. //------------------------------------------------------------- // // Flags for m_VMFlags // public: enum { #ifdef FEATURE_READYTORUN VMFLAG_LAYOUT_DEPENDS_ON_OTHER_MODULES = 0x00000001, #endif VMFLAG_DELEGATE = 0x00000002, #ifdef FEATURE_METADATA_UPDATER VMFLAG_ENC_STATIC_FIELDS = 0x00000004, #endif // FEATURE_METADATA_UPDATER // VMFLAG_UNUSED = 0x00000018, VMFLAG_FIXED_ADDRESS_VT_STATICS = 0x00000020, // Value type Statics in this class will be pinned VMFLAG_HASLAYOUT = 0x00000040, VMFLAG_ISNESTED = 0x00000080, VMFLAG_IS_EQUIVALENT_TYPE = 0x00000200, // OVERLAID is used to detect whether Equals can safely optimize to a bit-compare across the structure. VMFLAG_HASOVERLAIDFIELDS = 0x00000400, // Set this if this class or its parent have instance fields which // must be explicitly inited in a constructor (e.g. pointers of any // kind, gc or native). // // Currently this is used by the verifier when verifying value classes // - it's ok to use uninitialised value classes if there are no // pointer fields in them. VMFLAG_HAS_FIELDS_WHICH_MUST_BE_INITED = 0x00000800, VMFLAG_UNSAFEVALUETYPE = 0x00001000, VMFLAG_BESTFITMAPPING_INITED = 0x00002000, // VMFLAG_BESTFITMAPPING and VMFLAG_THROWONUNMAPPABLECHAR are valid only if this is set VMFLAG_BESTFITMAPPING = 0x00004000, // BestFitMappingAttribute.Value VMFLAG_THROWONUNMAPPABLECHAR = 0x00008000, // BestFitMappingAttribute.ThrowOnUnmappableChar VMFLAG_INLINE_ARRAY = 0x00010000, VMFLAG_NO_GUID = 0x00020000, VMFLAG_HASNONPUBLICFIELDS = 0x00040000, VMFLAG_HAS_CUSTOM_FIELD_ALIGNMENT = 0x00080000, VMFLAG_CONTAINS_STACK_PTR = 0x00100000, VMFLAG_PREFER_ALIGN8 = 0x00200000, // Would like to have 8-byte alignment VMFLAG_ONLY_ABSTRACT_METHODS = 0x00400000, // Type only contains abstract methods #ifdef FEATURE_COMINTEROP VMFLAG_SPARSE_FOR_COMINTEROP = 0x00800000, // interfaces may have a coclass attribute VMFLAG_HASCOCLASSATTRIB = 0x01000000, VMFLAG_COMEVENTITFMASK = 0x02000000, // class is a special COM event interface #endif // FEATURE_COMINTEROP VMFLAG_VTABLEMETHODIMPL = 0x04000000, // class uses MethodImpl to override virtual function defined on class VMFLAG_COVARIANTOVERRIDE = 0x08000000, // class has a covariant override // This one indicates that the fields of the valuetype are // not tightly packed and is used to check whether we can // do bit-equality on value types to implement ValueType::Equals. // It is not valid for classes, and only matters if ContainsPointer // is false. VMFLAG_NOT_TIGHTLY_PACKED = 0x10000000, // True if methoddesc on this class have any real (non-interface) methodimpls VMFLAG_CONTAINS_METHODIMPLS = 0x20000000, #ifdef FEATURE_COMINTEROP VMFLAG_MARSHALINGTYPE_MASK = 0xc0000000, VMFLAG_MARSHALINGTYPE_INHIBIT = 0x40000000, VMFLAG_MARSHALINGTYPE_FREETHREADED = 0x80000000, VMFLAG_MARSHALINGTYPE_STANDARD = 0xc0000000, #endif }; public: // C_ASSERTs in Jitinterface.cpp need this to be public to check the offset. // Put it first so the offset rarely changes, which just reduces the number of times we have to fiddle // with the offset. PTR_GuidInfo m_pGuidInfo; // The cached guid information for interfaces. #ifdef _DEBUG public: LPCUTF8 m_szDebugClassName; BOOL m_fDebuggingClass; #endif private: // Layout rest of fields below from largest to smallest to lessen the chance of wasting bytes with // compiler injected padding (especially with the difference between pointers and DWORDs on 64-bit). PTR_EEClassOptionalFields m_rpOptionalFields; // TODO: Remove this field. It is only used by SOS and object validation for stress. PTR_MethodTable m_pMethodTable; PTR_FieldDesc m_pFieldDescList; PTR_MethodDescChunk m_pChunks; #ifdef FEATURE_COMINTEROP union { // For COM+ wrapper objects that extend an unmanaged class, this field // may contain a delegate to be called to allocate the aggregated // unmanaged class (instead of using CoCreateInstance). OBJECTHANDLE m_ohDelegate; // For interfaces this contains the COM interface type. CorIfaceAttr m_ComInterfaceType; }; ComCallWrapperTemplate *m_pccwTemplate; // points to interop data structures used when this type is exposed to COM #endif // FEATURE_COMINTEROP DWORD m_dwAttrClass; DWORD m_VMFlags; /* * We maintain some auxiliary flags in DEBUG builds, * this frees up some bits in m_wVMFlags */ #if defined(_DEBUG) WORD m_wAuxFlags; #endif // NOTE: Following BYTE fields are laid out together so they'll fit within the same DWORD for efficient // structure packing. BYTE m_NormType; BYTE m_cbBaseSizePadding; // How many bytes of padding are included in BaseSize WORD m_NumInstanceFields; WORD m_NumMethods; WORD m_NumStaticFields; WORD m_NumHandleStatics; WORD m_NumThreadStaticFields; WORD m_NumHandleThreadStatics; WORD m_NumNonVirtualSlots; DWORD m_NonGCStaticFieldBytes; DWORD m_NonGCThreadStaticFieldBytes; public: // EEClass optional field support. Whether a particular EEClass instance has optional fields is determined // at class load time. The entire EEClassOptionalFields structure is allocated if the EEClass has need of // one or more optional fields. #ifndef DACCESS_COMPILE void AttachOptionalFields(EEClassOptionalFields *pFields) { LIMITED_METHOD_CONTRACT; _ASSERTE(m_rpOptionalFields == NULL); m_rpOptionalFields = pFields; } #endif // !DACCESS_COMPILE bool HasOptionalFields() { LIMITED_METHOD_DAC_CONTRACT; return m_rpOptionalFields != NULL; } PTR_EEClassOptionalFields GetOptionalFields() { LIMITED_METHOD_DAC_CONTRACT; return m_rpOptionalFields; } //------------------------------------------------------------- // END CONCRETE DATA LAYOUT //------------------------------------------------------------- /************************************ * PROTECTED METHODS ************************************/ protected: #ifndef DACCESS_COMPILE /* * Constructor: prevent any other class from doing a new() */ EEClass() { LIMITED_METHOD_CONTRACT; } /* * Destructor: prevent any other class from deleting */ ~EEClass() { LIMITED_METHOD_CONTRACT; } #endif // !DACCESS_COMPILE template friend struct ::cdac_offsets; }; template<> struct cdac_offsets { static constexpr size_t InternalCorElementType = offsetof(EEClass, m_NormType); static constexpr size_t MethodTable = offsetof(EEClass, m_pMethodTable); static constexpr size_t NumMethods = offsetof(EEClass, m_NumMethods); static constexpr size_t CorTypeAttr = offsetof(EEClass, m_dwAttrClass); static constexpr size_t NumNonVirtualSlots = offsetof(EEClass, m_NumNonVirtualSlots); }; // -------------------------------------------------------------------------------------------- template class FixedCapacityStackingAllocatedUTF8StringHash { public: // Entry struct HashEntry { HashEntry * m_pNext; // Next item with same bucketed hash value DWORD m_dwHashValue; // Hash value LPCUTF8 m_pKey; // String key Data m_data; // Data }; HashEntry ** m_pBuckets; // Pointer to first entry for each bucket DWORD m_dwNumBuckets; BYTE * m_pMemory; // Current pointer into preallocated memory for entries BYTE * m_pMemoryStart; // Start pointer of pre-allocated memory fo entries INDEBUG(BYTE * m_pDebugEndMemory;) FixedCapacityStackingAllocatedUTF8StringHash() : m_pMemoryStart(NULL) { LIMITED_METHOD_CONTRACT; } static DWORD GetHashCode( LPCUTF8 szString) { WRAPPER_NO_CONTRACT; return HashStringA(szString); } // Throws on error void Init( DWORD dwMaxEntries, StackingAllocator * pAllocator); // Insert new entry at head of list void Insert( LPCUTF8 pszName, const Data & data); // Return the first matching entry in the list, or NULL if there is no such entry HashEntry * Lookup( LPCUTF8 pszName); // Return the next matching entry in the list, or NULL if there is no such entry. HashEntry * FindNext( HashEntry * pEntry); }; //--------------------------------------------------------------------------------------- // class LayoutEEClass : public EEClass { public: DAC_ALIGNAS(EEClass) // Align the first member to the alignment of the base class EEClassLayoutInfo m_LayoutInfo; Volatile m_nativeLayoutInfo; #ifndef DACCESS_COMPILE LayoutEEClass() : EEClass() { LIMITED_METHOD_CONTRACT; m_nativeLayoutInfo = NULL; } #endif // !DACCESS_COMPILE }; class UMThunkMarshInfo; #ifdef FEATURE_COMINTEROP struct CLRToCOMCallInfo; #endif // FEATURE_COMINTEROP class DelegateEEClass : public EEClass { public: DAC_ALIGNAS(EEClass) // Align the first member to the alignment of the base class PTR_Stub m_pStaticCallStub; PTR_Stub m_pInstRetBuffCallStub; PTR_MethodDesc m_pInvokeMethod; PTR_Stub m_pMultiCastInvokeStub; PTR_Stub m_pWrapperDelegateInvokeStub; UMThunkMarshInfo* m_pUMThunkMarshInfo; PTR_MethodDesc m_pBeginInvokeMethod; PTR_MethodDesc m_pEndInvokeMethod; Volatile m_pMarshalStub; #ifdef FEATURE_COMINTEROP CLRToCOMCallInfo *m_pCLRToCOMCallInfo; #endif // FEATURE_COMINTEROP PTR_MethodDesc GetInvokeMethod() { return m_pInvokeMethod; } PTR_MethodDesc GetBeginInvokeMethod() { return m_pBeginInvokeMethod; } PTR_MethodDesc GetEndInvokeMethod() { return m_pEndInvokeMethod; } #ifndef DACCESS_COMPILE DelegateEEClass() : EEClass() { LIMITED_METHOD_CONTRACT; // Note: Memory allocated on loader heap is zero filled } // We need a LoaderHeap that lives at least as long as the DelegateEEClass, but ideally no longer LoaderHeap *GetStubHeap(); #endif // !DACCESS_COMPILE }; typedef DPTR(ArrayClass) PTR_ArrayClass; // Dynamically generated array class structure class ArrayClass : public EEClass { friend MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementType arrayKind, unsigned Rank, AllocMemTracker *pamTracker); #ifndef DACCESS_COMPILE ArrayClass() { LIMITED_METHOD_CONTRACT; } #else friend class NativeImageDumper; #endif private: DAC_ALIGNAS(EEClass) // Align the first member to the alignment of the base class unsigned char m_rank; CorElementType m_ElementType;// Cache of element type in m_ElementTypeHnd public: DWORD GetRank() { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; return m_rank; } void SetRank (unsigned Rank) { LIMITED_METHOD_CONTRACT; // The only code path calling this function is code:ClassLoader::CreateTypeHandleForTypeKey, which has // checked the rank already. Assert that the rank is less than MAX_RANK and that it fits in one byte. _ASSERTE((Rank <= MAX_RANK) && (Rank <= (unsigned char)(-1))); m_rank = (unsigned char)Rank; } CorElementType GetArrayElementType() { LIMITED_METHOD_CONTRACT; return m_ElementType; } void SetArrayElementType(CorElementType ElementType) { LIMITED_METHOD_CONTRACT; m_ElementType = ElementType; } // Allocate a new MethodDesc for the methods we add to this class void InitArrayMethodDesc( ArrayMethodDesc* pNewMD, PCCOR_SIGNATURE pShortSig, DWORD cShortSig, DWORD dwVtableSlot, AllocMemTracker *pamTracker); // Generate a short sig for an array accessor VOID GenerateArrayAccessorCallSig(DWORD dwRank, DWORD dwFuncType, // Load, store, or PCCOR_SIGNATURE *ppSig, // Generated signature DWORD * pcSig, // Generated signature size LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker, BOOL fForStubAsIL ); template friend struct ::cdac_offsets; }; template<> struct cdac_offsets { static constexpr size_t Rank = offsetof(ArrayClass, m_rank); }; inline EEClassLayoutInfo *EEClass::GetLayoutInfo() { LIMITED_METHOD_CONTRACT; _ASSERTE(HasLayout()); return &((LayoutEEClass *) this)->m_LayoutInfo; } inline PTR_EEClassNativeLayoutInfo EEClass::GetNativeLayoutInfo() { LIMITED_METHOD_CONTRACT; _ASSERTE(HasLayout()); return ((LayoutEEClass*)this)->m_nativeLayoutInfo; } inline BOOL EEClass::IsBlittable() { LIMITED_METHOD_CONTRACT; // Either we have an opaque bunch of bytes, or we have some fields that are // all isomorphic and explicitly laid out. return (HasLayout() && GetLayoutInfo()->IsBlittable()); } inline BOOL EEClass::IsManagedSequential() { LIMITED_METHOD_CONTRACT; return HasLayout() && GetLayoutInfo()->IsManagedSequential(); } inline BOOL EEClass::HasExplicitSize() { LIMITED_METHOD_CONTRACT; return HasLayout() && GetLayoutInfo()->HasExplicitSize(); } inline BOOL EEClass::IsAutoLayoutOrHasAutoLayoutField() { LIMITED_METHOD_CONTRACT; // If this type is not auto return !HasLayout() || GetLayoutInfo()->HasAutoLayoutField(); } inline BOOL EEClass::IsInt128OrHasInt128Fields() { // The name of this type is a slight misnomer as it doesn't detect Int128 fields on // auto layout types, but since we only need this for interop scenarios, it works out. LIMITED_METHOD_CONTRACT; // If this type is not auto return HasLayout() && GetLayoutInfo()->IsInt128OrHasInt128Fields(); } //========================================================================== // These routines manage the prestub (a bootstrapping stub that all // FunctionDesc's are initialized with.) //========================================================================== VOID InitPreStubManager(); EXTERN_C void STDCALL ThePreStub(); inline PCODE GetPreStubEntryPoint() { return GetEEFuncEntryPoint(ThePreStub); } PCODE TheUMThunkPreStub(); PCODE TheVarargNDirectStub(BOOL hasRetBuffArg); // workaround: These classification bits need cleanup bad: for now, this gets around // IJW setting both mdUnmanagedExport & mdPinvokeImpl on expored methods. #define IsReallyMdPinvokeImpl(x) ( ((x) & mdPinvokeImpl) && !((x) & mdUnmanagedExport) ) // // The MethodNameHash is a temporary loader structure which may be allocated if there are a large number of // methods in a class, to quickly get from a method name to a MethodDesc (potentially a chain of MethodDescs). // #define METH_NAME_CACHE_SIZE 5 #define MAX_MISSES 3 // -------------------------------------------------------------------------------------------- // For generic instantiations the FieldDescs stored for instance // fields are approximate, not exact, i.e. they are representatives owned by // canonical instantiation and they do not carry exact type information. // This will not include EnC related fields. (See EncApproxFieldDescIterator for that) class ApproxFieldDescIterator { private: int m_iteratorType; PTR_FieldDesc m_pFieldDescList; int m_currField; int m_totalFields; public: enum IteratorType { INSTANCE_FIELDS = 0x1, STATIC_FIELDS = 0x2, ALL_FIELDS = (INSTANCE_FIELDS | STATIC_FIELDS) }; ApproxFieldDescIterator(); ApproxFieldDescIterator(MethodTable *pMT, int iteratorType) { SUPPORTS_DAC; Init(pMT, iteratorType); } void Init(MethodTable *pMT, int iteratorType); PTR_FieldDesc Next(); int GetIteratorType() { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; return m_iteratorType; } int Count() { LIMITED_METHOD_CONTRACT; return m_totalFields; } int CountRemaining() { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; return m_totalFields - m_currField - 1; } int GetValueClassCacheIndex() { LIMITED_METHOD_CONTRACT; return m_currField; } }; // // DeepFieldDescIterator iterates over the entire // set of fields available to a class, inherited or // introduced. // class DeepFieldDescIterator { private: ApproxFieldDescIterator m_fieldIter; int m_numClasses; int m_curClass; MethodTable* m_classes[16]; int m_deepTotalFields; bool m_lastNextFromParentClass; bool NextClass(); public: DeepFieldDescIterator() { LIMITED_METHOD_CONTRACT; m_numClasses = 0; m_curClass = 0; m_deepTotalFields = 0; m_lastNextFromParentClass = false; } DeepFieldDescIterator(MethodTable* pMT, int iteratorType, bool includeParents = true) { WRAPPER_NO_CONTRACT; Init(pMT, iteratorType, includeParents); } void Init(MethodTable* pMT, int iteratorType, bool includeParents = true); FieldDesc* Next(); bool Skip(int numSkip); int Count() { LIMITED_METHOD_CONTRACT; return m_deepTotalFields; } bool IsFieldFromParentClass() { LIMITED_METHOD_CONTRACT; return m_lastNextFromParentClass; } }; #endif // !CLASS_H