mirror of
https://github.com/VSadov/Satori.git
synced 2025-06-09 17:44:48 +09:00

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