// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. // // File: typehandle.h // // // // ============================================================================ #ifndef TYPEHANDLE_H #define TYPEHANDLE_H #include "check.h" #include "classloadlevel.h" #include "fixuppointer.h" class TypeDesc; class TypeHandle; class Instantiation; class ArrayTypeDesc; class FnPtrTypeDesc; class ParamTypeDesc; class TypeVarTypeDesc; class MethodTable; class EEClass; class Module; class Assembly; class BaseDomain; class MethodDesc; class TypeKey; class TypeHandleList; class InstantiationContext; class DataImage; namespace Generics { class RecursionGraph; } struct CORINFO_CLASS_STRUCT_; typedef DPTR(class TypeVarTypeDesc) PTR_TypeVarTypeDesc; typedef SPTR(class FnPtrTypeDesc) PTR_FnPtrTypeDesc; typedef DPTR(class ParamTypeDesc) PTR_ParamTypeDesc; typedef DPTR(class ArrayTypeDesc) PTR_ArrayTypeDesc; typedef DPTR(class TypeDesc) PTR_TypeDesc; typedef DPTR(class TypeHandle) PTR_TypeHandle; typedef CUnorderedArray DFLPendingList; class TypeHandlePairList; #ifdef FEATURE_COMINTEROP class ComCallWrapperTemplate; #endif // FEATURE_COMINTEROP /*************************************************************************/ // A TypeHandle is the FUNDAMENTAL concept of type identity in the CLR. // That is two types are equal if and only if their type handles // are equal. A TypeHandle, is a pointer sized struture that encodes // everything you need to know to figure out what kind of type you are // actually dealing with. // At the present time a TypeHandle can point at two possible things // // 1) A MethodTable (Intrinsics, Classes, Value Types and their instantiations) // 2) A TypeDesc (all other cases: arrays, byrefs, pointer types, function pointers, generic type variables) // // or with IL stubs, a third thing: // // 3) A MethodTable for a native value type. // // Array MTs are not valid TypeHandles: for example no allocated object will // ever return such a type handle from Object::GetTypeHandle(), and // these type handles should not be passed across the JIT Interface // as CORINFO_CLASS_HANDLEs. However some code in the EE does create // temporary TypeHandles out of these MTs, so we can't yet assert // !pMT->IsArray() in the TypeHandle constructor. // // Wherever possible, you should be using TypeHandles or MethodTables. // Code that is known to work over Class/ValueClass types (including their // instantaitions) is currently written to use MethodTables. // // TypeDescs in turn break down into several variants and are // for special cases around the edges // - array types whose method tables get share // - types for function pointers for verification and reflection // - types for generic parameters for verification and reflection // // Generic type instantiations (in C# syntax: C) are represented by // MethodTables, i.e. a new MethodTable gets allocated for each such instantiation. // The entries in these tables (i.e. the code) are, however, often shared. // Clients of TypeHandle don't need to know any of this detail; just use the // GetInstantiation and HasInstantiation methods. class TypeHandle { public: TypeHandle() { LIMITED_METHOD_DAC_CONTRACT; m_asTAddr = 0; } static TypeHandle FromPtr(PTR_VOID aPtr) { LIMITED_METHOD_DAC_CONTRACT; return TypeHandle(dac_cast(aPtr)); } // Create a TypeHandle from the target address of a MethodTable static TypeHandle FromTAddr(TADDR data) { LIMITED_METHOD_DAC_CONTRACT; return TypeHandle(data); } // When you ask for a class in JitInterface when all you have // is a methodDesc of an array method... // Convert from a JitInterface handle to an internal EE TypeHandle explicit TypeHandle(struct CORINFO_CLASS_STRUCT_*aPtr) { LIMITED_METHOD_DAC_CONTRACT; m_asTAddr = dac_cast(aPtr); // NormalizeUnsharedArrayMT(); INDEBUGIMPL(Verify()); } TypeHandle(MethodTable const * aMT) { LIMITED_METHOD_DAC_CONTRACT; m_asTAddr = dac_cast(aMT); // NormalizeUnsharedArrayMT(); INDEBUGIMPL(Verify()); } explicit TypeHandle(TypeDesc *aType) { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(aType); m_asTAddr = (dac_cast(aType) | 2); INDEBUGIMPL(Verify()); } inline BOOL IsNativeValueType() const; inline MethodTable *AsNativeValueType() const; private: // This constructor has been made private. You must use the explicit static functions // TypeHandle::FromPtr and TypeHandle::TAddr instead of these constructors. // Allowing a public constructor that takes a "void *" or a "TADDR" is error-prone. explicit TypeHandle(TADDR aTAddr) { LIMITED_METHOD_DAC_CONTRACT; m_asTAddr = aTAddr; // NormalizeUnsharedArrayMT(); INDEBUGIMPL(Verify()); } public: FORCEINLINE int operator==(const TypeHandle& typeHnd) const { LIMITED_METHOD_DAC_CONTRACT; return(m_asTAddr == typeHnd.m_asTAddr); } FORCEINLINE int operator!=(const TypeHandle& typeHnd) const { LIMITED_METHOD_DAC_CONTRACT; return(m_asTAddr != typeHnd.m_asTAddr); } // Methods for probing exactly what kind of a type handle we have FORCEINLINE BOOL IsNull() const { LIMITED_METHOD_DAC_CONTRACT; #ifdef _PREFIX_ if (m_asTAddr == 0) { #ifndef DACCESS_COMPILE PREFIX_ASSUME(m_asPtr == NULL); #endif return true; } else { #ifndef DACCESS_COMPILE PREFIX_ASSUME(m_asPtr != NULL); #endif return false; } #else return(m_asTAddr == 0); #endif } // Note that this returns denormalized BOOL to help the compiler with optimizations FORCEINLINE BOOL IsTypeDesc() const { LIMITED_METHOD_DAC_CONTRACT; #ifdef _PREFIX_ if (m_asTAddr & 2) { PREFIX_ASSUME(m_asTAddr != NULL); #ifndef DACCESS_COMPILE PREFIX_ASSUME(m_asPtr != NULL); #endif return true; } else { return false; } #else return(m_asTAddr & 2); #endif } BOOL IsEnum() const; BOOL IsFnPtrType() const; inline PTR_MethodTable AsMethodTable() const; inline PTR_TypeDesc AsTypeDesc() const; // To the extent possible, you should try to use methods like the ones // below that treat all types uniformly. // Gets the size that this type would take up embedded in another object // thus objects all return sizeof(void*). unsigned GetSize() const; // Returns the type name, including the generic instantiation if possible. // See the TypeString class for better control over name formatting. void GetName(SString &result) const; // Returns the ELEMENT_TYPE_* that you would use in a signature // The only normalization that happens is that for type handles // for instantiated types (e.g. class List or // value type Pair)) this returns either ELEMENT_TYPE_CLASS // or ELEMENT_TYPE_VALUE, _not_ ELEMENT_TYPE_WITH. CorElementType GetSignatureCorElementType() const; // This helper: // - Will return enums underlying type // - Will return underlying primitive for System.Int32 etc... // - Will return underlying primitive as will be used in the calling convention // For example // struct t // { // public int i; // } // will return ELEMENT_TYPE_I4 in x86 instead of ELEMENT_TYPE_VALUETYPE. We // call this type of value type a primitive value type // // Internal representation is used among another things for the calling convention // (jit benefits of primitive value types) or optimizing marshalling. // // This will NOT convert E_T_ARRAY, E_T_SZARRAY etc. to E_T_CLASS (though it probably // should). Use CorTypeInfo::IsObjRef for that. CorElementType GetInternalCorElementType() const; // This helper will return the same as GetSignatureCorElementType except: // - Will return enums underlying type CorElementType GetVerifierCorElementType() const; //------------------------------------------------------------------- // CASTING // // There are two variants of the "CanCastTo" method: // // CanCastTo // - restore encoded pointers on demand // - might throw, might trigger GC // - return type is boolean (FALSE = cannot cast, TRUE = can cast) // // CanCastToNoGC // - do not restore encoded pointers on demand // - does not throw, does not trigger GC // - return type is three-valued (CanCast, CannotCast, MaybeCast) // - MaybeCast indicates that the test tripped on an encoded pointer // so the caller should now call CanCastTo if it cares // // Note that if the TypeHandle is a valuetype, the caller is responsible // for checking that the valuetype is in its boxed form before calling // CanCastTo. Otherwise, the caller should be using IsBoxedAndCanCastTo() typedef enum { CannotCast, CanCast, MaybeCast } CastResult; BOOL CanCastTo(TypeHandle type, TypeHandlePairList *pVisited = NULL) const; BOOL IsBoxedAndCanCastTo(TypeHandle type, TypeHandlePairList *pVisited) const; CastResult CanCastToNoGC(TypeHandle type) const; #ifndef DACCESS_COMPILE // Type equivalence based on Guid and TypeIdentifier attributes inline BOOL IsEquivalentTo(TypeHandle type COMMA_INDEBUG(TypeHandlePairList *pVisited = NULL)) const; #endif // Get the parent, known to be decoded TypeHandle GetParent() const; // Obtain element type for an array or pointer, returning NULL otherwise TypeHandle GetTypeParam() const; // Obtain instantiation from an instantiated type // NULL if not instantiated Instantiation GetInstantiation() const; // Does this type satisfy its class constraints, recursively up the hierarchy BOOL SatisfiesClassConstraints() const; TypeHandle Instantiate(Instantiation inst) const; TypeHandle MakePointer() const; TypeHandle MakeByRef() const; TypeHandle MakeSZArray() const; TypeHandle MakeArray(int rank) const; TypeHandle MakeNativeValueType() const; // Obtain instantiation from an instantiated type *or* a pointer to the element type for an array Instantiation GetClassOrArrayInstantiation() const; // Is this type instantiated? BOOL HasInstantiation() const; // Is this a generic type whose type arguments are its formal type parameters? BOOL IsGenericTypeDefinition() const; // Is this either a non-generic type (e.g. a non-genric class type or an array type or a pointer type etc.) // or a generic type whose type arguments are its formal type parameters? //Equivalent to (!HasInstantiation() || IsGenericTypeDefinition()); inline BOOL IsTypicalTypeDefinition() const; enum InteropKind { Interop_ManagedToNative, // use for RCW-related queries Interop_NativeToManaged, // use for CCW-related queries }; inline BOOL SupportsGenericInterop(InteropKind interopKind) const; BOOL IsSharedByGenericInstantiations() const; // Recursively search the type arguments and if // one of the type arguments is Canon then return TRUE // // A<__Canon> is the canonical TypeHandle (aka "representative" generic MT) // A> is a subtype that contains a Canonical type // BOOL IsCanonicalSubtype() const; // Similar to IsCanonicalSubtype, but applied to a vector. static BOOL IsCanonicalSubtypeInstantiation(Instantiation inst); // For an uninstantiated generic type, return the number of type parameters required for instantiation // For an instantiated type, return the number of type parameters in the instantiation // Otherwise return 0 DWORD GetNumGenericArgs() const; BOOL IsValueType() const; BOOL IsInterface() const; BOOL IsAbstract() const; inline DWORD IsObjectType() const { LIMITED_METHOD_CONTRACT; return *this == TypeHandle(g_pObjectClass); } DWORD IsTransparentProxy() const; // Retrieve the key corresponding to this handle TypeKey GetTypeKey() const; // To what level has this type been loaded? ClassLoadLevel GetLoadLevel() const; // Equivalent to GetLoadLevel() == CLASS_LOADED BOOL IsFullyLoaded() const; void DoFullyLoad(Generics::RecursionGraph *pVisited, ClassLoadLevel level, DFLPendingList *pPending, BOOL *pfBailed, const InstantiationContext *pInstContext); inline void SetIsFullyLoaded(); #ifdef _DEBUG // Check that this type matches the key given // i.e. that all aspects (element type, module/token, rank for arrays, instantiation for generic types) match up CHECK CheckMatchesKey(TypeKey *pKey) const; // Check that this type is loaded up to the level indicated // Also check that it is non-null CHECK CheckLoadLevel(ClassLoadLevel level); // Equivalent to CheckLoadLevel(CLASS_LOADED) CHECK CheckFullyLoaded(); #endif bool IsHFA() const; CorElementType GetHFAType() const; #ifdef FEATURE_64BIT_ALIGNMENT bool RequiresAlign8() const; #endif // FEATURE_64BIT_ALIGNMENT #ifndef DACCESS_COMPILE BOOL IsBlittable() const; BOOL HasLayout() const; #ifdef FEATURE_COMINTEROP TypeHandle GetCoClassForInterface() const; DWORD IsComClassInterface() const; BOOL IsComObjectType() const; BOOL IsComEventItfType() const; CorIfaceAttr GetComInterfaceType() const; TypeHandle GetDefItfForComClassItf() const; BOOL IsProjectedFromWinRT() const; BOOL IsExportedToWinRT() const; ComCallWrapperTemplate *GetComCallWrapperTemplate() const; BOOL SetComCallWrapperTemplate(ComCallWrapperTemplate *pTemplate); #endif // FEATURE_COMINTEROP #endif // Unlike AsMethodTable, GetMethodTable will get the method table // of the type, regardless of whether it is an array etc. Note, however // this method table may be shared, and some types (like TypeByRef), have // no method table (and this function returns NULL for them) inline PTR_MethodTable GetMethodTable() const; // Returns the method table which should be used for visibility checking. // Like GetMethodTable except for TypeDescs returns the root ElementType. // So for Foo[] instead of returning Array returns Foo. inline MethodTable* GetMethodTableOfElementType() const; // Returns the MethodTable for the SZARRAY or ARRAY type inline MethodTable * GetPossiblySharedArrayMethodTable() const; // As above but returns a TypeHandle (so it will return a non-null result // for generic type variables, for instance). inline TypeHandle GetElementType() const; // Return the canonical representative MT amongst the set of MT's that share // code with the MT for the given TypeHandle because of generics. PTR_MethodTable GetCanonicalMethodTable() const; // The module that defined the underlying type // (First strip off array/ptr qualifiers and generic type arguments) PTR_Module GetModule() const; // The ngen'ed module where this type lives PTR_Module GetZapModule() const; // Does this immediate item live in an NGEN module? BOOL IsZapped() const; // The module where this type lives for the purposes of loading and prejitting // Note: NGen time result might differ from runtime result for parametrized types (generics, arrays, etc.) // See code:ClassLoader::ComputeLoaderModule or file:clsload.hpp#LoaderModule for more information PTR_Module GetLoaderModule() const; // The assembly that defined this type (== GetModule()->GetAssembly()) Assembly * GetAssembly() const; // GetDomain on an instantiated type, e.g. C returns the SharedDomain if all the // constituent parts of the type are SharedDomain (i.e. domain-neutral), // and returns an AppDomain if any of the parts are from an AppDomain, // i.e. are domain-bound. If any of the parts are domain-bound // then they will all belong to the same domain. PTR_BaseDomain GetDomain() const; PTR_LoaderAllocator GetLoaderAllocator() const; BOOL IsDomainNeutral() const; // Get the class token, assuming the type handle represents a named type, // i.e. a class, a value type, a generic instantiation etc. inline mdTypeDef GetCl() const; // Shortcuts // ARRAY or SZARRAY TypeDesc (arrays with a shared MethodTable) // If this is TRUE, it is OK to call AsArray() // Also see IsArrayType() BOOL IsArray() const; // See comment of IsArrayType() for the explanation of this method #if 0 void NormalizeUnsharedArrayMT(); #endif // ARRAY or SZARRAY // Note that this does not imply that it is OK to call AsArray(). See IsArray() // // All arrays, even those with a unique unshared MethodTable, have an ArrayTypeDesc // which is used for type identity. However, over time, people have started // wrapping the MethodTables directly in a TypeHandle. Note that such // TypeHandles cannot be used for type identity. However, IsArrayType() lets // you check even for such cases where IsArray() returns FALSE, but the type // still is an array type. // // @TODO: Change all the constructors of TypeHandle which take a MethodTable // to call NormalizeUnsharedArrayMT(). TypeHandle::Verify() can then enforce // that IsArray() is fully correct. BOOL IsArrayType() const; // VAR or MVAR BOOL IsGenericVariable() const; // BYREF BOOL IsByRef() const; // BYREFLIKE (does not return TRUE for IsByRef types) BOOL IsByRefLike() const; // PTR BOOL IsPointer() const; // True if this type *is* a formal generic type parameter or any component of it is a formal generic type parameter BOOL ContainsGenericVariables(BOOL methodOnly=FALSE) const; Module* GetDefiningModuleForOpenType() const; // Is actually ParamTypeDesc (ARRAY, SZARRAY, BYREF, PTR) BOOL HasTypeParam() const; BOOL IsRestored_NoLogging() const; BOOL IsRestored() const; // Does this type have zap-encoded components (generic arguments, etc)? BOOL HasUnrestoredTypeKey() const; // True if this type handle is a zap-encoded fixup BOOL IsEncodedFixup() const; // Only used at NGEN-time BOOL ComputeNeedsRestore(DataImage *image, TypeHandleList *pVisited) const; void DoRestoreTypeKey(); void CheckRestore() const; BOOL IsExternallyVisible() const; // Is this type part of an assembly loaded for introspection? BOOL IsIntrospectionOnly() const; // Checks this type and its components for "IsIntrospectionOnly" BOOL ContainsIntrospectionOnlyTypes() const; // Does this type participate in type equivalence? inline BOOL HasTypeEquivalence() const; // Not clear we should have this. inline PTR_ArrayTypeDesc AsArray() const; FnPtrTypeDesc* AsFnPtrType() const; TypeVarTypeDesc* AsGenericVariable() const; Instantiation GetInstantiationOfParentClass(MethodTable *pWhichParent) const; PTR_VOID AsPtr() const { // Please don't use this if you can avoid it LIMITED_METHOD_DAC_CONTRACT; return(PTR_VOID(m_asTAddr)); } TADDR AsTAddr() const { LIMITED_METHOD_DAC_CONTRACT; return m_asTAddr; } INDEBUGIMPL(BOOL Verify();) // DEBUGGING Make certain this is a valid type handle #if defined(_DEBUG) BOOL IsAppDomainAgile() const; BOOL IsCheckAppDomainAgile() const; BOOL IsArrayOfElementsAppDomainAgile() const; BOOL IsArrayOfElementsCheckAppDomainAgile() const; #endif #ifdef DACCESS_COMPILE void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); #endif OBJECTREF GetManagedClassObject() const; OBJECTREF GetManagedClassObjectFast() const; static TypeHandle MergeArrayTypeHandlesToCommonParent( TypeHandle ta, TypeHandle tb); static TypeHandle MergeTypeHandlesToCommonParent( TypeHandle ta, TypeHandle tb); BOOL NotifyDebuggerLoad(AppDomain *domain, BOOL attaching) const; void NotifyDebuggerUnload(AppDomain *domain) const; // Execute the callback functor for each MethodTable that makes up the given type handle. This method // does not invoke the functor for generic variables template inline void ForEachComponentMethodTable(T &callback) const; private: static TypeHandle MergeClassWithInterface( TypeHandle tClass, TypeHandle tInterface); union { TADDR m_asTAddr; // we look at the low order bits #ifndef DACCESS_COMPILE void * m_asPtr; PTR_MethodTable m_asMT; PTR_TypeDesc m_asTypeDesc; PTR_ArrayTypeDesc m_asArrayTypeDesc; PTR_ParamTypeDesc m_asParamTypeDesc; PTR_TypeVarTypeDesc m_asTypeVarTypeDesc; PTR_FnPtrTypeDesc m_asFnPtrTypeDesc; #endif }; }; class TypeHandleList { TypeHandle m_typeHandle; TypeHandleList* m_pNext; bool m_fBrokenCycle; public: TypeHandleList(TypeHandle t, TypeHandleList* pNext) : m_typeHandle(t),m_pNext(pNext),m_fBrokenCycle(false) { }; static BOOL Exists(TypeHandleList* pList, TypeHandle t) { LIMITED_METHOD_CONTRACT; while (pList != NULL) { if (pList->m_typeHandle == t) return TRUE; pList = pList->m_pNext; } return FALSE; } // Supports enumeration of the list. static BOOL GetNext(TypeHandleList** ppList, TypeHandle* pHandle) { LIMITED_METHOD_CONTRACT; if (*ppList != NULL) { *pHandle = (*ppList)->m_typeHandle; (*ppList) = (*ppList)->m_pNext; return TRUE; } return FALSE; } void MarkBrokenCycle(TypeHandle th) { LIMITED_METHOD_CONTRACT; TypeHandleList* pList = this; while (pList->m_typeHandle != th) { pList->m_fBrokenCycle = true; pList = pList->m_pNext; } } bool HasBrokenCycleMark() { LIMITED_METHOD_CONTRACT; return m_fBrokenCycle; } }; class TypeHandlePairList // TODO: Template for TypeHandleList, TypeHandlePairList, TokenPairList? { TypeHandle m_typeHandle1; TypeHandle m_typeHandle2; TypeHandlePairList *m_pNext; public: TypeHandlePairList(TypeHandle t1, TypeHandle t2, TypeHandlePairList *pNext) : m_typeHandle1(t1), m_typeHandle2(t2), m_pNext(pNext) { }; static BOOL Exists(TypeHandlePairList *pList, TypeHandle t1, TypeHandle t2) { LIMITED_METHOD_CONTRACT; while (pList != NULL) { if (pList->m_typeHandle1 == t1 && pList->m_typeHandle2 == t2) return TRUE; if (pList->m_typeHandle1 == t2 && pList->m_typeHandle2 == t1) return TRUE; pList = pList->m_pNext; } return FALSE; } }; #if CHECK_INVARIANTS inline CHECK CheckPointer(TypeHandle th, IsNullOK ok = NULL_NOT_OK) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; STATIC_CONTRACT_FORBID_FAULT; STATIC_CONTRACT_SO_TOLERANT; SUPPORTS_DAC; STATIC_CONTRACT_CANNOT_TAKE_LOCK; if (th.IsNull()) { CHECK_MSG(ok, "Illegal null TypeHandle"); } else { __if_exists(TypeHandle::Check) { CHECK(th.Check()); } #if 0 CHECK(CheckInvariant(o)); #endif } CHECK_OK; } #endif // CHECK_INVARIANTS /*************************************************************************/ // dac_casts for TypeHandle makes FixupPointer work. // // TypeHandle is wrapper around pointer to MethodTable or TypeDesc. Even though // it may feel counterintuitive, it is possible to treat it like a pointer and // use the regular FixupPointer to implement TypeHandle indirection cells. // The lowest bit of TypeHandle (when wrapped inside FixupPointer) is // used to mark optional indirection. // template<> inline TADDR dac_cast(TypeHandle src) { SUPPORTS_DAC; return src.AsTAddr(); } template<> inline TypeHandle dac_cast(TADDR src) { SUPPORTS_DAC; return TypeHandle::FromTAddr(src); } /*************************************************************************/ // Instantiation is representation of generic instantiation. // It is simple read-only array of TypeHandles. In NGen, the type handles // may be encoded using indirections. That's one reason why it is convenient // to have wrapper class that performs the decoding. class Instantiation { public: // Construct empty instantiation Instantiation() : m_pArgs(NULL), m_nArgs(0) { LIMITED_METHOD_DAC_CONTRACT; } // Copy construct Instantiation(const Instantiation & inst) : m_pArgs(inst.m_pArgs), m_nArgs(inst.m_nArgs) { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(m_nArgs == 0 || m_pArgs != NULL); } // Construct instantiation from array of FixupPointers Instantiation(FixupPointer * pArgs, DWORD nArgs) : m_pArgs(pArgs), m_nArgs(nArgs) { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(m_nArgs == 0 || m_pArgs != NULL); } // Construct instantiation from array of TypeHandles Instantiation(TypeHandle * pArgs, DWORD nArgs) : m_nArgs(nArgs) { LIMITED_METHOD_DAC_CONTRACT; DACCOP_IGNORE(CastOfMarshalledType, "Dual mode DAC problem, but since the size is the same, the cast is safe"); m_pArgs = (FixupPointer *)pArgs; _ASSERTE(m_nArgs == 0 || m_pArgs != NULL); } #ifdef DACCESS_COMPILE // Construct instantiation from target array of FixupPointers in DAC. // This method will create local copy of the instantiation arguments. Instantiation(DPTR(FixupPointer) pArgs, DWORD nArgs) { LIMITED_METHOD_DAC_CONTRACT; // Create a local copy of the instanitation under DAC PVOID pLocalArgs = PTR_READ(dac_cast(pArgs), nArgs * sizeof(TypeHandle)); m_pArgs = (FixupPointer *)pLocalArgs; m_nArgs = nArgs; _ASSERTE(m_nArgs == 0 || m_pArgs != NULL); } #endif // Return i-th instantiation argument TypeHandle operator[](DWORD iArg) const { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(iArg < m_nArgs); return m_pArgs[iArg].GetValue(); } DWORD GetNumArgs() const { LIMITED_METHOD_DAC_CONTRACT; return m_nArgs; } BOOL IsEmpty() const { LIMITED_METHOD_DAC_CONTRACT; return m_nArgs == 0; } // Unsafe access to the instantiation. Do not use unless absolutely necessary!!! FixupPointer * GetRawArgs() const { LIMITED_METHOD_DAC_CONTRACT; return m_pArgs; } private: // Note that for DAC builds, m_pArgs may be host allocated buffer, not a copy of an object marshalled by DAC. FixupPointer * m_pArgs; DWORD m_nArgs; }; #endif // TYPEHANDLE_H