// 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. // // method.hpp // // // See the book of the runtime entry for overall design: // file:../../doc/BookOfTheRuntime/ClassLoader/MethodDescDesign.doc // #ifndef _METHOD_H #define _METHOD_H #include "cor.h" #include "util.hpp" #include "clsload.hpp" #include "codeman.h" #include "class.h" #include "siginfo.hpp" #include "declsec.h" #include "methodimpl.h" #include "typedesc.h" #include #include "eeconfig.h" #include "precode.h" #include "codeversion.h" #ifndef FEATURE_PREJIT #include "fixuppointer.h" #endif class Stub; class FCallMethodDesc; class FieldDesc; class NDirect; class MethodDescChunk; struct LayoutRawFieldInfo; class InstantiatedMethodDesc; class DictionaryLayout; class Dictionary; class GCCoverageInfo; class DynamicMethodDesc; class ReJitManager; class CodeVersionManager; class PrepareCodeConfig; class CallCounter; typedef DPTR(FCallMethodDesc) PTR_FCallMethodDesc; typedef DPTR(ArrayMethodDesc) PTR_ArrayMethodDesc; typedef DPTR(DynamicMethodDesc) PTR_DynamicMethodDesc; typedef DPTR(InstantiatedMethodDesc) PTR_InstantiatedMethodDesc; typedef DPTR(GCCoverageInfo) PTR_GCCoverageInfo; // see code:GCCoverageInfo::savedCode #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS GVAL_DECL(DWORD, g_MiniMetaDataBuffMaxSize); GVAL_DECL(TADDR, g_MiniMetaDataBuffAddress); #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS EXTERN_C VOID STDCALL NDirectImportThunk(); #define METHOD_TOKEN_REMAINDER_BIT_COUNT 14 #define METHOD_TOKEN_REMAINDER_MASK ((1 << METHOD_TOKEN_REMAINDER_BIT_COUNT) - 1) #define METHOD_TOKEN_RANGE_BIT_COUNT (24 - METHOD_TOKEN_REMAINDER_BIT_COUNT) #define METHOD_TOKEN_RANGE_MASK ((1 << METHOD_TOKEN_RANGE_BIT_COUNT) - 1) //============================================================= // Splits methoddef token into two pieces for // storage inside a methoddesc. //============================================================= FORCEINLINE UINT16 GetTokenRange(mdToken tok) { LIMITED_METHOD_CONTRACT; return (UINT16)((tok>>METHOD_TOKEN_REMAINDER_BIT_COUNT) & METHOD_TOKEN_RANGE_MASK); } FORCEINLINE VOID SplitToken(mdToken tok, UINT16 *ptokrange, UINT16 *ptokremainder) { LIMITED_METHOD_CONTRACT; *ptokrange = (UINT16)((tok>>METHOD_TOKEN_REMAINDER_BIT_COUNT) & METHOD_TOKEN_RANGE_MASK); *ptokremainder = (UINT16)(tok & METHOD_TOKEN_REMAINDER_MASK); } FORCEINLINE mdToken MergeToken(UINT16 tokrange, UINT16 tokremainder) { LIMITED_METHOD_DAC_CONTRACT; return (tokrange << METHOD_TOKEN_REMAINDER_BIT_COUNT) | tokremainder | mdtMethodDef; } // The MethodDesc is a union of several types. The following // 3-bit field determines which type it is. Note that JIT'ed/non-JIT'ed // is not represented here because this isn't known until the // method is executed for the first time. Because any thread could // change this bit, it has to be done in a place where access is // synchronized. // **** NOTE: if you add any new flags, make sure you add them to ClearFlagsOnUpdate // so that when a method is replaced its relevant flags are updated // Used in MethodDesc enum MethodClassification { mcIL = 0, // IL mcFCall = 1, // FCall (also includes tlbimped ctor, Delegate ctor) mcNDirect = 2, // N/Direct mcEEImpl = 3, // special method; implementation provided by EE (like Delegate Invoke) mcArray = 4, // Array ECall mcInstantiated = 5, // Instantiated generic methods, including descriptors // for both shared and unshared code (see InstantiatedMethodDesc) #ifdef FEATURE_COMINTEROP // This needs a little explanation. There are MethodDescs on MethodTables // which are Interfaces. These have the mdcInterface bit set. Then there // are MethodDescs on MethodTables that are Classes, where the method is // exposed through an interface. These do not have the mdcInterface bit set. // // So, today, a dispatch through an 'mdcInterface' MethodDesc is either an // error (someone forgot to look up the method in a class' VTable) or it is // a case of COM Interop. mcComInterop = 6, #endif // FEATURE_COMINTEROP mcDynamic = 7, // for method desc with no metadata behind mcCount, }; // All flags in the MethodDesc now reside in a single 16-bit field. enum MethodDescClassification { // Method is IL, FCall etc., see MethodClassification above. mdcClassification = 0x0007, mdcClassificationCount = mdcClassification+1, // Note that layout of code:MethodDesc::s_ClassificationSizeTable depends on the exact values // of mdcHasNonVtableSlot and mdcMethodImpl // Has local slot (vs. has real slot in MethodTable) mdcHasNonVtableSlot = 0x0008, // Method is a body for a method impl (MI_MethodDesc, MI_NDirectMethodDesc, etc) // where the function explicitly implements IInterface.foo() instead of foo(). mdcMethodImpl = 0x0010, // Method is static mdcStatic = 0x0020, // unused = 0x0040, // unused = 0x0080, // unused = 0x0100, // unused = 0x0200, // Duplicate method. When a method needs to be placed in multiple slots in the // method table, because it could not be packed into one slot. For eg, a method // providing implementation for two interfaces, MethodImpl, etc mdcDuplicate = 0x0400, // Has this method been verified? mdcVerifiedState = 0x0800, // Is the method verifiable? It needs to be verified first to determine this mdcVerifiable = 0x1000, // Is this method ineligible for inlining? mdcNotInline = 0x2000, // Is the method synchronized mdcSynchronized = 0x4000, // Does the method's slot number require all 16 bits mdcRequiresFullSlotNumber = 0x8000 }; #define METHOD_MAX_RVA 0x7FFFFFFF // The size of this structure needs to be a multiple of MethodDesc::ALIGNMENT // // @GENERICS: // Method descriptors for methods belonging to instantiated types may be shared between compatible instantiations // Hence for reflection and elsewhere where exact types are important it's necessary to pair a method desc // with the exact owning type handle. // // See genmeth.cpp for details of instantiated generic method descriptors. // // A MethodDesc is the representation of a method of a type. These live in code:MethodDescChunk which in // turn lives in code:EEClass. They are conceptually cold (we do not expect to access them in normal // program exectution, but we often fall short of that goal. // // A Method desc knows how to get at its metadata token code:GetMemberDef, its chunk // code:MethodDescChunk, which in turns knows how to get at its type code:MethodTable. // It also knows how to get at its IL code (code:IMAGE_COR_ILMETHOD) class MethodDesc { friend class EEClass; friend class MethodTableBuilder; friend class ArrayClass; friend class NDirect; friend class MethodDescChunk; friend class InstantiatedMethodDesc; friend class MethodImpl; friend class CheckAsmOffsets; friend class ClrDataAccess; friend class MethodDescCallSite; #ifdef DACCESS_COMPILE friend class NativeImageDumper; #endif public: enum { #ifdef _WIN64 ALIGNMENT_SHIFT = 3, #else ALIGNMENT_SHIFT = 2, #endif ALIGNMENT = (1< m_pDebugMethodTable; PTR_GCCoverageInfo m_GcCover; #endif // _DEBUG inline BOOL HasStableEntryPoint() { LIMITED_METHOD_DAC_CONTRACT; return (m_bFlags2 & enum_flag2_HasStableEntryPoint) != 0; } inline PCODE GetStableEntryPoint() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(HasStableEntryPoint()); return GetMethodEntryPoint(); } BOOL SetStableEntryPointInterlocked(PCODE addr); BOOL HasTemporaryEntryPoint(); PCODE GetTemporaryEntryPoint(); void SetTemporaryEntryPoint(LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker); inline BOOL HasPrecode() { LIMITED_METHOD_DAC_CONTRACT; return (m_bFlags2 & enum_flag2_HasPrecode) != 0; } inline Precode* GetPrecode() { LIMITED_METHOD_DAC_CONTRACT; PRECONDITION(HasPrecode()); Precode* pPrecode = Precode::GetPrecodeFromEntryPoint(GetStableEntryPoint()); PREFIX_ASSUME(pPrecode != NULL); return pPrecode; } inline BOOL MayHavePrecode() { CONTRACTL { THROWS; GC_TRIGGERS; MODE_ANY; } CONTRACTL_END return !MayHaveNativeCode() || IsVersionableWithPrecode(); } void InterlockedUpdateFlags2(BYTE bMask, BOOL fSet); Precode* GetOrCreatePrecode(); #ifdef FEATURE_PREJIT Precode * GetSavedPrecode(DataImage *image); Precode * GetSavedPrecodeOrNull(DataImage *image); #endif // FEATURE_PREJIT // Given a code address return back the MethodDesc whenever possible // static MethodDesc * GetMethodDescFromStubAddr(PCODE addr, BOOL fSpeculative = FALSE); DWORD GetAttrs() const; DWORD GetImplAttrs(); // This function can lie if a method impl was used to implement // more than one method on this class. Use GetName(int) to indicate // which slot you are interested in. // See the TypeString class for better control over name formatting. LPCUTF8 GetName(); LPCUTF8 GetName(USHORT slot); void PrecomputeNameHash(); BOOL MightHaveName(ULONG nameHashValue); FORCEINLINE LPCUTF8 GetNameOnNonArrayClass() { WRAPPER_NO_CONTRACT; LPCSTR szName; if (FAILED(GetMDImport()->GetNameOfMethodDef(GetMemberDef(), &szName))) { szName = NULL; } return szName; } COUNT_T GetStableHash(); // Non-zero for InstantiatedMethodDescs DWORD GetNumGenericMethodArgs(); // Return the number of class type parameters that are in scope for this method DWORD GetNumGenericClassArgs() { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; return GetMethodTable()->GetNumGenericArgs(); } // True if this is a method descriptor for an instantiated generic method // whose method type arguments are the formal type parameters of the generic method // NOTE: the declaring class may not be the generic type definition e.g. consider C.m BOOL IsGenericMethodDefinition() const; // True if the declaring type or instantiation of method (if any) contains formal generic type parameters BOOL ContainsGenericVariables(); Module* GetDefiningModuleForOpenMethod(); // True if this is a class and method instantiation that on <__Canon,...,__Canon> BOOL IsTypicalSharedInstantiation(); // True if and only if this is a method desriptor for : // 1. a non-generic method or a generic method at its typical method instantiation // 2. in a non-generic class or a typical instantiation of a generic class // This method can be called on a non-restored method desc BOOL IsTypicalMethodDefinition() const; // Force a load of the (typical) constraints on the type parameters of a typical method definition, // detecting cyclic bounds on class and method type parameters. void LoadConstraintsForTypicalMethodDefinition(BOOL *pfHasCircularClassConstraints, BOOL *pfHasCircularMethodConstraints, ClassLoadLevel level = CLASS_LOADED); DWORD IsClassConstructor() { WRAPPER_NO_CONTRACT; return IsMdClassConstructor(GetAttrs(), GetName()); } DWORD IsClassConstructorOrCtor() { WRAPPER_NO_CONTRACT; DWORD dwAttrs = GetAttrs(); if (IsMdRTSpecialName(dwAttrs)) { LPCUTF8 name = GetName(); return IsMdInstanceInitializer(dwAttrs, name) || IsMdClassConstructor(dwAttrs, name); } return FALSE; } inline void SetHasMethodImplSlot() { m_wFlags |= mdcMethodImpl; } inline BOOL HasMethodImplSlot() { LIMITED_METHOD_DAC_CONTRACT; return (mdcMethodImpl & m_wFlags); } FORCEINLINE BOOL IsMethodImpl() { LIMITED_METHOD_DAC_CONTRACT; // Once we stop allocating dummy MethodImplSlot in MethodTableBuilder::WriteMethodImplData, // the check for NULL will become unnecessary. return HasMethodImplSlot() && (GetMethodImpl()->GetSlots() != NULL); } inline DWORD IsStatic() { LIMITED_METHOD_DAC_CONTRACT; // This bit caches the IsMdStatic(GetAttrs()) check. We used to assert it here, but not doing it anymore. GetAttrs() // accesses metadata that is not compatible with contracts of this method. The metadata access can fail, the metadata // are not available during shutdown, the metadata access can take locks. It is not worth it to code around all these // just for the assert. // _ASSERTE((((m_wFlags & mdcStatic) != 0) == (IsMdStatic(flags) != 0))); return (m_wFlags & mdcStatic) != 0; } inline void SetStatic() { LIMITED_METHOD_CONTRACT; m_wFlags |= mdcStatic; } inline void ClearStatic() { LIMITED_METHOD_CONTRACT; m_wFlags &= ~mdcStatic; } inline BOOL IsIL() { LIMITED_METHOD_DAC_CONTRACT; return mcIL == GetClassification() || mcInstantiated == GetClassification(); } //================================================================ // Generics-related predicates etc. // True if the method descriptor is an instantiation of a generic method. inline BOOL HasMethodInstantiation() const; // True if the method descriptor is either an instantiation of // a generic method or is an instance method in an instantiated class (or both). BOOL HasClassOrMethodInstantiation() { LIMITED_METHOD_DAC_CONTRACT; return (HasClassInstantiation() || HasMethodInstantiation()); } BOOL HasClassOrMethodInstantiation_NoLogging() const { LIMITED_METHOD_DAC_CONTRACT; return (HasClassInstantiation_NoLogging() || HasMethodInstantiation()); } inline BOOL HasClassInstantiation() const { LIMITED_METHOD_DAC_CONTRACT; return GetMethodTable()->HasInstantiation(); } inline BOOL HasClassInstantiation_NoLogging() const { LIMITED_METHOD_DAC_CONTRACT; return GetMethodTable_NoLogging()->HasInstantiation(); } // Return the instantiation for an instantiated generic method // Return NULL if not an instantiated method // To get the (representative) instantiation of the declaring class use GetMethodTable()->GetInstantiation() // NOTE: This will assert if you try to get the instantiation of a generic method def in a non-typical class // e.g. C.m will fail but C.m will succeed Instantiation GetMethodInstantiation() const; // As above, but will succeed on C.m // To do this it might force a load of the typical parent Instantiation LoadMethodInstantiation(); // Return a pointer to the method dictionary for an instantiated generic method // The initial slots in a method dictionary are the type arguments themselves // Return NULL if not an instantiated method Dictionary* GetMethodDictionary(); DictionaryLayout* GetDictionaryLayout(); InstantiatedMethodDesc* AsInstantiatedMethodDesc() const; BaseDomain *GetDomain(); #ifdef FEATURE_CODE_VERSIONING CodeVersionManager* GetCodeVersionManager(); #endif #ifdef FEATURE_TIERED_COMPILATION CallCounter* GetCallCounter(); #endif PTR_LoaderAllocator GetLoaderAllocator(); // GetLoaderAllocatorForCode returns the allocator with the responsibility for allocation. // This is called from GetMulticallableAddrOfCode when allocating a small trampoline stub for the method. // Normally a method in a shared domain will allocate memory for stubs in the shared domain. // That has to be different for DynamicMethod as they need to be allocated always in the AppDomain // that created the method. LoaderAllocator * GetLoaderAllocatorForCode(); // GetDomainSpecificLoaderAllocator returns the collectable loader allocator for collectable types // and the loader allocator in the current domain for non-collectable types LoaderAllocator * GetDomainSpecificLoaderAllocator(); Module* GetLoaderModule(); Module* GetZapModule(); // Does this immediate item live in an NGEN module? BOOL IsZapped(); // Strip off method and class instantiation if present and replace by the typical instantiation // e.g. C.m -> C.m. Does not modify the MethodDesc, but returns // the appropriate stripped MethodDesc. // This is the identity function on non-instantiated method descs in non-instantiated classes MethodDesc* LoadTypicalMethodDefinition(); // Strip off the method instantiation (if present) and replace by the typical instantiation // e.g. // C.m -> C.m. Does not modify the MethodDesc, but returns // the appropriate stripped MethodDesc. // This is the identity function on non-instantiated method descs MethodDesc* StripMethodInstantiation(); // Return the instantiation of a method's enclosing class // Return NULL if the enclosing class is not instantiated // If the method code is shared then this might be a *representative* instantiation // // See GetExactClassInstantiation if you need to get the exact // instantiation of a shared method desc. Instantiation GetClassInstantiation() const; // Is the code shared between multiple instantiations of class or method? // If so, then when compiling the code we might need to look up tokens // in the class or method dictionary. Also, when debugging the exact generic arguments // need to be ripped off the stack, either from the this pointer or from one of the // extra args below. BOOL IsSharedByGenericInstantiations(); // shared code of any kind BOOL IsSharedByGenericMethodInstantiations(); // shared due to method instantiation // How does a method shared between generic instantiations get at // the extra instantiation information at runtime? Only one of the following three // will ever hold: // // AcquiresInstMethodTableFromThis() // The method is in a generic class but is not itself a // generic method (the normal case). Furthermore a "this" pointer // is available and we can get the exact instantiation from it. // // RequiresInstMethodTableArg() // The method is shared between generic classes but is not // itself generic. Furthermore no "this" pointer is given // (e.g. a value type method), so we pass in the exact-instantiation // method table as an extra argument. // i.e. per-inst static methods in shared-code instantiated generic // classes (e.g. static void MyClass::m()) // i.e. shared-code instance methods in instantiated generic // structs (e.g. void MyValueType::m()) // // RequiresInstMethodDescArg() // The method is itself generic and is shared between generic // instantiations but is not itself generic. Furthermore // no "this" pointer is given (e.g. a value type method), so we pass in the // exact-instantiation method table as an extra argument. // i.e. shared-code instantiated generic methods // // These are used for direct calls to instantiated generic methods // e.g. call void C::m() implemented by calculating dict(m) at compile-time and passing it as an extra parameter // call void C::m() implemented by calculating dict(m) at run-time (if the caller lives in shared-class code) BOOL AcquiresInstMethodTableFromThis(); BOOL RequiresInstMethodTableArg(); BOOL RequiresInstMethodDescArg(); BOOL RequiresInstArg(); // Can this method handle be given out to reflection for use in a MethodInfo // object? BOOL IsRuntimeMethodHandle(); // Given a method table of an object and a method that comes from some // superclass of the class of that object, find that superclass. MethodTable * GetExactDeclaringType(MethodTable * ownerOrSubType); // Given a type handle of an object and a method that comes from some // superclass of the class of that object, find the instantiation of // that superclass, i.e. the class instantiation which will be relevant // to interpreting the signature of the method. The type handle of // the object does not need to be given in all circumstances, in // particular it is only needed for MethodDescs pMD that // return true for pMD->RequiresInstMethodTableArg() or // pMD->RequiresInstMethodDescArg(). In other cases it is // allowed to be null. // // Will return NULL if the method is not in a generic class. Instantiation GetExactClassInstantiation(TypeHandle possibleObjType); BOOL SatisfiesMethodConstraints(TypeHandle thParent, BOOL fThrowIfNotSatisfied = FALSE); BOOL HasSameMethodDefAs(MethodDesc * pMD); //================================================================ // Classifications of kinds of MethodDescs. inline BOOL IsRuntimeSupplied() { LIMITED_METHOD_DAC_CONTRACT; return mcFCall == GetClassification() || mcArray == GetClassification(); } inline DWORD IsArray() const { LIMITED_METHOD_DAC_CONTRACT; return mcArray == GetClassification(); } inline DWORD IsEEImpl() const { LIMITED_METHOD_DAC_CONTRACT; return mcEEImpl == GetClassification(); } inline DWORD IsNoMetadata() const { LIMITED_METHOD_DAC_CONTRACT; return (mcDynamic == GetClassification()); } inline PTR_DynamicMethodDesc AsDynamicMethodDesc(); inline bool IsDynamicMethod(); inline bool IsILStub(); inline bool IsLCGMethod(); inline DWORD IsNDirect() { LIMITED_METHOD_DAC_CONTRACT; return mcNDirect == GetClassification(); } inline DWORD IsInterface() { WRAPPER_NO_CONTRACT; return GetMethodTable()->IsInterface(); } void ComputeSuppressUnmanagedCodeAccessAttr(IMDInternalImport *pImport); BOOL HasNativeCallableAttribute(); #ifdef FEATURE_COMINTEROP inline DWORD IsComPlusCall() { WRAPPER_NO_CONTRACT; return mcComInterop == GetClassification(); } inline DWORD IsGenericComPlusCall(); inline void SetupGenericComPlusCall(); #else // !FEATURE_COMINTEROP // hardcoded to return FALSE to improve code readibility inline DWORD IsComPlusCall() { LIMITED_METHOD_CONTRACT; return FALSE; } inline DWORD IsGenericComPlusCall() { LIMITED_METHOD_CONTRACT; return FALSE; } #endif // !FEATURE_COMINTEROP // Update flags in a thread safe manner. WORD InterlockedUpdateFlags(WORD wMask, BOOL fSet); // If the method is in an Edit and Contine (EnC) module, then // we DON'T want to backpatch this, ever. We MUST always call // through the precode so that we can update the method. inline DWORD IsEnCMethod() { WRAPPER_NO_CONTRACT; Module *pModule = GetModule(); PREFIX_ASSUME(pModule != NULL); return pModule->IsEditAndContinueEnabled(); } inline BOOL IsNotInline() { LIMITED_METHOD_CONTRACT; return (m_wFlags & mdcNotInline); } inline void SetNotInline(BOOL set) { WRAPPER_NO_CONTRACT; InterlockedUpdateFlags(mdcNotInline, set); } #ifndef DACCESS_COMPILE VOID EnsureActive(); #endif CHECK CheckActivated(); //================================================================ // FCalls. BOOL IsFCall() { WRAPPER_NO_CONTRACT; return mcFCall == GetClassification(); } BOOL IsFCallOrIntrinsic(); BOOL IsQCall(); //================================================================ // inline void ClearFlagsOnUpdate() { WRAPPER_NO_CONTRACT; SetNotInline(FALSE); } // Restore the MethodDesc to it's initial, pristine state, so that // it can be reused for new code (eg. for EnC, method rental, etc.) // // Things to think about before calling this: // // Does the caller need to free up the jitted code for the old IL // (including any other IJitManager datastructures) ? // Does the caller guarantee thread-safety ? // void Reset(); //================================================================ // About the signature. BOOL IsVarArg(); BOOL IsVoid(); BOOL HasRetBuffArg(); // Returns the # of bytes of stack used by arguments. Does not include // arguments passed in registers. UINT SizeOfArgStack(); // Returns the # of bytes of stack used by arguments in a call from native to this function. // Does not include arguments passed in registers. UINT SizeOfNativeArgStack(); // Returns the # of bytes to pop after a call. Not necessary the // same as SizeOfArgStack()! UINT CbStackPop(); //================================================================ // Unboxing stubs. // // Return TRUE if this is this a special stub used to implement delegates to an // instance method in a value class and/or virtual methods on a value class. // // For every BoxedEntryPointStub there is associated unboxed-this-MethodDesc // which accepts an unboxed "this" pointer. // // The action of a typical BoxedEntryPointStub is to // bump up the this pointer by one word so that it points to the interior of the object // and then call the underlying unboxed-this-MethodDesc. // // Additionally, if the non-BoxedEntryPointStub is RequiresInstMethodTableArg() // then pass on the MethodTable as an extra argument to the // underlying unboxed-this-MethodDesc. BOOL IsUnboxingStub() { LIMITED_METHOD_DAC_CONTRACT; return (m_bFlags2 & enum_flag2_IsUnboxingStub) != 0; } void SetIsUnboxingStub() { LIMITED_METHOD_CONTRACT; m_bFlags2 |= enum_flag2_IsUnboxingStub; } //================================================================ // Instantiating Stubs // // Return TRUE if this is this a special stub used to implement an // instantiated generic method or per-instantiation static method. // The action of an instantiating stub is // * pass on a MethodTable or InstantiatedMethodDesc extra argument to shared code BOOL IsInstantiatingStub(); // A wrapper stub is either an unboxing stub or an instantiating stub BOOL IsWrapperStub(); MethodDesc *GetWrappedMethodDesc(); MethodDesc *GetExistingWrappedMethodDesc(); //================================================================== // Access the underlying metadata BOOL HasILHeader() { CONTRACTL { NOTHROW; GC_NOTRIGGER; SO_TOLERANT; MODE_ANY; } CONTRACTL_END; return IsIL() && !IsUnboxingStub() && GetRVA(); } COR_ILMETHOD* GetILHeader(BOOL fAllowOverrides = FALSE); BOOL HasStoredSig() { LIMITED_METHOD_DAC_CONTRACT; return IsEEImpl() || IsArray() || IsNoMetadata(); } PCCOR_SIGNATURE GetSig(); void GetSig(PCCOR_SIGNATURE *ppSig, DWORD *pcSig); SigParser GetSigParser(); // Convenience methods for common signature wrapper types. SigPointer GetSigPointer(); Signature GetSignature(); void GetSigFromMetadata(IMDInternalImport * importer, PCCOR_SIGNATURE * ppSig, DWORD * pcSig); IMDInternalImport* GetMDImport() const { WRAPPER_NO_CONTRACT; Module *pModule = GetModule(); PREFIX_ASSUME(pModule != NULL); return pModule->GetMDImport(); } #ifndef DACCESS_COMPILE IMetaDataEmit* GetEmitter() { WRAPPER_NO_CONTRACT; Module *pModule = GetModule(); PREFIX_ASSUME(pModule != NULL); return pModule->GetEmitter(); } IMetaDataImport* GetRWImporter() { WRAPPER_NO_CONTRACT; Module *pModule = GetModule(); PREFIX_ASSUME(pModule != NULL); return pModule->GetRWImporter(); } #endif // !DACCESS_COMPILE #ifdef FEATURE_COMINTEROP WORD GetComSlot(); LONG GetComDispid(); #endif // FEATURE_COMINTEROP inline DWORD IsCtor() { WRAPPER_NO_CONTRACT; return IsMdInstanceInitializer(GetAttrs(), GetName()); } inline DWORD IsFinal() { WRAPPER_NO_CONTRACT; return IsMdFinal(GetAttrs()); } inline DWORD IsPrivate() { WRAPPER_NO_CONTRACT; return IsMdPrivate(GetAttrs()); } inline DWORD IsPublic() const { WRAPPER_NO_CONTRACT; return IsMdPublic(GetAttrs()); } inline DWORD IsProtected() const { WRAPPER_NO_CONTRACT; return IsMdFamily(GetAttrs()); } inline DWORD IsVirtual() { WRAPPER_NO_CONTRACT; return IsMdVirtual(GetAttrs()); } inline DWORD IsAbstract() { WRAPPER_NO_CONTRACT; return IsMdAbstract(GetAttrs()); } //================================================================== // Flags.. inline void SetSynchronized() { LIMITED_METHOD_CONTRACT; m_wFlags |= mdcSynchronized; } inline DWORD IsSynchronized() { LIMITED_METHOD_DAC_CONTRACT; return (m_wFlags & mdcSynchronized) != 0; } // Be careful about races with profiler when using this method. The profiler can // replace preimplemented code of the method with jitted code. // Avoid code patterns like if(IsPreImplemented()) { PCODE pCode = GetPreImplementedCode(); ... }. // Use PCODE pCode = GetPreImplementedCode(); if (pCode != NULL) { ... } instead. BOOL IsPreImplemented() { LIMITED_METHOD_DAC_CONTRACT; return GetPreImplementedCode() != NULL; } //================================================================== // The MethodDesc in relation to the VTable it is associated with. // WARNING: Not all MethodDescs have slots, nor do they all have // entries in MethodTables. Beware. // Does the method has virtual slot? Note that methods implementing interfaces // on value types do not have virtual slots, but they are marked as virtual in metadata. inline BOOL IsVtableMethod() { LIMITED_METHOD_CONTRACT; MethodTable *pMT = GetMethodTable(); g_IBCLogger.LogMethodTableAccess(pMT); return !IsEnCAddedMethod() // The slot numbers are currently meaningless for // some unboxed-this-generic-method-instantiations && !(pMT->IsValueType() && !IsStatic() && !IsUnboxingStub()) && GetSlot() < pMT->GetNumVirtuals(); } // Is this a default interface method (virtual non-abstract instance method) inline BOOL IsDefaultInterfaceMethod() { LIMITED_METHOD_CONTRACT; #ifdef FEATURE_DEFAULT_INTERFACES return (GetMethodTable()->IsInterface() && !IsStatic() && IsVirtual() && !IsAbstract()); #else return false; #endif // FEATURE_DEFAULT_INTERFACES } inline BOOL HasNonVtableSlot(); void SetHasNonVtableSlot() { LIMITED_METHOD_CONTRACT; m_wFlags |= mdcHasNonVtableSlot; } // duplicate methods inline BOOL IsDuplicate() { LIMITED_METHOD_CONTRACT; return (m_wFlags & mdcDuplicate) == mdcDuplicate; } void SetDuplicate() { LIMITED_METHOD_CONTRACT; // method table is not setup yet //_ASSERTE(!GetClass()->IsInterface()); m_wFlags |= mdcDuplicate; } //================================================================== // EnC inline BOOL IsEnCAddedMethod(); //================================================================== // inline EEClass* GetClass() { WRAPPER_NO_CONTRACT; MethodTable *pMT = GetMethodTable_NoLogging(); g_IBCLogger.LogEEClassAndMethodTableAccess(pMT); EEClass *pClass = pMT->GetClass_NoLogging(); PREFIX_ASSUME(pClass != NULL); return pClass; } inline PTR_MethodTable GetMethodTable() const; inline PTR_MethodTable GetMethodTable_NoLogging() const; inline DPTR(RelativeFixupPointer) GetMethodTablePtr() const; public: inline MethodDescChunk* GetMethodDescChunk() const; inline int GetMethodDescIndex() const; // If this is an method desc. (whether non-generic shared-instantiated or exact-instantiated) // inside a shared class then get the method table for the representative // class. inline MethodTable* GetCanonicalMethodTable(); Module *GetModule() const; Module *GetModule_NoLogging() const; Assembly *GetAssembly() const { WRAPPER_NO_CONTRACT; Module *pModule = GetModule(); PREFIX_ASSUME(pModule != NULL); return pModule->GetAssembly(); } //================================================================== // The slot number of this method in the corresponding method table. // // Use with extreme caution. The slot number will not be // valid for EnC code or for MethodDescs representing instantiation // of generic methods. It may also not mean what you think it will mean // for strange method descs such as BoxedEntryPointStubs. // // In any case we should be moving to use slot numbers a lot less // since they make the EE code inflexible. inline WORD GetSlot() { LIMITED_METHOD_DAC_CONTRACT; #ifndef DACCESS_COMPILE // The DAC build uses this method to test for "sanity" of a MethodDesc, and // doesn't need the assert. _ASSERTE(! IsEnCAddedMethod() || !"Cannot get slot for method added via EnC"); #endif // !DACCESS_COMPILE // Check if this MD is using the packed slot layout if (!RequiresFullSlotNumber()) { return (m_wSlotNumber & enum_packedSlotLayout_SlotMask); } return m_wSlotNumber; } inline VOID SetSlot(WORD wSlotNum) { LIMITED_METHOD_CONTRACT; // Check if we have to avoid using the packed slot layout if (wSlotNum > enum_packedSlotLayout_SlotMask) { SetRequiresFullSlotNumber(); } // Set only the portion of m_wSlotNumber we are using if (!RequiresFullSlotNumber()) { m_wSlotNumber &= ~enum_packedSlotLayout_SlotMask; m_wSlotNumber |= wSlotNum; } else { m_wSlotNumber = wSlotNum; } } inline BOOL IsVirtualSlot() { return GetSlot() < GetMethodTable()->GetNumVirtuals(); } inline BOOL IsVtableSlot() { return IsVirtualSlot() && !HasNonVtableSlot(); } TADDR GetAddrOfSlot(); PTR_MethodDesc GetDeclMethodDesc(UINT32 slotNumber); protected: inline void SetRequiresFullSlotNumber() { LIMITED_METHOD_CONTRACT; m_wFlags |= mdcRequiresFullSlotNumber; } inline DWORD RequiresFullSlotNumber() { LIMITED_METHOD_DAC_CONTRACT; return (m_wFlags & mdcRequiresFullSlotNumber) != 0; } public: mdMethodDef GetMemberDef() const; mdMethodDef GetMemberDef_NoLogging() const; #ifdef _DEBUG BOOL SanityCheck(); #endif // _DEBUG public: void SetMemberDef(mdMethodDef mb); //================================================================ // Set the offset of this method desc in a chunk table (which allows us // to work back to the method table/module pointer stored at the head of // the table. void SetChunkIndex(MethodDescChunk *pChunk); BOOL IsPointingToPrestub(); public: // TRUE iff it is possible to change the code this method will run using // the CodeVersionManager. // Note: EnC currently returns FALSE here because it uses its own seperate // scheme to manage versionability. We will likely want to converge them // at some point. BOOL IsVersionable() { #ifndef FEATURE_CODE_VERSIONING return FALSE; #else return IsVersionableWithPrecode() || IsVersionableWithJumpStamp(); #endif } // If true, these methods version using the CodeVersionManager and // switch between different code versions by updating the target of the precode. // Note: EnC returns FALSE - even though it uses precode updates it does not // use the CodeVersionManager right now BOOL IsVersionableWithPrecode() { #ifdef FEATURE_CODE_VERSIONING return // policy: which things do we want to version with a precode if possible IsEligibleForTieredCompilation() && // functional requirements: !IsZapped() && // NGEN directly invokes the pre-generated native code. // without necessarily going through the prestub or // precode HasNativeCodeSlot(); // the stable entry point will need to point at our // precode and not directly contain the native code. #else return FALSE; #endif } // If true, these methods version using the CodeVersionManager and switch between // different code versions by overwriting the first bytes of the method's initial // native code with a jmp instruction. BOOL IsVersionableWithJumpStamp() { #if defined(FEATURE_CODE_VERSIONING) && defined(FEATURE_JUMPSTAMP) return // for native image code this is policy, but for jitted code it is a functional requirement // to ensure the prolog is sufficiently large ReJitManager::IsReJITEnabled() && // functional requirement - the runtime doesn't expect both options to be possible !IsVersionableWithPrecode() && // functional requirement - we must be able to evacuate the prolog and the prolog must be big // enough, both of which are only designed to work on jitted code (IsIL() || IsNoMetadata()) && !IsUnboxingStub() && !IsInstantiatingStub() && // functional requirement - code version manager can't handle what would happen if the code // was collected !GetLoaderAllocator()->IsCollectible(); #else return FALSE; #endif } #ifdef FEATURE_TIERED_COMPILATION // Is this method allowed to be recompiled and the entrypoint redirected so that we // can optimize its performance? Eligibility is invariant for the lifetime of a method. BOOL IsEligibleForTieredCompilation() { LIMITED_METHOD_DAC_CONTRACT; // Keep in-sync with MethodTableBuilder::NeedsNativeCodeSlot(bmtMDMethod * pMDMethod) // to ensure native slots are available where needed. return g_pConfig->TieredCompilation() && !IsZapped() && !IsEnCMethod() && HasNativeCodeSlot() && !IsUnboxingStub() && !IsInstantiatingStub() && !IsDynamicMethod() && !GetLoaderAllocator()->IsCollectible() && !CORDisableJITOptimizations(GetModule()->GetDebuggerInfoBits()) && !CORProfilerDisableTieredCompilation(); } #endif bool RequestedAggressiveOptimization() { WRAPPER_NO_CONTRACT; return IsIL() && // only makes sense for IL methods, and this implies !IsNoMetadata() IsMiAggressiveOptimization(GetImplAttrs()); } // Does this method force the NativeCodeSlot to stay fixed after it // is first initialized to native code? Consumers of the native code // pointer need to be very careful about if and when they cache it // if it is not stable. // // The stability of the native code pointer is separate from the // stability of the entrypoint. A stable entrypoint can be a precode // which dispatches to an unstable native code pointer. BOOL IsNativeCodeStableAfterInit() { LIMITED_METHOD_DAC_CONTRACT; #if defined(FEATURE_JIT_PITCHING) if (IsPitchable()) return false; #endif return #ifdef FEATURE_TIERED_COMPILATION !IsEligibleForTieredCompilation() && #endif !IsEnCMethod(); } //Is this method currently pointing to native code that will never change? BOOL IsPointingToStableNativeCode() { LIMITED_METHOD_DAC_CONTRACT; if (!IsNativeCodeStableAfterInit()) return FALSE; return IsPointingToNativeCode(); } // Note: We are skipping the prestub based on addition information from the JIT. // (e.g. that the call is on same this ptr or that the this ptr is not null). // Thus we can end up with a running NGENed method for which IsPointingToNativeCode is false! BOOL IsPointingToNativeCode() { LIMITED_METHOD_DAC_CONTRACT; if (!HasStableEntryPoint()) return FALSE; if (!HasPrecode()) return TRUE; return GetPrecode()->IsPointingToNativeCode(GetNativeCode()); } // Be careful about races with profiler when using this method. The profiler can // replace preimplemented code of the method with jitted code. // Avoid code patterns like if(HasNativeCode()) { PCODE pCode = GetNativeCode(); ... }. // Use PCODE pCode = GetNativeCode(); if (pCode != NULL) { ... } instead. BOOL HasNativeCode() { LIMITED_METHOD_DAC_CONTRACT; return GetNativeCode() != NULL; } BOOL SetNativeCodeInterlocked(PCODE addr, PCODE pExpected = NULL); TADDR GetAddrOfNativeCodeSlot(); BOOL MayHaveNativeCode(); ULONG GetRVA(); public: // Returns preimplemented code of the method if method has one. // Returns NULL if method has no preimplemented code. // Be careful about races with profiler when using this method. The profiler can // replace preimplemented code of the method with jitted code. PCODE GetPreImplementedCode(); // Returns address of code to call. The address is good for one immediate invocation only. // Use GetMultiCallableAddrOfCode() to get address that can be invoked multiple times. // // Only call GetSingleCallableAddrOfCode() if you can guarantee that no virtualization is // necessary, or if you can guarantee that it has already happened. For instance, the frame of a // stackwalk has obviously been virtualized as much as it will be. // PCODE GetSingleCallableAddrOfCode() { WRAPPER_NO_CONTRACT; _ASSERTE(!IsGenericMethodDefinition()); return GetMethodEntryPoint(); } // This one is used to implement "ldftn". PCODE GetMultiCallableAddrOfCode(CORINFO_ACCESS_FLAGS accessFlags = CORINFO_ACCESS_LDFTN); // Internal version of GetMultiCallableAddrOfCode. Returns NULL if attempt to acquire directly // callable entrypoint would result into unnecesary allocation of indirection stub. Caller should use // indirect call via slot in this case. PCODE TryGetMultiCallableAddrOfCode(CORINFO_ACCESS_FLAGS accessFlags); // These return an address after resolving "virtual methods" correctly, including any // handling of context proxies, other thunking layers and also including // instantiation of generic virtual methods if required. // The first one returns an address which cannot be invoked // multiple times. Use GetMultiCallableAddrOfVirtualizedCode() for that. // // The code that implements these was taken verbatim from elsewhere in the // codebase, and there may be subtle differences between the two, e.g. with // regard to thunking. PCODE GetSingleCallableAddrOfVirtualizedCode(OBJECTREF *orThis, TypeHandle staticTH); PCODE GetMultiCallableAddrOfVirtualizedCode(OBJECTREF *orThis, TypeHandle staticTH); // The current method entrypoint. It is simply the value of the current method slot. // GetMethodEntryPoint() should be used to get an opaque method entrypoint, for instance // when copying or searching vtables. It should not be used to get address to call. // // GetSingleCallableAddrOfCode() and GetStableEntryPoint() are aliases with stricter preconditions. // Use of these aliases is as appropriate. // PCODE GetMethodEntryPoint(); //******************************************************************************* // Returns the address of the native code. The native code can be one of: // - jitted code if !IsPreImplemented() // - ngened code if IsPreImplemented() PCODE GetNativeCode(); #if defined(FEATURE_JIT_PITCHING) bool IsPitchable(); void PitchNativeCode(); #endif //================================================================ // FindOrCreateAssociatedMethodDesc // // You might think that every MethodDef in the metadata had // one and only one MethodDesc in the source... Well, how wrong // you are :-) // // Some MethodDefs can be associated with more than one MethodDesc. // This can happen because: // (1) The method is an instance method in a struct, which // can be called with either an unboxed "this" pointer or // a "boxed" this pointer.. There is a different MethodDesc for // these two cases. // (2) The method is a generic method. There is one primary // MethodDesc for each generic method, called the GenericMethodDefinition. // This is the one stored in the vtable. New MethodDescs will // be created for instantiations according to the scheme described // elsewhere in this file. // There are also various other stubs associated with MethodDesc, but these stubs // do not result in new MethodDescs. // // All of the above MethodDescs are called "associates" of the primary MethodDesc. // Note that the primary MethodDesc for an instance method on a struct is // the one that accepts an unboxed "this" pointer. // // FindOrCreateAssociatedMethodDesc is the _primary_ routine // in the codebase for getting an associated MethodDesc from a primary MethodDesc. // You should treat this routine as a black box, i.e. just specify the right // parameters and it will do all the hard work of finding the right // MethodDesc for you. // // This routine can be used for "normal" MethodDescs that have nothing // to do with generics. For example, if you need an BoxedEntryPointStub then // you may call this routine to get it. It may also return // the Primary MethodDesc itself if that MethodDesc is suitable given the // parameters. // // NOTE: The behaviour of this method is not thoroughly defined // if pPrimaryMD is not really a "primary" MD. Primary MDs are: // 1. Primary MDs are:never a generic method instantiation, // but are instead the "uninstantiated" generic MD. // 2. Primary MDs are never instantiating stubs. // 3. Primary MDs are never BoxedEntryPointStubs. // // We assert if cases (1) or (2) occur. However, some places in the // code pass in an BoxedEntryPointStub when pPrimaryMD is a virtual/interface method on // a struct. These cases are confusing and should be rooted // out: it is probably preferable in terms // of correctness to pass in the the corresponding non-unboxing MD. // // allowCreate may be set to FALSE to enforce that the method searched // should already be in existence - thus preventing creation and GCs during // inappropriate times. // static MethodDesc* FindOrCreateAssociatedMethodDesc(MethodDesc* pPrimaryMD, MethodTable *pExactMT, BOOL forceBoxedEntryPoint, Instantiation methodInst, BOOL allowInstParam, BOOL forceRemotableMethod = FALSE, BOOL allowCreate = TRUE, ClassLoadLevel level = CLASS_LOADED); // Normalize methoddesc for reflection static MethodDesc* FindOrCreateAssociatedMethodDescForReflection(MethodDesc *pMethod, TypeHandle instType, Instantiation methodInst); // True if a MD is an funny BoxedEntryPointStub (not from the method table) or // an MD for a generic instantiation...In other words the MethodDescs and the // MethodTable are guaranteed to be "tightly-knit", i.e. if one is present in // an NGEN image then then other will be, and if one is "used" at runtime then // the other will be too. BOOL IsTightlyBoundToMethodTable(); // For method descriptors which are non-generic this is the identity function // (except it returns the primary descriptor, not an BoxedEntryPointStub). // // For a generic method definition C.m this will return // C<__Canon>.m<__Canon> // // allowCreate may be set to FALSE to enforce that the method searched // should already be in existence - thus preventing creation and GCs during // inappropriate times. // MethodDesc * FindOrCreateTypicalSharedInstantiation(BOOL allowCreate = TRUE); // Given an object and an method descriptor for an instantiation of // a virtualized generic method, get the // corresponding instantiation of the target of a call. MethodDesc *ResolveGenericVirtualMethod(OBJECTREF *orThis); public: // does this function return an object reference? MetaSig::RETURNTYPE ReturnsObject( #ifdef _DEBUG bool supportStringConstructors = false, #endif MethodTable** pMT = NULL ); void Destruct(); public: // In general you don't want to call GetCallTarget - you want to // use either "call" directly or call MethodDesc::GetSingleCallableAddrOfVirtualizedCode and // then "CallTarget". Note that GetCallTarget is approximately GetSingleCallableAddrOfCode // but the additional wierdness that class-based-virtual calls (but not interface calls nor calls // on proxies) are resolved to their target. Because of this, many clients of "Call" (see above) // end up doing some resolution for interface calls and/or proxies themselves. PCODE GetCallTarget(OBJECTREF* pThisObj, TypeHandle ownerType = TypeHandle()); MethodImpl *GetMethodImpl(); #if defined(FEATURE_PREJIT ) && !defined(DACCESS_COMPILE) //================================================================ // Precompilation (NGEN) void Save(DataImage *image); void Fixup(DataImage *image); void FixupSlot(DataImage *image, PVOID p, SSIZE_T offset, ZapRelocationType type = IMAGE_REL_BASED_PTR); // // Helper class used to regroup MethodDesc chunks before saving them into NGen image. // The regrouping takes into account IBC data and optional NGen-specific MethodDesc members. // class SaveChunk { DataImage * m_pImage; ZapStoredStructure * m_pFirstNode; MethodDescChunk * m_pLastChunk; typedef enum _MethodPriorityEnum { NoFlags = -1, HotMethodDesc = 0x0, WriteableMethodDesc = 0x1, ColdMethodDesc = 0x2, ColdWriteableMethodDesc= ColdMethodDesc | WriteableMethodDesc } MethodPriorityEnum; struct MethodInfo { MethodDesc * m_pMD; //MethodPriorityEnum BYTE m_priority; BOOL m_fHasPrecode:1; BOOL m_fHasNativeCodeSlot:1; BOOL m_fHasFixupList:1; }; InlineSArray m_methodInfos; static int __cdecl MethodInfoCmp(const void* a_, const void* b_); SIZE_T GetSavedMethodDescSize(MethodInfo * pMethodInfo); void SaveOneChunk(COUNT_T start, COUNT_T count, ULONG size, DWORD priority); public: SaveChunk(DataImage * image) : m_pImage(image), m_pFirstNode(NULL), m_pLastChunk(NULL) { LIMITED_METHOD_CONTRACT; } void Append(MethodDesc * pMD); ZapStoredStructure * Save(); }; bool CanSkipDoPrestub(MethodDesc * callerMD, CorInfoIndirectCallReason *pReason, CORINFO_ACCESS_FLAGS accessFlags = CORINFO_ACCESS_ANY); // This is different from !IsRestored() in that it checks if restoring // will ever be needed for this ngened data-structure. // This is to be used at ngen time of a dependent module to determine // if it can be accessed directly, or if the restoring mechanism needs // to be hooked in. BOOL NeedsRestore(DataImage *image, BOOL fAssumeMethodTableRestored = FALSE) { WRAPPER_NO_CONTRACT; return ComputeNeedsRestore(image, NULL, fAssumeMethodTableRestored); } BOOL ComputeNeedsRestore(DataImage *image, TypeHandleList *pVisited, BOOL fAssumeMethodTableRestored = FALSE); // // After the zapper compiles all code in a module it may attempt // to populate entries in all dictionaries // associated with instantiations of generic methods. This is an optional step - nothing will // go wrong at runtime except we may get more one-off calls to JIT_GenericHandle. // Although these are one-off we prefer to avoid them since they touch metadata // pages. // // Fully populating a dictionary may in theory load more types, methods etc. However // for the moment only those entries that refer to things that // are already loaded will be filled in. void PrepopulateDictionary(DataImage * image, BOOL nonExpansive); #endif // FEATURE_PREJIT && !DACCESS_COMPILE TADDR GetFixupList(); BOOL IsRestored_NoLogging(); BOOL IsRestored(); void CheckRestore(ClassLoadLevel level = CLASS_LOADED); //================================================================ // Running the Prestub preparation step. // The stub produced by prestub requires method desc to be passed // in dedicated register. Used to implement stubs shared between // MethodDescs (e.g. PInvoke stubs) BOOL RequiresMethodDescCallingConvention(BOOL fEstimateForChunk = FALSE); // Returns true if the method has to have stable entrypoint always. BOOL RequiresStableEntryPoint(BOOL fEstimateForChunk = FALSE); // // Backpatch method slots // // Arguments: // pMT - cached value of code:MethodDesc::GetMethodTable() // pDispatchingMT - method table of the object that the method is being dispatched on, can be NULL. // fFullBackPatch - indicates whether to patch all possible slots, including the ones // expensive to patch // // Return value: // stable entry point (code:MethodDesc::GetStableEntryPoint()) // PCODE DoBackpatch(MethodTable * pMT, MethodTable * pDispatchingMT, BOOL fFullBackPatch); PCODE DoPrestub(MethodTable *pDispatchingMT); VOID GetMethodInfo(SString &namespaceOrClassName, SString &methodName, SString &methodSignature); VOID GetMethodInfoWithNewSig(SString &namespaceOrClassName, SString &methodName, SString &methodSignature); VOID GetMethodInfoNoSig(SString &namespaceOrClassName, SString &methodName); VOID GetFullMethodInfo(SString& fullMethodSigName); BOOL HasTypeEquivalentStructParameters(); typedef void (*WalkValueTypeParameterFnPtr)(Module *pModule, mdToken token, Module *pDefModule, mdToken tkDefToken, const SigParser *ptr, SigTypeContext *pTypeContext, void *pData); void WalkValueTypeParameters(MethodTable *pMT, WalkValueTypeParameterFnPtr function, void *pData); void PrepareForUseAsADependencyOfANativeImage() { WRAPPER_NO_CONTRACT; if (!IsZapped() && !HaveValueTypeParametersBeenWalked()) PrepareForUseAsADependencyOfANativeImageWorker(); } void PrepareForUseAsADependencyOfANativeImageWorker(); //================================================================ // The actual data stored in a MethodDesc follows. protected: enum { // There are flags available for use here (currently 5 flags bits are available); however, new bits are hard to come by, so any new flags bits should // have a fairly strong justification for existence. enum_flag3_TokenRemainderMask = 0x3FFF, // This must equal METHOD_TOKEN_REMAINDER_MASK calculated higher in this file // These are seperate to allow the flags space available and used to be obvious here // and for the logic that splits the token to be algorithmically generated based on the // #define enum_flag3_HasForwardedValuetypeParameter = 0x4000, // Indicates that a type-forwarded type is used as a valuetype parameter (this flag is only valid for ngenned items) enum_flag3_ValueTypeParametersWalked = 0x4000, // Indicates that all typeref's in the signature of the method have been resolved to typedefs (or that process failed) (this flag is only valid for non-ngenned methods) enum_flag3_DoesNotHaveEquivalentValuetypeParameters = 0x8000, // Indicates that we have verified that there are no equivalent valuetype parameters for this method }; UINT16 m_wFlags3AndTokenRemainder; BYTE m_chunkIndex; enum { // enum_flag2_HasPrecode implies that enum_flag2_HasStableEntryPoint is set. enum_flag2_HasStableEntryPoint = 0x01, // The method entrypoint is stable (either precode or actual code) enum_flag2_HasPrecode = 0x02, // Precode has been allocated for this method enum_flag2_IsUnboxingStub = 0x04, enum_flag2_HasNativeCodeSlot = 0x08, // Has slot for native code enum_flag2_IsJitIntrinsic = 0x10, // Jit may expand method as an intrinsic // unused = 0x20, // unused = 0x40, // unused = 0x80, }; BYTE m_bFlags2; // The slot number of this MethodDesc in the vtable array. // Note that we may store other information in the high bits if available -- // see enum_packedSlotLayout and mdcRequiresFullSlotNumber for details. WORD m_wSlotNumber; enum { enum_packedSlotLayout_SlotMask = 0x03FF, enum_packedSlotLayout_NameHashMask = 0xFC00 }; WORD m_wFlags; public: #ifdef DACCESS_COMPILE void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); #endif public: inline DWORD GetClassification() const { LIMITED_METHOD_DAC_CONTRACT; return (m_wFlags & mdcClassification); } inline void SetClassification(DWORD classification) { LIMITED_METHOD_CONTRACT; _ASSERTE((m_wFlags & mdcClassification) == 0); m_wFlags |= classification; } inline BOOL HasNativeCodeSlot() { LIMITED_METHOD_DAC_CONTRACT; return (m_bFlags2 & enum_flag2_HasNativeCodeSlot) != 0; } inline void SetHasNativeCodeSlot() { LIMITED_METHOD_CONTRACT; m_bFlags2 |= enum_flag2_HasNativeCodeSlot; } inline BOOL IsJitIntrinsic() { LIMITED_METHOD_DAC_CONTRACT; return (m_bFlags2 & enum_flag2_IsJitIntrinsic) != 0; } inline void SetIsJitIntrinsic() { LIMITED_METHOD_CONTRACT; m_bFlags2 |= enum_flag2_IsJitIntrinsic; } static const SIZE_T s_ClassificationSizeTable[]; static SIZE_T GetBaseSize(DWORD classification) { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(classification < mdcClassificationCount); return s_ClassificationSizeTable[classification]; } SIZE_T GetBaseSize() { LIMITED_METHOD_DAC_CONTRACT; return GetBaseSize(GetClassification()); } SIZE_T SizeOf(); WORD InterlockedUpdateFlags3(WORD wMask, BOOL fSet); #ifdef FEATURE_TYPEEQUIVALENCE inline BOOL DoesNotHaveEquivalentValuetypeParameters() { LIMITED_METHOD_DAC_CONTRACT; return (m_wFlags3AndTokenRemainder & enum_flag3_DoesNotHaveEquivalentValuetypeParameters) != 0; } inline void SetDoesNotHaveEquivalentValuetypeParameters() { LIMITED_METHOD_CONTRACT; InterlockedUpdateFlags3(enum_flag3_DoesNotHaveEquivalentValuetypeParameters, TRUE); } #endif // FEATURE_TYPEEQUIVALENCE inline BOOL HasForwardedValuetypeParameter() { LIMITED_METHOD_DAC_CONTRACT; // This should only be asked of Zapped MethodDescs _ASSERTE(IsZapped()); return (m_wFlags3AndTokenRemainder & enum_flag3_HasForwardedValuetypeParameter) != 0; } inline void SetHasForwardedValuetypeParameter() { LIMITED_METHOD_CONTRACT; InterlockedUpdateFlags3(enum_flag3_HasForwardedValuetypeParameter, TRUE); } inline BOOL HaveValueTypeParametersBeenWalked() { LIMITED_METHOD_DAC_CONTRACT; // This should only be asked of non-Zapped MethodDescs, and only during execution (not compilation) _ASSERTE(!IsZapped() && !IsCompilationProcess()); return (m_wFlags3AndTokenRemainder & enum_flag3_ValueTypeParametersWalked) != 0; } inline void SetValueTypeParametersWalked() { LIMITED_METHOD_CONTRACT; _ASSERTE(!IsZapped() && !IsCompilationProcess()); InterlockedUpdateFlags3(enum_flag3_ValueTypeParametersWalked, TRUE); } // // Optional MethodDesc slots appear after the end of base MethodDesc in this order: // // class MethodImpl; // Present if HasMethodImplSlot() is true typedef RelativePointer NonVtableSlot; // Present if HasNonVtableSlot() is true // RelativePointer for NGen, PCODE for JIT #define FIXUP_LIST_MASK 1 typedef RelativePointer NativeCodeSlot; // Present if HasNativeCodeSlot() is true // lower order bit (FIXUP_LIST_MASK) used to determine if FixupListSlot is present typedef RelativePointer FixupListSlot; // Stub Dispatch code public: MethodDesc *GetInterfaceMD(); // StubMethodInfo for use in creating RuntimeMethodHandles REFLECTMETHODREF GetStubMethodInfo(); PrecodeType GetPrecodeType(); // --------------------------------------------------------------------------------- // IL based Code generation pipeline // --------------------------------------------------------------------------------- #ifndef DACCESS_COMPILE public: PCODE PrepareInitialCode(); PCODE PrepareCode(NativeCodeVersion codeVersion); PCODE PrepareCode(PrepareCodeConfig* pConfig); private: PCODE PrepareILBasedCode(PrepareCodeConfig* pConfig); PCODE GetPrecompiledCode(PrepareCodeConfig* pConfig); PCODE GetPrecompiledNgenCode(PrepareCodeConfig* pConfig); PCODE GetPrecompiledR2RCode(PrepareCodeConfig* pConfig); PCODE GetMulticoreJitCode(); COR_ILMETHOD_DECODER* GetAndVerifyILHeader(PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pIlDecoderMemory); COR_ILMETHOD_DECODER* GetAndVerifyMetadataILHeader(PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pIlDecoderMemory); COR_ILMETHOD_DECODER* GetAndVerifyNoMetadataILHeader(); PCODE JitCompileCode(PrepareCodeConfig* pConfig); PCODE JitCompileCodeLockedEventWrapper(PrepareCodeConfig* pConfig, JitListLockEntry* pEntry); PCODE JitCompileCodeLocked(PrepareCodeConfig* pConfig, JitListLockEntry* pLockEntry, ULONG* pSizeOfCode, CORJIT_FLAGS* pFlags); #endif // DACCESS_COMPILE #ifdef HAVE_GCCOVER private: static CrstStatic m_GCCoverCrst; public: static void Init(); #endif }; #ifndef DACCESS_COMPILE class PrepareCodeConfig { public: PrepareCodeConfig(); PrepareCodeConfig(NativeCodeVersion nativeCodeVersion, BOOL needsMulticoreJitNotification, BOOL mayUsePrecompiledCode); MethodDesc* GetMethodDesc(); NativeCodeVersion GetCodeVersion(); BOOL NeedsMulticoreJitNotification(); BOOL MayUsePrecompiledCode(); virtual PCODE IsJitCancellationRequested(); virtual BOOL SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse); virtual COR_ILMETHOD* GetILHeader(); virtual CORJIT_FLAGS GetJitCompilationFlags(); BOOL ProfilerRejectedPrecompiledCode(); BOOL ReadyToRunRejectedPrecompiledCode(); void SetProfilerRejectedPrecompiledCode(); void SetReadyToRunRejectedPrecompiledCode(); protected: MethodDesc* m_pMethodDesc; NativeCodeVersion m_nativeCodeVersion; BOOL m_needsMulticoreJitNotification; BOOL m_mayUsePrecompiledCode; BOOL m_ProfilerRejectedPrecompiledCode; BOOL m_ReadyToRunRejectedPrecompiledCode; }; #ifdef FEATURE_CODE_VERSIONING class VersionedPrepareCodeConfig : public PrepareCodeConfig { public: VersionedPrepareCodeConfig(); VersionedPrepareCodeConfig(NativeCodeVersion codeVersion); HRESULT FinishConfiguration(); virtual PCODE IsJitCancellationRequested(); virtual BOOL SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse); virtual COR_ILMETHOD* GetILHeader(); virtual CORJIT_FLAGS GetJitCompilationFlags(); private: ILCodeVersion m_ilCodeVersion; }; #endif // FEATURE_CODE_VERSIONING #endif // DACCESS_COMPILE /******************************************************************/ // A code:MethodDescChunk is a container that holds one or more code:MethodDesc. Logically it is just // compression. Basically fields that are common among methods descs in the chunk are stored in the chunk // and the MethodDescs themselves just store and index that allows them to find their Chunk. Semantically // a code:MethodDescChunk is just a set of code:MethodDesc. class MethodDescChunk { friend class MethodDesc; friend class CheckAsmOffsets; #if defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE) friend class MethodDesc::SaveChunk; #endif #ifdef DACCESS_COMPILE friend class NativeImageDumper; #endif // DACCESS_COMPILE enum { enum_flag_TokenRangeMask = 0x03FF, // This must equal METHOD_TOKEN_RANGE_MASK calculated higher in this file // These are seperate to allow the flags space available and used to be obvious here // and for the logic that splits the token to be algorithmically generated based on the // #define enum_flag_HasCompactEntrypoints = 0x4000, // Compact temporary entry points enum_flag_IsZapped = 0x8000, // This chunk lives in NGen module }; public: // // Allocates methodDescCount identical MethodDescs in smallest possible number of chunks. // If methodDescCount is zero, one chunk with maximum number of MethodDescs is allocated. // static MethodDescChunk *CreateChunk(LoaderHeap *pHeap, DWORD methodDescCount, DWORD classification, BOOL fNonVtableSlot, BOOL fNativeCodeSlot, BOOL fComPlusCallInfo, MethodTable *initialMT, class AllocMemTracker *pamTracker); BOOL HasTemporaryEntryPoints() { LIMITED_METHOD_CONTRACT; return !IsZapped(); } TADDR GetTemporaryEntryPoints() { LIMITED_METHOD_CONTRACT; _ASSERTE(HasTemporaryEntryPoints()); return *(dac_cast(this) - 1); } PCODE GetTemporaryEntryPoint(int index); void EnsureTemporaryEntryPointsCreated(LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker) { CONTRACTL { THROWS; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; if (GetTemporaryEntryPoints() == NULL) CreateTemporaryEntryPoints(pLoaderAllocator, pamTracker); } void CreateTemporaryEntryPoints(LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker); #ifdef HAS_COMPACT_ENTRYPOINTS // // There two implementation options for temporary entrypoints: // // (1) Compact entrypoints. They provide as dense entrypoints as possible, but can't be patched // to point to the final code. The call to unjitted method is indirect call via slot. // // (2) Precodes. The precode will be patched to point to the final code eventually, thus // the temporary entrypoint can be embedded in the code. The call to unjitted method is // direct call to direct jump. // // We use (1) for x86 and (2) for 64-bit to get the best performance on each platform. // For ARM (1) is used. TADDR AllocateCompactEntryPoints(LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker); static MethodDesc* GetMethodDescFromCompactEntryPoint(PCODE addr, BOOL fSpeculative = FALSE); static SIZE_T SizeOfCompactEntryPoints(int count); static BOOL IsCompactEntryPointAtAddress(PCODE addr); #ifdef _TARGET_ARM_ static int GetCompactEntryPointMaxCount (); #endif // _TARGET_ARM_ #endif // HAS_COMPACT_ENTRYPOINTS FORCEINLINE PTR_MethodTable GetMethodTable() { LIMITED_METHOD_DAC_CONTRACT; return m_methodTable.GetValue(PTR_HOST_MEMBER_TADDR(MethodDescChunk, this, m_methodTable)); } inline DPTR(RelativeFixupPointer) GetMethodTablePtr() const { LIMITED_METHOD_DAC_CONTRACT; return dac_cast)>(PTR_HOST_MEMBER_TADDR(MethodDescChunk, this, m_methodTable)); } #ifndef DACCESS_COMPILE inline void SetMethodTable(MethodTable * pMT) { LIMITED_METHOD_CONTRACT; _ASSERTE(m_methodTable.IsNull()); _ASSERTE(pMT != NULL); m_methodTable.SetValue(pMT); } inline void SetSizeAndCount(ULONG sizeOfMethodDescs, COUNT_T methodDescCount) { LIMITED_METHOD_CONTRACT; _ASSERTE(FitsIn((sizeOfMethodDescs / MethodDesc::ALIGNMENT) - 1)); m_size = static_cast((sizeOfMethodDescs / MethodDesc::ALIGNMENT) - 1); _ASSERTE(SizeOf() == sizeof(MethodDescChunk) + sizeOfMethodDescs); _ASSERTE(FitsIn(methodDescCount - 1)); m_count = static_cast(methodDescCount - 1); _ASSERTE(GetCount() == methodDescCount); } #endif // !DACCESS_COMPILE #ifdef FEATURE_PREJIT #ifndef DACCESS_COMPILE inline void RestoreMTPointer(ClassLoadLevel level = CLASS_LOADED) { LIMITED_METHOD_CONTRACT; Module::RestoreMethodTablePointer(&m_methodTable, NULL, level); } #endif // !DACCESS_COMPILE #endif // FEATURE_PREJIT #ifndef DACCESS_COMPILE void SetNextChunk(MethodDescChunk *chunk) { LIMITED_METHOD_CONTRACT; m_next.SetValueMaybeNull(chunk); } #endif // !DACCESS_COMPILE PTR_MethodDescChunk GetNextChunk() { LIMITED_METHOD_CONTRACT; return m_next.GetValueMaybeNull(PTR_HOST_MEMBER_TADDR(MethodDescChunk, this, m_next)); } UINT32 GetCount() { LIMITED_METHOD_DAC_CONTRACT; return m_count + 1; } BOOL IsZapped() { LIMITED_METHOD_DAC_CONTRACT; #ifdef FEATURE_PREJIT return (m_flagsAndTokenRange & enum_flag_IsZapped) != 0; #else return FALSE; #endif } inline BOOL HasCompactEntryPoints() { LIMITED_METHOD_DAC_CONTRACT; #ifdef HAS_COMPACT_ENTRYPOINTS return (m_flagsAndTokenRange & enum_flag_HasCompactEntrypoints) != 0; #else return FALSE; #endif } inline UINT16 GetTokRange() { LIMITED_METHOD_DAC_CONTRACT; return m_flagsAndTokenRange & enum_flag_TokenRangeMask; } inline SIZE_T SizeOf() { LIMITED_METHOD_DAC_CONTRACT; return sizeof(MethodDescChunk) + (m_size + 1) * MethodDesc::ALIGNMENT; } inline MethodDesc *GetFirstMethodDesc() { LIMITED_METHOD_DAC_CONTRACT; return PTR_MethodDesc(dac_cast(this) + sizeof(MethodDescChunk)); } // Maximum size of one chunk (corresponts to the maximum of m_size = 0xFF) static const SIZE_T MaxSizeOfMethodDescs = 0x100 * MethodDesc::ALIGNMENT; #ifdef DACCESS_COMPILE void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); #endif private: void SetIsZapped() { LIMITED_METHOD_CONTRACT; m_flagsAndTokenRange |= enum_flag_IsZapped; } void SetHasCompactEntryPoints() { LIMITED_METHOD_CONTRACT; m_flagsAndTokenRange |= enum_flag_HasCompactEntrypoints; } void SetTokenRange(UINT16 tokenRange) { LIMITED_METHOD_CONTRACT; _ASSERTE((tokenRange & ~enum_flag_TokenRangeMask) == 0); static_assert_no_msg(enum_flag_TokenRangeMask == METHOD_TOKEN_RANGE_MASK); m_flagsAndTokenRange = (m_flagsAndTokenRange & ~enum_flag_TokenRangeMask) | tokenRange; } RelativeFixupPointer m_methodTable; RelativePointer m_next; BYTE m_size; // The size of this chunk minus 1 (in multiples of MethodDesc::ALIGNMENT) BYTE m_count; // The number of MethodDescs in this chunk minus 1 UINT16 m_flagsAndTokenRange; // Followed by array of method descs... }; inline int MethodDesc::GetMethodDescIndex() const { LIMITED_METHOD_DAC_CONTRACT; return m_chunkIndex; } inline MethodDescChunk *MethodDesc::GetMethodDescChunk() const { LIMITED_METHOD_DAC_CONTRACT; return PTR_MethodDescChunk(dac_cast(this) - (sizeof(MethodDescChunk) + (GetMethodDescIndex() * MethodDesc::ALIGNMENT))); } // convert an entry point into a MethodDesc MethodDesc* Entry2MethodDesc(PCODE entryPoint, MethodTable *pMT); typedef DPTR(class StoredSigMethodDesc) PTR_StoredSigMethodDesc; class StoredSigMethodDesc : public MethodDesc { public: // Put the sig RVA in here - this allows us to avoid // touching the method desc table when mscorlib is prejitted. RelativePointer m_pSig; DWORD m_cSig; #ifdef _WIN64 // m_dwExtendedFlags is not used by StoredSigMethodDesc itself. // It is used by child classes. We allocate the space here to get // optimal layout. DWORD m_dwExtendedFlags; #endif TADDR GetSigRVA() { LIMITED_METHOD_DAC_CONTRACT; return RelativePointer::GetValueMaybeNullAtPtr(PTR_HOST_MEMBER_TADDR(StoredSigMethodDesc, this, m_pSig)); } bool HasStoredMethodSig(void) { LIMITED_METHOD_DAC_CONTRACT; return !m_pSig.IsNull(); } PCCOR_SIGNATURE GetStoredMethodSig(DWORD* sigLen = NULL) { LIMITED_METHOD_DAC_CONTRACT; if (sigLen) { *sigLen = m_cSig; } #ifdef DACCESS_COMPILE return (PCCOR_SIGNATURE) DacInstantiateTypeByAddress(GetSigRVA(), m_cSig, true); #else // !DACCESS_COMPILE g_IBCLogger.LogNDirectCodeAccess(this); return (PCCOR_SIGNATURE) m_pSig.GetValueMaybeNull(); #endif // !DACCESS_COMPILE } void SetStoredMethodSig(PCCOR_SIGNATURE sig, DWORD sigBytes) { #ifndef DACCESS_COMPILE m_pSig.SetValueMaybeNull((TADDR)sig); m_cSig = sigBytes; #endif // !DACCESS_COMPILE } #ifdef DACCESS_COMPILE void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); #endif }; //----------------------------------------------------------------------- // Operations specific to FCall methods. We use a derived class to get // the compiler involved in enforcing proper method type usage. // DO NOT ADD FIELDS TO THIS CLASS. //----------------------------------------------------------------------- class FCallMethodDesc : public MethodDesc { #ifdef DACCESS_COMPILE friend class NativeImageDumper; #endif DWORD m_dwECallID; #ifdef _WIN64 DWORD m_padding; #endif public: void SetECallID(DWORD dwID) { LIMITED_METHOD_CONTRACT; m_dwECallID = dwID; } DWORD GetECallID() { LIMITED_METHOD_CONTRACT; return m_dwECallID; } }; class HostCodeHeap; class LCGMethodResolver; typedef DPTR(LCGMethodResolver) PTR_LCGMethodResolver; class ILStubResolver; typedef DPTR(ILStubResolver) PTR_ILStubResolver; class DynamicResolver; typedef DPTR(DynamicResolver) PTR_DynamicResolver; class DynamicMethodDesc : public StoredSigMethodDesc { friend class ILStubCache; friend class ILStubState; friend class DynamicMethodTable; friend class MethodDesc; #ifdef DACCESS_COMPILE friend class NativeImageDumper; #endif protected: RelativePointer m_pszMethodName; PTR_DynamicResolver m_pResolver; #ifndef _WIN64 // We use m_dwExtendedFlags from StoredSigMethodDesc on WIN64 DWORD m_dwExtendedFlags; // see DynamicMethodDesc::ExtendedFlags enum #endif typedef enum ExtendedFlags { nomdAttrs = 0x0000FFFF, // method attributes (LCG) nomdILStubAttrs = mdMemberAccessMask | mdStatic, // method attributes (IL stubs) // attributes (except mdStatic and mdMemberAccessMask) have different meaning for IL stubs // mdMemberAccessMask = 0x0007, nomdReverseStub = 0x0008, // mdStatic = 0x0010, nomdCALLIStub = 0x0020, nomdDelegateStub = 0x0040, nomdCopyCtorArgs = 0x0080, nomdUnbreakable = 0x0100, nomdDelegateCOMStub = 0x0200, // CLR->COM or COM->CLR call via a delegate (WinRT specific) nomdSignatureNeedsRestore = 0x0400, nomdStubNeedsCOMStarted = 0x0800, // EnsureComStarted must be called before executing the method nomdMulticastStub = 0x1000, nomdUnboxingILStub = 0x2000, nomdSecureDelegateStub = 0x4000, nomdILStub = 0x00010000, nomdLCGMethod = 0x00020000, nomdStackArgSize = 0xFFFC0000, // native stack arg size for IL stubs } ExtendedFlags; public: bool IsILStub() { LIMITED_METHOD_DAC_CONTRACT; return !!(m_dwExtendedFlags & nomdILStub); } bool IsLCGMethod() { LIMITED_METHOD_DAC_CONTRACT; return !!(m_dwExtendedFlags & nomdLCGMethod); } inline PTR_DynamicResolver GetResolver(); inline PTR_LCGMethodResolver GetLCGMethodResolver(); inline PTR_ILStubResolver GetILStubResolver(); PTR_CUTF8 GetMethodName() { LIMITED_METHOD_DAC_CONTRACT; return RelativePointer::GetValueMaybeNullAtPtr(PTR_HOST_MEMBER_TADDR(DynamicMethodDesc, this, m_pszMethodName)); } WORD GetAttrs() { LIMITED_METHOD_CONTRACT; return (IsILStub() ? (m_dwExtendedFlags & nomdILStubAttrs) : (m_dwExtendedFlags & nomdAttrs)); } DWORD GetExtendedFlags() { LIMITED_METHOD_CONTRACT; return m_dwExtendedFlags; } WORD GetNativeStackArgSize() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(IsILStub()); return (WORD)((m_dwExtendedFlags & nomdStackArgSize) >> 16); } void SetNativeStackArgSize(WORD cbArgSize) { LIMITED_METHOD_CONTRACT; _ASSERTE(IsILStub() && (cbArgSize % sizeof(SLOT)) == 0); m_dwExtendedFlags = (m_dwExtendedFlags & ~nomdStackArgSize) | ((DWORD)cbArgSize << 16); } void SetHasCopyCtorArgs(bool value) { LIMITED_METHOD_CONTRACT; if (value) { m_dwExtendedFlags |= nomdCopyCtorArgs; } } void SetUnbreakable(bool value) { LIMITED_METHOD_CONTRACT; if (value) { m_dwExtendedFlags |= nomdUnbreakable; } } void SetSignatureNeedsRestore(bool value) { LIMITED_METHOD_CONTRACT; if (value) { m_dwExtendedFlags |= nomdSignatureNeedsRestore; } } void SetStubNeedsCOMStarted(bool value) { LIMITED_METHOD_CONTRACT; if (value) { m_dwExtendedFlags |= nomdStubNeedsCOMStarted; } } bool IsRestored() { LIMITED_METHOD_CONTRACT; if (IsSignatureNeedsRestore()) { // Since we don't update the signatreNeedsRestore bit when we actually // restore the signature, the bit will have a stall value. The signature // bit in the metadata will always contain the correct, up-to-date // information. Volatile *pVolatileSig = (Volatile *)GetStoredMethodSig(); if ((*pVolatileSig & IMAGE_CEE_CS_CALLCONV_NEEDSRESTORE) != 0) return false; } return true; } bool IsReverseStub() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(IsILStub()); return (0 != (m_dwExtendedFlags & nomdReverseStub)); } bool IsCALLIStub() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(IsILStub()); return (0 != (m_dwExtendedFlags & nomdCALLIStub)); } bool IsDelegateStub() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(IsILStub()); return (0 != (m_dwExtendedFlags & nomdDelegateStub)); } bool IsCLRToCOMStub() { LIMITED_METHOD_CONTRACT; _ASSERTE(IsILStub()); return ((0 == (m_dwExtendedFlags & mdStatic)) && !IsReverseStub() && !IsDelegateStub()); } bool IsCOMToCLRStub() { LIMITED_METHOD_CONTRACT; _ASSERTE(IsILStub()); return ((0 == (m_dwExtendedFlags & mdStatic)) && IsReverseStub()); } bool IsPInvokeStub() { LIMITED_METHOD_CONTRACT; _ASSERTE(IsILStub()); return ((0 != (m_dwExtendedFlags & mdStatic)) && !IsReverseStub() && !IsCALLIStub()); } bool HasCopyCtorArgs() { LIMITED_METHOD_CONTRACT; _ASSERTE(IsILStub()); return (0 != (m_dwExtendedFlags & nomdCopyCtorArgs)); } bool IsUnbreakable() { LIMITED_METHOD_CONTRACT; _ASSERTE(IsILStub()); return (0 != (m_dwExtendedFlags & nomdUnbreakable)); } bool IsDelegateCOMStub() { LIMITED_METHOD_CONTRACT; _ASSERTE(IsILStub()); return (0 != (m_dwExtendedFlags & nomdDelegateCOMStub)); } bool IsSignatureNeedsRestore() { LIMITED_METHOD_CONTRACT; _ASSERTE(IsILStub()); return (0 != (m_dwExtendedFlags & nomdSignatureNeedsRestore)); } bool IsStubNeedsCOMStarted() { LIMITED_METHOD_CONTRACT; _ASSERTE(IsILStub()); return (0 != (m_dwExtendedFlags & nomdStubNeedsCOMStarted)); } #ifdef FEATURE_MULTICASTSTUB_AS_IL bool IsMulticastStub() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(IsILStub()); return !!(m_dwExtendedFlags & nomdMulticastStub); } #endif #ifdef FEATURE_STUBS_AS_IL bool IsSecureDelegateStub() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(IsILStub()); return !!(m_dwExtendedFlags & nomdSecureDelegateStub); } bool IsUnboxingILStub() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(IsILStub()); return !!(m_dwExtendedFlags & nomdUnboxingILStub); } #endif // Whether the stub takes a context argument that is an interop MethodDesc. bool HasMDContextArg() { LIMITED_METHOD_CONTRACT; return ((IsCLRToCOMStub() && !IsDelegateCOMStub()) || IsPInvokeStub()); } void Restore(); void Fixup(DataImage* image); // // following implementations defined in DynamicMethod.cpp // void Destroy(BOOL fDomainUnload = FALSE); }; class ArrayMethodDesc : public StoredSigMethodDesc { public: // The VTABLE for an array look like // System.Object Vtable // System.Array Vtable // type[] Vtable // Get(= ARRAY_FUNC_CTOR is .ctor }; // Get the index of runtime provided array method DWORD GetArrayFuncIndex() { LIMITED_METHOD_DAC_CONTRACT; // The ru DWORD dwSlot = GetSlot(); DWORD dwVirtuals = GetMethodTable()->GetNumVirtuals(); _ASSERTE(dwSlot >= dwVirtuals); return dwSlot - dwVirtuals; } LPCUTF8 GetMethodName(); DWORD GetAttrs(); CorInfoIntrinsics GetIntrinsicID(); }; #ifdef HAS_NDIRECT_IMPORT_PRECODE typedef NDirectImportPrecode NDirectImportThunkGlue; #else // HAS_NDIRECT_IMPORT_PRECODE class NDirectImportThunkGlue { PVOID m_dummy; // Dummy field to make the alignment right public: LPVOID GetEntrypoint() { LIMITED_METHOD_CONTRACT; return NULL; } void Init(MethodDesc *pMethod) { LIMITED_METHOD_CONTRACT; } }; #ifdef FEATURE_PREJIT PORTABILITY_WARNING("NDirectImportThunkGlue"); #endif // FEATURE_PREJIT #endif // HAS_NDIRECT_IMPORT_PRECODE typedef DPTR(NDirectImportThunkGlue) PTR_NDirectImportThunkGlue; // // This struct consolidates the writeable parts of the NDirectMethodDesc // so that we can eventually layout a read-only NDirectMethodDesc with a pointer // to the writeable parts in an ngen image // class NDirectWriteableData { public: // The JIT generates an indirect call through this location in some cases. // Initialized to NDirectImportThunkGlue. Patched to the true target or // host interceptor stub or alignment thunk after linking. LPVOID m_pNDirectTarget; }; typedef DPTR(NDirectWriteableData) PTR_NDirectWriteableData; //----------------------------------------------------------------------- // Operations specific to NDirect methods. We use a derived class to get // the compiler involved in enforcing proper method type usage. // DO NOT ADD FIELDS TO THIS CLASS. //----------------------------------------------------------------------- class NDirectMethodDesc : public MethodDesc { public: struct temp1 { // If we are hosted, stack imbalance MDA is active, or alignment thunks are needed, // we will intercept m_pNDirectTarget. The true target is saved here. LPVOID m_pNativeNDirectTarget; // Information about the entrypoint RelativePointer m_pszEntrypointName; union { RelativePointer m_pszLibName; DWORD m_dwECallID; // ECallID for QCalls }; // The writeable part of the methoddesc. #if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) RelativePointer m_pWriteableData; #else PlainPointer m_pWriteableData; #endif #ifdef HAS_NDIRECT_IMPORT_PRECODE RelativePointer m_pImportThunkGlue; #else // HAS_NDIRECT_IMPORT_PRECODE NDirectImportThunkGlue m_ImportThunkGlue; #endif // HAS_NDIRECT_IMPORT_PRECODE ULONG m_DefaultDllImportSearchPathsAttributeValue; // DefaultDllImportSearchPathsAttribute is saved. // Various attributes needed at runtime. WORD m_wFlags; #if defined(_TARGET_X86_) // Size of outgoing arguments (on stack). Note that in order to get the @n stdcall name decoration, // it may be necessary to subtract 4 as the hidden large structure pointer parameter does not count. // See code:kStdCallWithRetBuf WORD m_cbStackArgumentSize; #endif // defined(_TARGET_X86_) // This field gets set only when this MethodDesc is marked as PreImplemented RelativePointer m_pStubMD; } ndirect; enum Flags { // There are two groups of flag bits here each which gets initialized // at different times. // // Group 1: The init group. // // This group is set during MethodDesc construction. No race issues // here since they are initialized before the MD is ever published // and never change after that. kEarlyBound = 0x0001, // IJW managed->unmanaged thunk. Standard [sysimport] stuff otherwise. kHasSuppressUnmanagedCodeAccess = 0x0002, kDefaultDllImportSearchPathsIsCached = 0x0004, // set if we cache attribute value. // kUnusedMask = 0x0008 // // Group 2: The runtime group. // // This group is set during runtime potentially by multiple threads // at the same time. All flags in this category has to be set via interlocked operation. // kIsMarshalingRequiredCached = 0x0010, // Set if we have cached the results of marshaling required computation kCachedMarshalingRequired = 0x0020, // The result of the marshaling required computation kNativeAnsi = 0x0040, kLastError = 0x0080, // setLastError keyword specified kNativeNoMangle = 0x0100, // nomangle keyword specified kVarArgs = 0x0200, kStdCall = 0x0400, kThisCall = 0x0800, kIsQCall = 0x1000, kDefaultDllImportSearchPathsStatus = 0x2000, // either method has custom attribute or not. kHasCopyCtorArgs = 0x4000, kStdCallWithRetBuf = 0x8000, // Call returns large structure, only valid if kStdCall is also set }; // Retrieves the cached result of marshaling required computation, or performs the computation // if the result is not cached yet. BOOL MarshalingRequired() { STANDARD_VM_CONTRACT; if ((ndirect.m_wFlags & kIsMarshalingRequiredCached) == 0) { // Compute the flag and cache the result InterlockedSetNDirectFlags(kIsMarshalingRequiredCached | (ComputeMarshalingRequired() ? kCachedMarshalingRequired : 0)); } _ASSERTE((ndirect.m_wFlags & kIsMarshalingRequiredCached) != 0); return (ndirect.m_wFlags & kCachedMarshalingRequired) != 0; } BOOL ComputeMarshalingRequired(); // Atomically set specified flags. Only setting of the bits is supported. void InterlockedSetNDirectFlags(WORD wFlags); void SetIsEarlyBound() { LIMITED_METHOD_CONTRACT; ndirect.m_wFlags |= kEarlyBound; } BOOL IsEarlyBound() { LIMITED_METHOD_CONTRACT; return (ndirect.m_wFlags & kEarlyBound) != 0; } BOOL IsNativeAnsi() const { LIMITED_METHOD_CONTRACT; return (ndirect.m_wFlags & kNativeAnsi) != 0; } BOOL IsNativeNoMangled() const { LIMITED_METHOD_CONTRACT; return (ndirect.m_wFlags & kNativeNoMangle) != 0; } DWORD GetECallID() const { LIMITED_METHOD_CONTRACT; _ASSERTE(IsQCall()); return ndirect.m_dwECallID; } void SetECallID(DWORD dwID) { LIMITED_METHOD_CONTRACT; _ASSERTE(IsQCall()); ndirect.m_dwECallID = dwID; } PTR_CUTF8 GetLibNameRaw() { LIMITED_METHOD_DAC_CONTRACT; return RelativePointer::GetValueMaybeNullAtPtr(PTR_HOST_MEMBER_TADDR(NDirectMethodDesc, this, ndirect.m_pszLibName)); } #ifndef DACCESS_COMPILE LPCUTF8 GetLibName() const { LIMITED_METHOD_CONTRACT; return IsQCall() ? "QCall" : ndirect.m_pszLibName.GetValueMaybeNull(); } #endif // !DACCESS_COMPILE PTR_CUTF8 GetEntrypointName() const { LIMITED_METHOD_DAC_CONTRACT; return RelativePointer::GetValueMaybeNullAtPtr(PTR_HOST_MEMBER_TADDR(NDirectMethodDesc, this, ndirect.m_pszEntrypointName)); } BOOL IsVarArgs() const { LIMITED_METHOD_DAC_CONTRACT; return (ndirect.m_wFlags & kVarArgs) != 0; } BOOL IsStdCall() const { LIMITED_METHOD_DAC_CONTRACT; return (ndirect.m_wFlags & kStdCall) != 0; } BOOL IsThisCall() const { LIMITED_METHOD_DAC_CONTRACT; return (ndirect.m_wFlags & kThisCall) != 0; } // Returns TRUE if this MethodDesc is internal call from mscorlib to mscorwks BOOL IsQCall() const { LIMITED_METHOD_DAC_CONTRACT; return (ndirect.m_wFlags & kIsQCall) != 0; } BOOL HasDefaultDllImportSearchPathsAttribute(); BOOL IsDefaultDllImportSearchPathsAttributeCached() { LIMITED_METHOD_CONTRACT; return (ndirect.m_wFlags & kDefaultDllImportSearchPathsIsCached) != 0; } ULONG DefaultDllImportSearchPathsAttributeCachedValue() { LIMITED_METHOD_CONTRACT; return ndirect.m_DefaultDllImportSearchPathsAttributeValue & 0xFFFFFFFD; } BOOL DllImportSearchAssemblyDirectory() { LIMITED_METHOD_CONTRACT; return (ndirect.m_DefaultDllImportSearchPathsAttributeValue & 0x2) != 0; } BOOL HasCopyCtorArgs() const { LIMITED_METHOD_DAC_CONTRACT; return (ndirect.m_wFlags & kHasCopyCtorArgs) != 0; } void SetHasCopyCtorArgs(BOOL value) { WRAPPER_NO_CONTRACT; if (value) { InterlockedSetNDirectFlags(kHasCopyCtorArgs); } } BOOL IsStdCallWithRetBuf() const { LIMITED_METHOD_DAC_CONTRACT; return (ndirect.m_wFlags & kStdCallWithRetBuf) != 0; } PTR_NDirectWriteableData GetWriteableData() const { LIMITED_METHOD_DAC_CONTRACT; return ReadPointer(this, &NDirectMethodDesc::ndirect, &decltype(NDirectMethodDesc::ndirect)::m_pWriteableData); } PTR_NDirectImportThunkGlue GetNDirectImportThunkGlue() { LIMITED_METHOD_DAC_CONTRACT; TADDR base = PTR_HOST_MEMBER_TADDR(NDirectMethodDesc, this, ndirect.m_pImportThunkGlue); #ifdef HAS_NDIRECT_IMPORT_PRECODE return RelativePointer::GetValueAtPtr(base); #else return dac_cast(base); #endif } LPVOID GetNDirectTarget() { LIMITED_METHOD_CONTRACT; _ASSERTE(IsNDirect()); return GetWriteableData()->m_pNDirectTarget; } LPVOID GetNativeNDirectTarget() { LIMITED_METHOD_CONTRACT; _ASSERTE(IsNDirect()); _ASSERTE_IMPL(!NDirectTargetIsImportThunk()); LPVOID pNativeNDirectTarget = ndirect.m_pNativeNDirectTarget; if (pNativeNDirectTarget != NULL) return pNativeNDirectTarget; return GetNDirectTarget(); } VOID SetNDirectTarget(LPVOID pTarget); #ifndef DACCESS_COMPILE BOOL NDirectTargetIsImportThunk() { WRAPPER_NO_CONTRACT; _ASSERTE(IsNDirect()); return (GetNDirectTarget() == GetNDirectImportThunkGlue()->GetEntrypoint()); } #endif // !DACCESS_COMPILE // Find the entry point name and function address // based on the module and data from NDirectMethodDesc // LPVOID FindEntryPoint(HINSTANCE hMod) const; private: FARPROC FindEntryPointWithMangling(HINSTANCE mod, PTR_CUTF8 entryPointName) const; #ifdef MDA_SUPPORTED Stub* GenerateStubForMDA(LPVOID pNativeTarget, Stub *pInnerStub); #endif // MDA_SUPPORTED public: void SetStackArgumentSize(WORD cbDstBuffer, CorPinvokeMap unmgdCallConv) { LIMITED_METHOD_CONTRACT; #if defined(_TARGET_X86_) // thiscall passes the this pointer in ECX if (unmgdCallConv == pmCallConvThiscall) { _ASSERTE(cbDstBuffer >= sizeof(SLOT)); cbDstBuffer -= sizeof(SLOT); } // Don't write to the field if it's already initialized to avoid creating private pages (NGEN) if (ndirect.m_cbStackArgumentSize == 0xFFFF) { ndirect.m_cbStackArgumentSize = cbDstBuffer; } else { _ASSERTE(ndirect.m_cbStackArgumentSize == cbDstBuffer); } #endif // defined(_TARGET_X86_) } #if defined(_TARGET_X86_) WORD GetStackArgumentSize() const { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(ndirect.m_cbStackArgumentSize != 0xFFFF); // If we have a methoddesc, stackArgSize is the number of bytes of // the outgoing marshalling buffer. return ndirect.m_cbStackArgumentSize; } #endif // defined(_TARGET_X86_) VOID InitEarlyBoundNDirectTarget(); // In AppDomains, we can trigger declarer's cctor when we link the P/Invoke, // which takes care of inlined calls as well. See code:NDirect.NDirectLink. // Although the cctor is guaranteed to run in the shared domain before the // target is invoked (code:IsClassConstructorTriggeredByILStub), we will // trigger at it link time as well because linking may depend on it - the // cctor may change the target DLL, change DLL search path etc. BOOL IsClassConstructorTriggeredAtLinkTime() { LIMITED_METHOD_CONTRACT; MethodTable * pMT = GetMethodTable(); // Try to avoid touching the EEClass if possible if (pMT->IsClassPreInited()) return FALSE; return !pMT->GetClass()->IsBeforeFieldInit(); } #ifndef DACCESS_COMPILE // In the shared domain and in NGENed code, we will trigger declarer's cctor // in the marshaling stub by calling code:StubHelpers.InitDeclaringType. If // this returns TRUE, the call must not be inlined. BOOL IsClassConstructorTriggeredByILStub() { WRAPPER_NO_CONTRACT; return (IsClassConstructorTriggeredAtLinkTime() && (IsZapped() || GetDomain()->IsSharedDomain() || SystemDomain::GetCurrentDomain()->IsCompilationDomain())); } #endif //!DACCESS_COMPILE }; //class NDirectMethodDesc //----------------------------------------------------------------------- // Operations specific to EEImplCall methods. We use a derived class to get // the compiler involved in enforcing proper method type usage. // // For now, the only EE impl is the delegate Invoke method. If we // add other EE impl types in the future, may need a discriminator // field here. //----------------------------------------------------------------------- class EEImplMethodDesc : public StoredSigMethodDesc { }; #ifdef FEATURE_COMINTEROP // This is the extra information needed to be associated with a method in order to use it for // CLR->COM calls. It is currently used by code:ComPlusCallMethodDesc (ordinary CLR->COM calls), // code:InstantiatedMethodDesc (optional field, CLR->COM calls on shared generic interfaces), // and code:DelegateEEClass (delegate->COM calls for WinRT). typedef DPTR(struct ComPlusCallInfo) PTR_ComPlusCallInfo; struct ComPlusCallInfo { // Returns ComPlusCallInfo associated with a method. pMD must be a ComPlusCallMethodDesc or // EEImplMethodDesc that has already been initialized for COM interop. inline static ComPlusCallInfo *FromMethodDesc(MethodDesc *pMD); enum Flags { kHasSuppressUnmanagedCodeAccess = 0x1, kRequiresArgumentWrapping = 0x2, kHasCopyCtorArgs = 0x4, }; union { // IL stub for CLR to COM call PCODE m_pILStub; // MethodDesc of the COM event provider to forward the call to (COM event interfaces) MethodDesc *m_pEventProviderMD; }; // method table of the interface which this represents PTR_MethodTable m_pInterfaceMT; // We need only 3 bits here, see enum Flags below. BYTE m_flags; // ComSlot() (is cached when we first invoke the method and generate // the stubs for it. There's probably a better place to do this // caching but I'm not sure I know all the places these things are // created.) WORD m_cachedComSlot; PCODE * GetAddrOfILStubField() { LIMITED_METHOD_CONTRACT; return &m_pILStub; } #ifdef _TARGET_X86_ // Size of outgoing arguments (on stack). This is currently used only // on x86 when we have an InlinedCallFrame representing a CLR->COM call. WORD m_cbStackArgumentSize; void SetHasCopyCtorArgs(BOOL value) { LIMITED_METHOD_CONTRACT; if (value) FastInterlockOr(reinterpret_cast(&m_flags), kHasCopyCtorArgs); } BOOL HasCopyCtorArgs() { LIMITED_METHOD_CONTRACT; return ((m_flags & kHasCopyCtorArgs) != 0); } void InitStackArgumentSize() { LIMITED_METHOD_CONTRACT; m_cbStackArgumentSize = 0xFFFF; } void SetStackArgumentSize(WORD cbDstBuffer) { LIMITED_METHOD_CONTRACT; // Don't write to the field if it's already initialized to avoid creating private pages (NGEN) if (m_cbStackArgumentSize == 0xFFFF) { m_cbStackArgumentSize = cbDstBuffer; } _ASSERTE(m_cbStackArgumentSize == cbDstBuffer); } WORD GetStackArgumentSize() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(m_cbStackArgumentSize != 0xFFFF); return m_cbStackArgumentSize; } union { LPVOID m_pRetThunk; // used for late-bound calls LPVOID m_pInterceptStub; // used for early-bound IL stub calls }; #else // _TARGET_X86_ void InitStackArgumentSize() { LIMITED_METHOD_CONTRACT; } void SetStackArgumentSize(WORD cbDstBuffer) { LIMITED_METHOD_CONTRACT; } #endif // _TARGET_X86_ // This field gets set only when this MethodDesc is marked as PreImplemented RelativePointer m_pStubMD; #ifdef FEATURE_PREJIT BOOL ShouldSave(DataImage *image); void Fixup(DataImage *image); #endif }; //----------------------------------------------------------------------- // Operations specific to ComPlusCall methods. We use a derived class to get // the compiler involved in enforcing proper method type usage. // DO NOT ADD FIELDS TO THIS CLASS. //----------------------------------------------------------------------- class ComPlusCallMethodDesc : public MethodDesc { public: ComPlusCallInfo *m_pComPlusCallInfo; // initialized in code:ComPlusCall.PopulateComPlusCallMethodDesc void InitRetThunk(); void InitComEventCallInfo(); PCODE * GetAddrOfILStubField() { LIMITED_METHOD_CONTRACT; return m_pComPlusCallInfo->GetAddrOfILStubField(); } MethodTable* GetInterfaceMethodTable() { LIMITED_METHOD_CONTRACT; _ASSERTE(m_pComPlusCallInfo->m_pInterfaceMT != NULL); return m_pComPlusCallInfo->m_pInterfaceMT; } MethodDesc* GetEventProviderMD() { LIMITED_METHOD_CONTRACT; return m_pComPlusCallInfo->m_pEventProviderMD; } BOOL RequiresArgumentWrapping() { LIMITED_METHOD_CONTRACT; return (m_pComPlusCallInfo->m_flags & ComPlusCallInfo::kRequiresArgumentWrapping) != 0; } void SetLateBoundFlags(BYTE newFlags) { LIMITED_METHOD_CONTRACT; FastInterlockOr(reinterpret_cast(&m_pComPlusCallInfo->m_flags), newFlags); } #ifdef _TARGET_X86_ BOOL HasCopyCtorArgs() { LIMITED_METHOD_CONTRACT; return m_pComPlusCallInfo->HasCopyCtorArgs(); } void SetHasCopyCtorArgs(BOOL value) { LIMITED_METHOD_CONTRACT; m_pComPlusCallInfo->SetHasCopyCtorArgs(value); } WORD GetStackArgumentSize() { LIMITED_METHOD_DAC_CONTRACT; return m_pComPlusCallInfo->GetStackArgumentSize(); } void SetStackArgumentSize(WORD cbDstBuffer) { LIMITED_METHOD_CONTRACT; m_pComPlusCallInfo->SetStackArgumentSize(cbDstBuffer); } #else // _TARGET_X86_ void SetStackArgumentSize(WORD cbDstBuffer) { LIMITED_METHOD_CONTRACT; } #endif // _TARGET_X86_ }; #endif // FEATURE_COMINTEROP //----------------------------------------------------------------------- // InstantiatedMethodDesc's are used for generics and // come in four flavours, discriminated by the // low order bits of the first field: // // 00 --> GenericMethodDefinition // 01 --> UnsharedMethodInstantiation // 10 --> SharedMethodInstantiation // 11 --> WrapperStubWithInstantiations - and unboxing or instantiating stub // // A SharedMethodInstantiation descriptor extends MethodDesc // with a pointer to dictionary layout and a representative instantiation. // // A GenericMethodDefinition is the instantiation of a // generic method at its formals, used for verifying the method and // also for reflection. // // A WrapperStubWithInstantiations extends MethodDesc with: // (1) a method instantiation // (2) an "underlying" method descriptor. // A WrapperStubWithInstantiations may be placed in a MethodChunk for // a method table which specifies an exact instantiation for the class/struct. // A WrapperStubWithInstantiations may be either // an BoxedEntryPointStub or an exact-instantiation stub. // // Exact-instantiation stubs are used as extra type-context parameters. When // used as an entry, instantiating stubs pass an instantiation // dictionary on to the underlying method. These entries are required to // implement ldftn instructions on instantiations of shared generic // methods, as the InstantiatingStub's pointer does not expect a // dictionary argument; instead, it passes itself on to the shared // code as the dictionary. // // An UnsharedMethodInstantiation contains just an instantiation. // These are fully-specialized wrt method and class type parameters. // These satisfy (!IMD_IsGenericMethodDefinition() && // !IMD_IsSharedByGenericMethodInstantiations() && // !IMD_IsWrapperStubWithInstantiations()) // // Note that plain MethodDescs may represent shared code w.r.t. class type // parameters (see MethodDesc::IsSharedByGenericInstantiations()). //----------------------------------------------------------------------- class InstantiatedMethodDesc : public MethodDesc { #ifdef DACCESS_COMPILE friend class NativeImageDumper; #endif public: // All varities of InstantiatedMethodDesc's support this method. BOOL IMD_HasMethodInstantiation() { LIMITED_METHOD_DAC_CONTRACT; if (IMD_IsGenericMethodDefinition()) return TRUE; else return !m_pPerInstInfo.IsNull(); } // All varieties of InstantiatedMethodDesc's support this method. Instantiation IMD_GetMethodInstantiation() { LIMITED_METHOD_DAC_CONTRACT; return Instantiation(IMD_GetMethodDictionary()->GetInstantiation(), m_wNumGenericArgs); } PTR_Dictionary IMD_GetMethodDictionary() { LIMITED_METHOD_DAC_CONTRACT; return ReadPointerMaybeNull(this, &InstantiatedMethodDesc::m_pPerInstInfo); } PTR_Dictionary IMD_GetMethodDictionaryNonNull() { LIMITED_METHOD_DAC_CONTRACT; return ReadPointer(this, &InstantiatedMethodDesc::m_pPerInstInfo); } BOOL IMD_IsGenericMethodDefinition() { LIMITED_METHOD_DAC_CONTRACT; return((m_wFlags2 & KindMask) == GenericMethodDefinition); } BOOL IMD_IsSharedByGenericMethodInstantiations() { LIMITED_METHOD_DAC_CONTRACT; return((m_wFlags2 & KindMask) == SharedMethodInstantiation); } BOOL IMD_IsWrapperStubWithInstantiations() { LIMITED_METHOD_DAC_CONTRACT; return((m_wFlags2 & KindMask) == WrapperStubWithInstantiations); } BOOL IMD_IsEnCAddedMethod() { LIMITED_METHOD_CONTRACT; #ifdef EnC_SUPPORTED return((m_wFlags2 & KindMask) == EnCAddedMethod); #else return FALSE; #endif } #ifdef FEATURE_COMINTEROP BOOL IMD_HasComPlusCallInfo() { LIMITED_METHOD_CONTRACT; return ((m_wFlags2 & HasComPlusCallInfo) != 0); } void IMD_SetupGenericComPlusCall() { LIMITED_METHOD_CONTRACT; m_wFlags2 |= InstantiatedMethodDesc::HasComPlusCallInfo; IMD_GetComPlusCallInfo()->InitStackArgumentSize(); } PTR_ComPlusCallInfo IMD_GetComPlusCallInfo() { LIMITED_METHOD_CONTRACT; _ASSERTE(IMD_HasComPlusCallInfo()); SIZE_T size = s_ClassificationSizeTable[m_wFlags & (mdcClassification | mdcHasNonVtableSlot | mdcMethodImpl)]; if (HasNativeCodeSlot()) { size += (*dac_cast(dac_cast(this) + size) & FIXUP_LIST_MASK) ? (sizeof(NativeCodeSlot) + sizeof(FixupListSlot)) : sizeof(NativeCodeSlot); } return dac_cast(dac_cast(this) + size); } #endif // FEATURE_COMINTEROP PTR_DictionaryLayout GetDictLayoutRaw() { LIMITED_METHOD_DAC_CONTRACT; return RelativePointer::GetValueMaybeNullAtPtr(PTR_HOST_MEMBER_TADDR(InstantiatedMethodDesc, this, m_pDictLayout)); } PTR_MethodDesc IMD_GetWrappedMethodDesc() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(IMD_IsWrapperStubWithInstantiations()); return RelativeFixupPointer::GetValueAtPtr(PTR_HOST_MEMBER_TADDR(InstantiatedMethodDesc, this, m_pWrappedMethodDesc)); } #ifndef DACCESS_COMPILE // Get the dictionary layout, if there is one DictionaryLayout* IMD_GetDictionaryLayout() { WRAPPER_NO_CONTRACT; if (IMD_IsWrapperStubWithInstantiations() && IMD_HasMethodInstantiation()) { InstantiatedMethodDesc* pIMD = IMD_GetWrappedMethodDesc()->AsInstantiatedMethodDesc(); return pIMD->m_pDictLayout.GetValueMaybeNull(); } else if (IMD_IsSharedByGenericMethodInstantiations()) return m_pDictLayout.GetValueMaybeNull(); else return NULL; } #endif // !DACCESS_COMPILE // Setup the IMD as shared code void SetupSharedMethodInstantiation(DWORD numGenericArgs, TypeHandle *pPerInstInfo, DictionaryLayout *pDL); // Setup the IMD as unshared code void SetupUnsharedMethodInstantiation(DWORD numGenericArgs, TypeHandle *pInst); // Setup the IMD as the special MethodDesc for a "generic" method void SetupGenericMethodDefinition(IMDInternalImport *pIMDII, LoaderAllocator* pAllocator, AllocMemTracker *pamTracker, Module *pModule, mdMethodDef tok); // Setup the IMD as a wrapper around another method desc void SetupWrapperStubWithInstantiations(MethodDesc* wrappedMD,DWORD numGenericArgs, TypeHandle *pGenericMethodInst); #ifdef EnC_SUPPORTED void SetupEnCAddedMethod() { LIMITED_METHOD_CONTRACT; m_wFlags2 = EnCAddedMethod; } #endif private: enum { KindMask = 0x07, GenericMethodDefinition = 0x00, UnsharedMethodInstantiation = 0x01, SharedMethodInstantiation = 0x02, WrapperStubWithInstantiations = 0x03, #ifdef EnC_SUPPORTED // Non-virtual method added through EditAndContinue. EnCAddedMethod = 0x07, #endif // EnC_SUPPORTED Unrestored = 0x08, #ifdef FEATURE_COMINTEROP HasComPlusCallInfo = 0x10, // this IMD contains an optional ComPlusCallInfo #endif // FEATURE_COMINTEROP }; friend class MethodDesc; // this fields are currently accessed by MethodDesc::Save/Restore etc. union { RelativePointer m_pDictLayout; //SharedMethodInstantiation RelativeFixupPointer m_pWrappedMethodDesc; // For WrapperStubWithInstantiations }; public: // make private: JITinterface.cpp accesses through this // Note we can't steal bits off m_pPerInstInfo as the JIT generates code to access through it!! // Type parameters to method (exact) // For non-unboxing instantiating stubs this is actually // a dictionary and further slots may hang off the end of the // instantiation. // // For generic method definitions that are not the typical method definition (e.g. C.m) // this field is null; to obtain the instantiation use LoadMethodInstantiation #if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) RelativePointer m_pPerInstInfo; //SHARED #else PlainPointer m_pPerInstInfo; //SHARED #endif private: WORD m_wFlags2; WORD m_wNumGenericArgs; public: static InstantiatedMethodDesc *FindOrCreateExactClassMethod(MethodTable *pExactMT, MethodDesc *pCanonicalMD); static InstantiatedMethodDesc* FindLoadedInstantiatedMethodDesc(MethodTable *pMT, mdMethodDef methodDef, Instantiation methodInst, BOOL getSharedNotStub); private: static InstantiatedMethodDesc *NewInstantiatedMethodDesc(MethodTable *pMT, MethodDesc* pGenericMDescInRepMT, MethodDesc* pSharedMDescForStub, Instantiation methodInst, BOOL getSharedNotStub); }; inline PTR_MethodTable MethodDesc::GetMethodTable_NoLogging() const { LIMITED_METHOD_DAC_CONTRACT; MethodDescChunk *pChunk = GetMethodDescChunk(); PREFIX_ASSUME(pChunk != NULL); return pChunk->GetMethodTable(); } inline PTR_MethodTable MethodDesc::GetMethodTable() const { LIMITED_METHOD_DAC_CONTRACT; g_IBCLogger.LogMethodDescAccess(this); return GetMethodTable_NoLogging(); } inline DPTR(RelativeFixupPointer) MethodDesc::GetMethodTablePtr() const { LIMITED_METHOD_DAC_CONTRACT; MethodDescChunk *pChunk = GetMethodDescChunk(); PREFIX_ASSUME(pChunk != NULL); return pChunk->GetMethodTablePtr(); } inline MethodTable* MethodDesc::GetCanonicalMethodTable() { LIMITED_METHOD_DAC_CONTRACT; return GetMethodTable()->GetCanonicalMethodTable(); } inline mdMethodDef MethodDesc::GetMemberDef_NoLogging() const { LIMITED_METHOD_DAC_CONTRACT; MethodDescChunk *pChunk = GetMethodDescChunk(); PREFIX_ASSUME(pChunk != NULL); UINT16 tokrange = pChunk->GetTokRange(); UINT16 tokremainder = m_wFlags3AndTokenRemainder & enum_flag3_TokenRemainderMask; static_assert_no_msg(enum_flag3_TokenRemainderMask == METHOD_TOKEN_REMAINDER_MASK); return MergeToken(tokrange, tokremainder); } inline mdMethodDef MethodDesc::GetMemberDef() const { LIMITED_METHOD_DAC_CONTRACT; g_IBCLogger.LogMethodDescAccess(this); return GetMemberDef_NoLogging(); } // Set the offset of this method desc in a chunk table (which allows us // to work back to the method table/module pointer stored at the head of // the table. inline void MethodDesc::SetChunkIndex(MethodDescChunk * pChunk) { WRAPPER_NO_CONTRACT; // Calculate the offset (mod 8) from the chunk table header. SIZE_T offset = (BYTE*)this - (BYTE*)pChunk->GetFirstMethodDesc(); _ASSERTE((offset & ALIGNMENT_MASK) == 0); offset >>= ALIGNMENT_SHIFT; // Make sure that we did not overflow the BYTE _ASSERTE(offset == (BYTE)offset); m_chunkIndex = (BYTE)offset; // Make sure that the MethodDescChunk is setup correctly _ASSERTE(GetMethodDescChunk() == pChunk); } inline void MethodDesc::SetMemberDef(mdMethodDef mb) { WRAPPER_NO_CONTRACT; UINT16 tokrange; UINT16 tokremainder; SplitToken(mb, &tokrange, &tokremainder); _ASSERTE((tokremainder & ~enum_flag3_TokenRemainderMask) == 0); m_wFlags3AndTokenRemainder = (m_wFlags3AndTokenRemainder & ~enum_flag3_TokenRemainderMask) | tokremainder; if (GetMethodDescIndex() == 0) { GetMethodDescChunk()->SetTokenRange(tokrange); } #ifdef _DEBUG if (mb != 0) { _ASSERTE(GetMemberDef_NoLogging() == mb); } #endif } #ifdef _DEBUG inline BOOL MethodDesc::SanityCheck() { CONTRACTL { NOTHROW; GC_NOTRIGGER; SO_TOLERANT; MODE_ANY; SUPPORTS_DAC; } CONTRACTL_END; // Do a simple sanity test if (IsRestored()) { // If it looks good, do a more intensive sanity test. We don't care about the result, // we just want it to not AV. return GetMethodTable() == m_pDebugMethodTable.GetValue() && this->GetModule() != NULL; } return TRUE; } #endif // _DEBUG inline BOOL MethodDesc::IsEnCAddedMethod() { LIMITED_METHOD_DAC_CONTRACT; return (GetClassification() == mcInstantiated) && AsInstantiatedMethodDesc()->IMD_IsEnCAddedMethod(); } inline BOOL MethodDesc::HasNonVtableSlot() { LIMITED_METHOD_DAC_CONTRACT; return (m_wFlags & mdcHasNonVtableSlot) != 0; } inline Instantiation MethodDesc::GetMethodInstantiation() const { LIMITED_METHOD_DAC_CONTRACT; return (GetClassification() == mcInstantiated) ? AsInstantiatedMethodDesc()->IMD_GetMethodInstantiation() : Instantiation(); } inline Instantiation MethodDesc::GetClassInstantiation() const { LIMITED_METHOD_DAC_CONTRACT; return GetMethodTable()->GetInstantiation(); } inline BOOL MethodDesc::IsGenericMethodDefinition() const { LIMITED_METHOD_DAC_CONTRACT; g_IBCLogger.LogMethodDescAccess(this); return GetClassification() == mcInstantiated && AsInstantiatedMethodDesc()->IMD_IsGenericMethodDefinition(); } // True if the method descriptor is an instantiation of a generic method. inline BOOL MethodDesc::HasMethodInstantiation() const { LIMITED_METHOD_DAC_CONTRACT; return mcInstantiated == GetClassification() && AsInstantiatedMethodDesc()->IMD_HasMethodInstantiation(); } #if defined(FEATURE_GDBJIT) class CalledMethod { private: MethodDesc * m_pMD; void * m_CallAddr; CalledMethod * m_pNext; public: CalledMethod(MethodDesc *pMD, void * addr, CalledMethod * next) : m_pMD(pMD), m_CallAddr(addr), m_pNext(next) {} ~CalledMethod() {} MethodDesc * GetMethodDesc() { return m_pMD; } void * GetCallAddr() { return m_CallAddr; } CalledMethod * GetNext() { return m_pNext; } }; #endif #include "method.inl" #endif // !_METHOD_H