summaryrefslogtreecommitdiff
path: root/src/debug/di/rsclass.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/di/rsclass.cpp')
-rw-r--r--src/debug/di/rsclass.cpp1194
1 files changed, 1194 insertions, 0 deletions
diff --git a/src/debug/di/rsclass.cpp b/src/debug/di/rsclass.cpp
new file mode 100644
index 0000000000..f0cdda4a0b
--- /dev/null
+++ b/src/debug/di/rsclass.cpp
@@ -0,0 +1,1194 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+
+//
+// File: class.cpp
+//
+//*****************************************************************************
+#include "stdafx.h"
+
+// We have an assert in ceemain.cpp that validates this assumption
+#define FIELD_OFFSET_NEW_ENC_DB 0x07FFFFFB
+
+#include "winbase.h"
+#include "corpriv.h"
+
+
+
+//-----------------------------------------------------------------------------
+// class CordbClass
+// Represents a IL-Class in the debuggee, eg: List<T>, System.Console, etc
+//
+// Parameters:
+// m - module that the class is contained in.
+// classMetadataToken - metadata token for the class, scoped to module m.
+//-----------------------------------------------------------------------------
+CordbClass::CordbClass(CordbModule *m, mdTypeDef classMetadataToken)
+ : CordbBase(m->GetProcess(), classMetadataToken, enumCordbClass),
+ m_loadLevel(Constructed),
+ m_fLoadEventSent(FALSE),
+ m_fHasBeenUnloaded(false),
+ m_pModule(m),
+ m_token(classMetadataToken),
+ m_fIsValueClassKnown(false),
+ m_fIsValueClass(false),
+ m_fHasTypeParams(false),
+ m_continueCounterLastSync(0),
+ m_fCustomNotificationsEnabled(false)
+{
+ m_classInfo.Clear();
+}
+
+
+/*
+ A list of which resources owned by this object are accounted for.
+
+ HANDLED:
+ CordbModule* m_module; // Assigned w/o AddRef()
+ FieldData *m_fields; // Deleted in ~CordbClass
+ CordbHangingFieldTable m_hangingFieldsStatic; // by value, ~CHashTableAndData frees
+*/
+
+
+//-----------------------------------------------------------------------------
+// Destructor for CordbClass
+//-----------------------------------------------------------------------------
+CordbClass::~CordbClass()
+{
+ // We should have been explicitly neutered before our internal ref went to 0.
+ _ASSERTE(IsNeutered());
+}
+
+//-----------------------------------------------------------------------------
+// Neutered by CordbModule
+// See CordbBase::Neuter for semantics.
+//-----------------------------------------------------------------------------
+void CordbClass::Neuter()
+{
+ // Reduce the reference count on the type object for this class
+ m_type.Clear();
+ CordbBase::Neuter();
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Standard IUnknown::QI implementation.
+// See IUnknown::QI for standard semantics.
+//-----------------------------------------------------------------------------
+HRESULT CordbClass::QueryInterface(REFIID id, void **pInterface)
+{
+ if (id == IID_ICorDebugClass)
+ {
+ *pInterface = static_cast<ICorDebugClass*>(this);
+ }
+ else if (id == IID_ICorDebugClass2)
+ {
+ *pInterface = static_cast<ICorDebugClass2*>(this);
+ }
+ else if (id == IID_IUnknown)
+ {
+ *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugClass*>(this));
+ }
+ else
+ {
+ *pInterface = NULL;
+ return E_NOINTERFACE;
+ }
+
+ ExternalAddRef();
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Get a ICorDebugValue for a static field on this class.
+//
+// Parameters:
+// fieldDef - metadata token for field on this class. Can not be from an
+// inherited class.
+// pFrame - frame used to resolve Thread-static, AppDomain-static, etc.
+// ppValue - OUT: gets value of the field.
+//
+// Returns:
+// S_OK on success.
+// CORDBG_E_STATIC_VAR_NOT_AVAILABLE
+//-----------------------------------------------------------------------------
+HRESULT CordbClass::GetStaticFieldValue(mdFieldDef fieldDef,
+ ICorDebugFrame *pFrame,
+ ICorDebugValue **ppValue)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ HRESULT hr = S_OK;
+ *ppValue = NULL;
+ BOOL fEnCHangingField = FALSE;
+
+
+ IMetaDataImport * pImport = NULL;
+ EX_TRY
+ {
+ pImport = GetModule()->GetMetaDataImporter(); // throws
+
+ // Validate the token.
+ if (!pImport->IsValidToken(fieldDef) || (TypeFromToken(fieldDef) != mdtFieldDef))
+ {
+ ThrowHR(E_INVALIDARG);
+ }
+
+ // Make sure we have enough info about the class.
+ Init();
+
+ // Uninstantiated generics (eg, Foo<T>) don't have static data. Must use instantiated (eg Foo<int>)
+ // But all CordbClass instances are uninstantiated. So this should fail for all generic types.
+ // Normally, debuggers should be using ICorDebugType instead.
+ // Though in the forward compat case, they'll hit this.
+ if (HasTypeParams())
+ {
+ ThrowHR(CORDBG_E_STATIC_VAR_NOT_AVAILABLE);
+ }
+
+
+ // Lookup the field given its metadata token.
+ FieldData *pFieldData;
+
+ hr = GetFieldInfo(fieldDef, &pFieldData);
+
+ // This field was added by EnC, need to use EnC specific code path
+ if (hr == CORDBG_E_ENC_HANGING_FIELD)
+ {
+ // Static fields added with EnC hang off the EnCFieldDesc
+ hr = GetEnCHangingField(fieldDef,
+ &pFieldData,
+ NULL);
+
+ if (SUCCEEDED(hr))
+ {
+ fEnCHangingField = TRUE;
+ }
+ // Note: the FieldOffset in pFieldData has been cooked to produce
+ // the correct address of the field in the syncBlock.
+ // @todo: extend Debugger_IPCEFieldData so we don't have to cook the offset here
+ }
+
+ IfFailThrow(hr);
+
+ {
+ Instantiation emptyInst;
+
+ hr = CordbClass::GetStaticFieldValue2(GetModule(),
+ pFieldData,
+ fEnCHangingField,
+ &emptyInst,
+ pFrame,
+ ppValue);
+ // Let hr fall through
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+
+ // Translate Failure HRs.
+ if (pImport != NULL)
+ {
+ hr = CordbClass::PostProcessUnavailableHRESULT(hr, pImport, fieldDef);
+ }
+
+ return hr;
+
+}
+
+//-----------------------------------------------------------------------------
+// Common helper for accessing statics from both CordbClass and CordbType.
+//
+// Arguments:
+// pModule - module containing the class
+// pFieldData - field data describing the field (this is correlated to a
+// mdFieldDef, but has more specific data)
+// fEnCHangingField - field storage hangs off the FieldDesc for EnC
+// pInst - generic instantiation.
+// pFrame - frame used for context for Thread-static, AD-static, etc.
+// ppValue - OUT: out parameter to get value.
+//
+// Returns:
+// S_OK on success.
+// CORDBG_E_FIELD_NOT_STATIC - if field isn't static.
+// CORDBG_E_STATIC_VAR_NOT_AVAILABLE - if field storage is not available.
+// Else some other failure.
+/* static */
+HRESULT CordbClass::GetStaticFieldValue2(CordbModule * pModule,
+ FieldData * pFieldData,
+ BOOL fEnCHangingField,
+ const Instantiation * pInst,
+ ICorDebugFrame * pFrame,
+ ICorDebugValue ** ppValue)
+{
+ FAIL_IF_NEUTERED(pModule);
+ INTERNAL_SYNC_API_ENTRY(pModule->GetProcess());
+ _ASSERTE((pModule->GetProcess()->GetShim() == NULL) || pModule->GetProcess()->GetSynchronized());
+ HRESULT hr = S_OK;
+
+ if (!pFieldData->m_fFldIsStatic)
+ {
+ return CORDBG_E_FIELD_NOT_STATIC;
+ }
+
+ CORDB_ADDRESS pRmtStaticValue = NULL;
+ CordbProcess * pProcess = pModule->GetProcess();
+
+ if (pFieldData->m_fFldIsCollectibleStatic)
+ {
+ EX_TRY
+ {
+ pRmtStaticValue = pProcess->GetDAC()->GetCollectibleTypeStaticAddress(pFieldData->m_vmFieldDesc,
+ pModule->GetAppDomain()->GetADToken());
+ }
+ EX_CATCH_HRESULT(hr);
+ if(FAILED(hr))
+ {
+ return hr;
+ }
+ }
+ else if (!pFieldData->m_fFldIsTLS && !pFieldData->m_fFldIsContextStatic)
+ {
+ // Statics never move, so we always address them using their absolute address.
+ _ASSERTE(pFieldData->OkToGetOrSetStaticAddress());
+ pRmtStaticValue = pFieldData->GetStaticAddress();
+ }
+ else
+ {
+ // We've got a thread or context local static
+
+ if( fEnCHangingField )
+ {
+ // fEnCHangingField is set for fields added with EnC which hang off the FieldDesc.
+ // Thread-local and context-local statics cannot be added with EnC, so we shouldn't be here
+ // if this is an EnC field is thread- or context-local.
+ _ASSERTE(!pFieldData->m_fFldIsTLS );
+ _ASSERTE(!pFieldData->m_fFldIsContextStatic );
+ }
+ else
+ {
+ if (pFrame == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ CordbFrame * pRealFrame = CordbFrame::GetCordbFrameFromInterface(pFrame);
+ _ASSERTE(pRealFrame != NULL);
+
+ // Get the thread we are working on
+ CordbThread * pThread = pRealFrame->m_pThread;
+
+ EX_TRY
+ {
+ pRmtStaticValue = pProcess->GetDAC()->GetThreadOrContextStaticAddress(pFieldData->m_vmFieldDesc,
+ pThread->m_vmThreadToken);
+ }
+ EX_CATCH_HRESULT(hr);
+ if(FAILED(hr))
+ {
+ return hr;
+ }
+
+ }
+ }
+
+ if (pRmtStaticValue == NULL)
+ {
+ // type probably wasn't loaded yet.
+ // The debugger may chose to func-eval the creation of an instance of this type and try again.
+ return CORDBG_E_STATIC_VAR_NOT_AVAILABLE;
+ }
+
+ SigParser sigParser;
+ hr = S_OK;
+ EX_TRY
+ {
+ hr = pFieldData->GetFieldSignature(pModule, &sigParser);
+ }
+ EX_CATCH_HRESULT(hr);
+ IfFailRet(hr);
+
+ CordbType * pType;
+ IfFailRet (CordbType::SigToType(pModule, &sigParser, pInst, &pType));
+
+ bool fIsValueClass = false;
+ EX_TRY
+ {
+ fIsValueClass = pType->IsValueType(); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+ IfFailRet(hr);
+
+ // Static value classes are stored as handles so that GC can deal with them properly. Thus, we need to follow the
+ // handle like an objectref. Do this by forcing CreateValueByType to think this is an objectref. Note: we don't do
+ // this for value classes that have an RVA, since they're layed out at the RVA with no handle.
+ bool fIsBoxed = (fIsValueClass &&
+ !pFieldData->m_fFldIsRVA &&
+ !pFieldData->m_fFldIsPrimitive &&
+ !pFieldData->m_fFldIsTLS &&
+ !pFieldData->m_fFldIsContextStatic);
+
+ TargetBuffer remoteValue(pRmtStaticValue, CordbValue::GetSizeForType(pType, fIsBoxed ? kBoxed : kUnboxed));
+ ICorDebugValue * pValue;
+
+ EX_TRY
+ {
+ CordbValue::CreateValueByType(pModule->GetAppDomain(),
+ pType,
+ fIsBoxed,
+ remoteValue,
+ MemoryRange(NULL, 0),
+ NULL,
+ &pValue); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (SUCCEEDED(hr))
+ {
+ *ppValue = pValue;
+ }
+
+ return hr;
+}
+
+
+//-----------------------------------------------------------------------------
+// Public method to build a CordbType from a CordbClass.
+// This is used to build up generic types. Eg, build:
+// List<T> + { int } --> List<int>
+//
+// Arguments:
+// elementType - element type. Either ELEMENT_TYPE_CLASS, or ELEMENT_TYPE_VALUETYPE.
+// We could technically figure this out from the metadata (by looking if it derives
+// from System.ValueType).
+// cTypeArgs - number of elements in rgpTypeArgs array
+// rgpTypeArgs - array for type args.
+// ppType - OUT: out parameter to hold resulting type.
+//
+// Returns:
+// S_OK on success. Else false.
+//
+HRESULT CordbClass::GetParameterizedType(CorElementType elementType,
+ ULONG32 cTypeArgs,
+ ICorDebugType * rgpTypeArgs[],
+ ICorDebugType ** ppType)
+{
+ PUBLIC_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(ppType, ICorDebugType **);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ // Note: Do not call Init() to find out if its a VC or not.
+ // Rather expect the client to tell us. This means the debug client
+ // can describe type instantiations not yet seen in the EE.
+
+ if ((elementType != ELEMENT_TYPE_CLASS) && (elementType != ELEMENT_TYPE_VALUETYPE))
+ {
+ return E_INVALIDARG;
+ }
+
+ // Prefast overflow check:
+ S_UINT32 allocSize = S_UINT32( cTypeArgs ) * S_UINT32( sizeof(CordbType *) );
+
+ if (allocSize.IsOverflow())
+ {
+ return E_INVALIDARG;
+ }
+
+ CordbAppDomain * pClassAppDomain = GetAppDomain();
+
+ // Note: casting from (ICorDebugType **) to (CordbType **) is not valid.
+ // Offsets may differ. Copy and validate the type array.
+ CordbType ** ppArgTypes = reinterpret_cast<CordbType **>(_alloca( allocSize.Value()));
+
+ for (unsigned int i = 0; i < cTypeArgs; i++)
+ {
+ ppArgTypes[i] = static_cast<CordbType *>( rgpTypeArgs[i] );
+
+ CordbAppDomain * pArgAppDomain = ppArgTypes[i]->GetAppDomain();
+
+ if ((pArgAppDomain != NULL) && (pArgAppDomain != pClassAppDomain))
+ {
+ return CORDBG_E_APPDOMAIN_MISMATCH;
+ }
+ }
+
+ {
+ CordbType * pResultType;
+
+ Instantiation typeInstantiation(cTypeArgs, ppArgTypes);
+
+ HRESULT hr = CordbType::MkType(pClassAppDomain, elementType, this, &typeInstantiation, &pResultType);
+
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ *ppType = pResultType;
+ }
+
+ _ASSERTE(*ppType);
+
+ if (*ppType)
+ {
+ (*ppType)->AddRef();
+ }
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Returns true if the field is a static literal.
+// In this case, the debugger should get the value from the metadata.
+//-----------------------------------------------------------------------------
+bool IsFieldStaticLiteral(IMetaDataImport *pImport, mdFieldDef fieldDef)
+{
+ DWORD dwFieldAttr;
+ HRESULT hr2 = pImport->GetFieldProps(
+ fieldDef,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ &dwFieldAttr,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ 0);
+
+ if (SUCCEEDED(hr2) && IsFdLiteral(dwFieldAttr))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Filter to determine a more descriptive failing HResult for a field lookup.
+//
+// Parameters:
+// hr - incoming ambiguous HR.
+// pImport - metadata importer for this class.
+// feildDef - field being looked up.
+//
+// Returns:
+// hr - the incoming HR if no further HR can be determined.
+// else another failing HR that it judged to be more specific that the incoming HR.
+//-----------------------------------------------------------------------------
+HRESULT CordbClass::PostProcessUnavailableHRESULT(HRESULT hr,
+ IMetaDataImport *pImport,
+ mdFieldDef fieldDef)
+{
+ CONTRACTL
+ {
+ NOTHROW; // just translates an HR. shouldn't need to throw.
+ }
+ CONTRACTL_END;
+
+ if (hr == CORDBG_E_FIELD_NOT_AVAILABLE)
+ {
+ if (IsFieldStaticLiteral(pImport, fieldDef))
+ {
+ return CORDBG_E_VARIABLE_IS_ACTUALLY_LITERAL;
+ }
+ }
+
+ return hr;
+}
+
+//-----------------------------------------------------------------------------
+// Public method to get the Module that this class lives in.
+//
+// Parameters:
+// ppModule - OUT: holds module that this class gets in.
+//
+// Returns:
+// S_OK on success.
+//-----------------------------------------------------------------------------
+HRESULT CordbClass::GetModule(ICorDebugModule **ppModule)
+{
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(ppModule, ICorDebugModule **);
+
+ *ppModule = static_cast<ICorDebugModule*> (m_pModule);
+ m_pModule->ExternalAddRef();
+
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Get the mdTypeDef token that this class corresponds to.
+//
+// Parameters:
+// pTypeDef - OUT: out param to get typedef token.
+//
+// Returns:
+// S_OK - on success.
+//-----------------------------------------------------------------------------
+HRESULT CordbClass::GetToken(mdTypeDef *pTypeDef)
+{
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(pTypeDef, mdTypeDef *);
+
+ _ASSERTE(TypeFromToken(m_token) == mdtTypeDef);
+
+ *pTypeDef = m_token;
+
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Set the JMC status on all of our member functions.
+// The current implementation just uses the metadata to enumerate all
+// methods and then calls SetJMCStatus on each method.
+// This isn't great perf, but this should never be needed in a
+// perf-critical situation.
+//
+// Parameters:
+// fIsUserCode - true to set entire class to user code. False to set to
+// non-user code.
+//
+// Returns:
+// S_OK on success. On failure, the user-code status of the methods in the
+// class is random.
+//-----------------------------------------------------------------------------
+HRESULT CordbClass::SetJMCStatus(BOOL fIsUserCode)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ // Get the member functions via a meta data interface
+ CordbModule * pModule = GetModule();
+
+ // Ensure that our process is in a sane state.
+ CordbProcess * pProcess = pModule->GetProcess();
+ _ASSERTE(pProcess != NULL);
+
+ IMetaDataImport * pImport = NULL;
+ HCORENUM phEnum = 0;
+
+ HRESULT hr = S_OK;
+
+ mdMethodDef rTokens[100];
+ ULONG i;
+ ULONG count;
+
+ EX_TRY
+ {
+ pImport = pModule->GetMetaDataImporter();
+ do
+ {
+ hr = pImport->EnumMethods(&phEnum, m_token, rTokens, NumItems(rTokens), &count);
+ IfFailThrow(hr);
+
+ for (i = 0; i < count; i++)
+ {
+ RSLockHolder lockHolder(pProcess->GetProcessLock());
+ // Need the ICorDebugFunction to query for JMC status.
+ CordbFunction * pFunction = pModule->LookupOrCreateFunctionLatestVersion(rTokens[i]);
+
+ lockHolder.Release(); // Must release before sending an IPC event
+ hr = pFunction->SetJMCStatus(fIsUserCode);
+ IfFailThrow(hr);
+ }
+ }
+ while (count > 0);
+
+ _ASSERTE(SUCCEEDED(hr));
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if ((pImport != NULL) && (phEnum != 0))
+ {
+ pImport->CloseEnum(phEnum);
+ }
+
+ return hr;
+
+}
+
+//-----------------------------------------------------------------------------
+// We have to go the the EE to find out if a class is a value
+// class or not. This is because there is no flag for this, but rather
+// it depends on whether the class subclasses System.ValueType (apart
+// from System.Enum...). Replicating all that resoultion logic
+// does not seem like a good plan.
+//
+// We also accept other "evidence" that the class is or isn't a VC, in
+// particular:
+// - It is definitely a VC if it has been used after a
+// E_T_VALUETYPE in a signature.
+// - It is definitely not a VC if it has been used after a
+// E_T_CLASS in a signature.
+// - It is definitely a VC if it has been used in combination with
+// E_T_VALUETYPE in one of COM API operations that take both
+// a ICorDebugClass and a CorElementType (e.g. GetParameterizedType)
+//
+// !!!Note the following!!!!
+// - A class may still be a VC even if it has been
+// used in combination with E_T_CLASS in one of COM API operations that take both
+// a ICorDebugClass and a CorElementType (e.g. GetParameterizedType).
+// We allow the user of the API to specify E_T_CLASS when the VC status
+// is not known or is not important.
+//
+// Return Value:
+// indicates whether this is a value-class
+//
+// Notes:
+// Throws CORDBG_E_CLASS_NOT_LOADED or synchronization errors on failure
+//-----------------------------------------------------------------------------
+bool CordbClass::IsValueClass()
+{
+ INTERNAL_API_ENTRY(this);
+ THROW_IF_NEUTERED(this);
+
+ if (!m_fIsValueClassKnown)
+ {
+ ATT_REQUIRE_STOPPED_MAY_FAIL_OR_THROW(GetProcess(), ThrowHR);
+ Init();
+ }
+ return m_fIsValueClass;
+}
+
+//-----------------------------------------------------------------------------
+// Get a CordbType for the 'this' pointer of a method in a CordbClass.
+// The 'this' pointer is argument #0 in an instance method.
+//
+// For ReferenceTypes (ELEMENT_TYPE_CLASS), the 'this' pointer is just a
+// normal reference, and so GetThisType() behaves like GetParameterizedType().
+// For ValueTypes, the 'this' pointer is a byref.
+//
+// Arguments:
+// pInst - instantiation info (eg, the type parameters) to produce CordbType
+// ppResultType - OUT: out parameter to hold outgoing CordbType.
+//
+// Returns:
+// S_OK on success. Else failure.
+//
+HRESULT CordbClass::GetThisType(const Instantiation * pInst, CordbType ** ppResultType)
+{
+ FAIL_IF_NEUTERED(this);
+
+ HRESULT hr = S_OK;
+ // Note: We have to call Init() here to find out if it really a VC or not.
+ bool fIsValueClass = false;
+ EX_TRY
+ {
+ fIsValueClass = IsValueClass();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ if (fIsValueClass)
+ {
+ CordbType *pType;
+
+ hr = CordbType::MkType(GetAppDomain(), // OK: this E_T_VALUETYPE will be normalized by MkType
+ ELEMENT_TYPE_VALUETYPE,
+ this,
+ pInst,
+ &pType);
+
+ if (!SUCCEEDED(hr))
+ {
+ return hr;
+ }
+
+ hr = CordbType::MkType(GetAppDomain(), ELEMENT_TYPE_BYREF, 0, pType, ppResultType);
+
+ if (!SUCCEEDED(hr))
+ {
+ return hr;
+ }
+ }
+ else
+ {
+ hr = CordbType::MkType(GetAppDomain(), ELEMENT_TYPE_CLASS, this, pInst, ppResultType);
+
+ if (!SUCCEEDED(hr))
+ {
+ return hr;
+ }
+ }
+
+ return hr;
+}
+
+
+//-----------------------------------------------------------------------------
+// Initialize the CordbClass.
+// This will collect all the field information via the DAC, and also determine
+// whether this Type is a ReferenceType or ValueType.
+//
+// Parameters:
+// fForceInit - if true, always reinitialize. If false, may skip
+// initialization if we believe we already have the info.
+//
+// Note:
+// Throws CORDBG_E_CLASS_NOT_LOADED on failure
+//-----------------------------------------------------------------------------
+void CordbClass::Init(ClassLoadLevel desiredLoadLevel)
+{
+ INTERNAL_SYNC_API_ENTRY(this->GetProcess());
+
+ CordbProcess * pProcess = GetProcess();
+ IDacDbiInterface* pDac = pProcess->GetDAC();
+
+ // If we've done a continue since the last time we got hanging static fields,
+ // we should clear out our cache, since everything may have moved.
+ if (m_continueCounterLastSync < GetProcess()->m_continueCounter)
+ {
+ m_hangingFieldsStatic.Clear();
+ m_continueCounterLastSync = GetProcess()->m_continueCounter;
+ }
+
+ if (m_loadLevel < desiredLoadLevel)
+ {
+ // reset everything
+ m_loadLevel = Constructed;
+ m_fIsValueClass = false;
+ m_fIsValueClassKnown = false;
+ m_fHasTypeParams = false;
+ m_classInfo.Clear();
+ // @dbgtodo Microsoft inspection: declare a constant to replace badbad
+ m_classInfo.m_objectSize = 0xbadbad;
+ VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr();
+
+ // basic info load level
+ if(desiredLoadLevel >= BasicInfo)
+ {
+ vmTypeHandle = pDac->GetTypeHandle(m_pModule->GetRuntimeModule(), GetToken());
+ SetIsValueClass(pDac->IsValueType(vmTypeHandle));
+ m_fHasTypeParams = !!pDac->HasTypeParams(vmTypeHandle);
+ m_loadLevel = BasicInfo;
+ }
+
+ // full info load level
+ if(desiredLoadLevel == FullInfo)
+ {
+ VMPTR_AppDomain vmAppDomain = VMPTR_AppDomain::NullPtr();
+ VMPTR_DomainFile vmDomainFile = m_pModule->GetRuntimeDomainFile();
+ if (!vmDomainFile.IsNull())
+ {
+ DomainFileInfo info;
+ pDac->GetDomainFileData(vmDomainFile, &info);
+ vmAppDomain = info.vmAppDomain;
+ }
+ pDac->GetClassInfo(vmAppDomain, vmTypeHandle, &m_classInfo);
+
+ BOOL fGotUnallocatedStatic = GotUnallocatedStatic(&m_classInfo.m_fieldList);
+
+ // if we have an unallocated static don't record that we reached FullInfo stage
+ // this seems pretty ugly but I don't want to bite off cleaning this up just yet
+ // Not saving the FullInfo stage effectively means future calls to Init() will
+ // re-init everything and some parts of DBI may be depending on that re-initialization
+ // with alternate data in order to operate correctly
+ if(!fGotUnallocatedStatic)
+ m_loadLevel = FullInfo;
+ }
+ }
+} // CordbClass::Init
+
+// determine if any fields for a type are unallocated statics
+BOOL CordbClass::GotUnallocatedStatic(DacDbiArrayList<FieldData> * pFieldList)
+{
+ BOOL fGotUnallocatedStatic = FALSE;
+ int count = 0;
+ while ((count < pFieldList->Count()) && !fGotUnallocatedStatic )
+ {
+ if ((*pFieldList)[count].OkToGetOrSetStaticAddress() &&
+ (*pFieldList)[count].GetStaticAddress() == NULL )
+ {
+ // The address for a regular static field isn't available yet
+ // How can this happen? Statics appear to get allocated during domain load.
+ // There may be some lazieness or a race-condition involved.
+ fGotUnallocatedStatic = TRUE;
+ }
+ ++count;
+ }
+ return fGotUnallocatedStatic;
+} // CordbClass::GotUnallocatedStatic
+
+/*
+ * FieldData::GetFieldSignature
+ *
+ * Get the field's full metadata signature. This may be cached, but for dynamic modules we'll always read it from
+ * the metadata.
+ *
+ * Parameters:
+ * pModule - pointer to the module that contains the field
+ *
+ * pSigParser - OUT: the full signature for the field.
+ *
+ * Returns:
+ * HRESULT for success or failure.
+ *
+ */
+HRESULT FieldData::GetFieldSignature(CordbModule *pModule,
+ SigParser *pSigParser)
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ INTERNAL_SYNC_API_ENTRY(pModule->GetProcess());
+
+ HRESULT hr = S_OK;
+
+ IMetaDataImport * pImport = pModule->GetMetaDataImporter(); // throws;
+
+ PCCOR_SIGNATURE fieldSignature = NULL;
+ ULONG size = ((ULONG) -1);
+
+ _ASSERTE(pSigParser != NULL);
+
+ // If the module is dynamic, there had better not be a cached field signature.
+ _ASSERTE(!pModule->IsDynamic() || (m_fldSignatureCache == NULL));
+
+ // If the field signature cache is null, or if this is a dynamic module, then go read the signature from the
+ // matadata. We always read from the metadata for dynamic modules because our metadata blob is constantly
+ // getting deleted and re-allocated. If we kept a pointer to the signature, we'd end up pointing to bad data.
+ if (m_fldSignatureCache == NULL)
+ {
+ // Go to the metadata for all fields: previously the left-side tranferred over
+ // single-byte signatures as part of the field info. Since the left-side
+ // goes to the metadata anyway, and we already fetch plenty of other metadata,
+ // I don't believe that fetching it here instead of transferring it over
+ // is going to slow things down at all, and
+ // in any case will not be where the primary optimizations lie...
+
+ IfFailRet(pImport->GetFieldProps(m_fldMetadataToken, NULL, NULL, 0, NULL, NULL,
+ &fieldSignature,
+ &size,
+ NULL, NULL, NULL));
+
+ // Point past the calling convention
+ CorCallingConvention conv;
+
+ // Move pointer,
+ BYTE * pOldPtr = (BYTE*) fieldSignature;
+ conv = (CorCallingConvention) CorSigUncompressData(fieldSignature);
+ _ASSERTE(conv == IMAGE_CEE_CS_CALLCONV_FIELD);
+ size -= (ULONG) (((BYTE*) fieldSignature) - pOldPtr); // since we updated filedSignature, adjust size
+
+ // Although the pointer will keep updating, the size should be the same. So we assert that.
+ _ASSERTE((m_fldSignatureCacheSize == 0) || (m_fldSignatureCacheSize == size));
+
+ // Cache the value for non-dynamic modules, so this is faster later.
+ // Since we're caching in a FieldData, we can't store the actual SigParser object.
+ if (!pModule->IsDynamic())
+ {
+ m_fldSignatureCache = fieldSignature;
+ m_fldSignatureCacheSize = size;
+ }
+ }
+ else
+ {
+ // We have a cached value, so return it. Note: we should never have a cached value for a field in a dynamic
+ // module.
+ CONSISTENCY_CHECK_MSGF((!pModule->IsDynamic()),
+ ("We should never cache a field signature in a dynamic module! Module=%p This=%p",
+ pModule, this));
+
+ fieldSignature = m_fldSignatureCache;
+ size = m_fldSignatureCacheSize;
+ }
+
+ _ASSERTE(fieldSignature != NULL);
+ _ASSERTE(size != ((ULONG) -1));
+ *pSigParser = SigParser(fieldSignature, size);
+ return hr;
+}
+
+// CordbClass::InitEnCFieldInfo
+// Initializes an instance of EnCHangingFieldInfo.
+// Arguments:
+// input: fStatic - flag to indicate whether the EnC field is static
+// pObject - For instance fields, the Object instance containing the the sync-block.
+// For static fields (if this is being called from GetStaticFieldValue) object is NULL.
+// fieldToken - token for the EnC field
+// metadataToken - metadata token for this instance of CordbClass
+// output: pEncField - the fields of this class will be appropriately initialized
+void CordbClass::InitEnCFieldInfo(EnCHangingFieldInfo * pEncField,
+ BOOL fStatic,
+ CordbObjectValue * pObject,
+ mdFieldDef fieldToken,
+ mdTypeDef classToken)
+{
+ IDacDbiInterface * pInterface = GetProcess()->GetDAC();
+
+ if (fStatic)
+ {
+ // the field is static, we don't need any additional data
+ pEncField->Init(VMPTR_Object::NullPtr(), /* vmObject */
+ NULL, /* offsetToVars */
+ fieldToken,
+ ELEMENT_TYPE_MAX,
+ classToken,
+ m_pModule->GetRuntimeDomainFile());
+ }
+ else
+ {
+ // This is an instance field, we need to pass a bunch of type information back
+ _ASSERTE(pObject != NULL);
+
+ pEncField->Init(pInterface->GetObject(pObject->m_id), // VMPTR to the object instance of interest.
+ pObject->GetInfo().objOffsetToVars, // The offset from the beginning of the object
+ // to the beginning of the fields. Fields added
+ // with EnC don't actually reside in the object
+ // (they hang off the sync block instead), so
+ // this is used to compute the returned field
+ // offset (fieldData.m_fldInstanceOffset). This
+ // makes it appear to be an offset from the object.
+ // Ideally we wouldn't do any of this, and just
+ // explicitly deal with absolute addresses (instead
+ // of "offsets") for EnC hanging instance fields.
+ fieldToken, // Field token for the added field.
+ pObject->GetInfo().objTypeData.elementType, // An indication of the type of object to which
+ // we're adding a field (specifically,
+ // whether it's a value type or a class).
+ // This is used only for log messages, and could
+ // be removed.
+ classToken, // metadata token for the class
+ m_pModule->GetRuntimeDomainFile()); // Domain file for the class
+ }
+} // CordbClass::InitFieldData
+
+// CordbClass::GetEnCFieldFromDac
+// Get information via the DAC about a field added with Edit and Continue.
+// Arguments:
+// input: fStatic - flag to indicate whether the EnC field is static
+// pObject - For instance fields, the Object instance containing the the sync-block.
+// For static fields (if this is being called from GetStaticFieldValue) object is NULL.
+// fieldToken - token for the EnC field
+// output: pointer to an initialized instance of FieldData that has been added to the appropriate table
+// in our cache
+FieldData * CordbClass::GetEnCFieldFromDac(BOOL fStatic,
+ CordbObjectValue * pObject,
+ mdFieldDef fieldToken)
+{
+ EnCHangingFieldInfo encField;
+ mdTypeDef metadataToken;
+ FieldData fieldData,
+ * pInfo = NULL;
+ BOOL fDacStatic;
+ CordbProcess * pProcess = GetModule()->GetProcess();
+
+ _ASSERTE(pProcess != NULL);
+ IfFailThrow(GetToken(&metadataToken));
+ InitEnCFieldInfo(&encField, fStatic, pObject, fieldToken, metadataToken);
+
+ // Go get this particular field.
+ pProcess->GetDAC()->GetEnCHangingFieldInfo(&encField, &fieldData, &fDacStatic);
+ _ASSERTE(fStatic == fDacStatic);
+
+ // Save the field results in our cache and get a stable pointer to the data
+ if (fStatic)
+ {
+ pInfo = m_hangingFieldsStatic.AddFieldInfo(&fieldData);
+ }
+ else
+ {
+ pInfo = pObject->GetHangingFieldTable()->AddFieldInfo(&fieldData);
+ }
+
+ // We should have a fresh copy of the data (don't want to return a pointer to data on our stack)
+ _ASSERTE((void *)pInfo != (void *)&fieldData);
+ _ASSERTE(pInfo->m_fFldIsStatic == (fStatic == TRUE));
+ _ASSERTE(pInfo->m_fldMetadataToken == fieldToken);
+
+ // Pass a pointer to the data out.
+ return pInfo;
+} // CordbClass::GetEnCFieldFromDac
+
+//-----------------------------------------------------------------------------
+// Internal helper to get a FieldData for fields added by EnC after the type
+// was loaded. Since object and MethodTable layout has already been fixed,
+// such added fields are "hanging" off some other data structure. For instance
+// fields, they're stored in a syncblock off the object. For static fields
+// they're stored off the EnCFieldDesc.
+//
+// The caller must have already determined this is a hanging field (i.e.
+// GetFieldInfo returned CORDBG_E_ENC_HANGING_FIELDF).
+//
+// Arguments:
+// input: fldToken - field of interest to get.
+// pObject - For instance fields, the Object instance containing the the sync-block.
+// For static fields (if this is being called from GetStaticFieldValue) object is NULL.
+// output: ppFieldData - the FieldData matching the fldToken.
+//
+// Returns:
+// S_OK on success, failure code otherwise.
+//-----------------------------------------------------------------------------
+HRESULT CordbClass::GetEnCHangingField(mdFieldDef fldToken,
+ FieldData **ppFieldData,
+ CordbObjectValue * pObject)
+{
+ FAIL_IF_NEUTERED(this);
+ INTERNAL_SYNC_API_ENTRY(GetProcess());
+
+ HRESULT hr = S_OK;
+ _ASSERTE(pObject == NULL || !pObject->IsNeutered() );
+
+ if (HasTypeParams())
+ {
+ _ASSERTE(!"EnC hanging field not yet implemented on constructed types!");
+ return E_FAIL;
+ }
+
+ // This must be a static field if no object was supplied
+ BOOL fStatic = (pObject == NULL);
+
+ // Look for cached field information
+ FieldData *pInfo = NULL;
+ if (fStatic)
+ {
+ // Static fields should _NOT_ be cleared, since they stick around. Thus
+ // the separate tables.
+ pInfo = m_hangingFieldsStatic.GetFieldInfo(fldToken);
+ }
+ else
+ {
+ // We must get new copies each time we call continue b/c we get the
+ // actual Object ptr from the left side, which can move during a GC.
+ pInfo = pObject->GetHangingFieldTable()->GetFieldInfo(fldToken);
+ }
+
+ // We've found a previously located entry
+ if (pInfo != NULL)
+ {
+ *ppFieldData = pInfo;
+ return S_OK;
+ }
+
+ // Field information not already available - go get it
+ EX_TRY
+ {
+
+ // We're not going to be able to get the instance-specific field
+ // if we can't get the instance.
+ if (!fStatic && pObject->GetInfo().objRefBad)
+ {
+ ThrowHR(CORDBG_E_INVALID_OBJECT);
+ }
+
+ *ppFieldData = GetEnCFieldFromDac(fStatic, pObject, fldToken);
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+}
+
+//-----------------------------------------------------------------------------
+// Get a FieldData (which rich information, including details about storage)
+// from a metadata token.
+//
+// Parameters:
+// fldToken - incoming metadata token specifying the field.
+// ppFieldData - OUT: resulting FieldData structure.
+//
+// Returns:
+// S_OK on success. else failure.
+//-----------------------------------------------------------------------------
+HRESULT CordbClass::GetFieldInfo(mdFieldDef fldToken, FieldData **ppFieldData)
+{
+ INTERNAL_SYNC_API_ENTRY(GetProcess());
+
+ Init();
+ return SearchFieldInfo(GetModule(), &m_classInfo.m_fieldList, m_token, fldToken, ppFieldData);
+}
+
+
+//-----------------------------------------------------------------------------
+// Search an array of FieldData (pFieldList) for a field (fldToken).
+// The FieldData array must match the class supplied by classToken, and live
+// in the supplied module.
+//
+// Internal helper used by CordbType::GetFieldInfo, CordbClass::GetFieldInfo
+//
+// Parameters:
+// module - module containing the class that the FieldData array matches.
+// pFieldList - array of fields to search through and the number of elements in
+// the array.
+// classToken - class that the data array matches. class must live in
+// the supplied moudle.
+// fldToken - metadata token of the field to search for. This field should be
+// on the class supplied by classToken.
+//
+// Returns:
+// CORDBG_E_ENC_HANGING_FIELD for "hanging fields" (fields added via Enc) (common error).
+// Returns S_OK, set ppFieldData = pointer into data array for matching field. (*retval)->m_fldMetadataToken == fldToken)
+// Throws on other errors.
+//-----------------------------------------------------------------------------
+/* static */
+HRESULT CordbClass::SearchFieldInfo(
+ CordbModule * pModule,
+ DacDbiArrayList<FieldData> * pFieldList,
+ mdTypeDef classToken,
+ mdFieldDef fldToken,
+ FieldData **ppFieldData
+)
+{
+ int i;
+
+ IMetaDataImport * pImport = pModule->GetMetaDataImporter(); // throws
+
+ HRESULT hr = S_OK;
+ for (i = 0; i < pFieldList->Count(); i++)
+ {
+ if ((*pFieldList)[i].m_fldMetadataToken == fldToken)
+ {
+ // If the storage for this field isn't yet available (i.e. it is newly added with EnC)
+ if (!(*pFieldList)[i].m_fFldStorageAvailable)
+ {
+ // If we're a static literal, then return special HR to let
+ // debugger know that it should look it up via the metadata.
+ // Check m_fFldIsStatic first b/c that's fast.
+ if ((*pFieldList)[i].m_fFldIsStatic)
+ {
+ if (IsFieldStaticLiteral(pImport, fldToken))
+ {
+ ThrowHR(CORDBG_E_VARIABLE_IS_ACTUALLY_LITERAL);
+ }
+ }
+
+ // This is a field added by EnC, caller needs to get instance-specific info.
+ return CORDBG_E_ENC_HANGING_FIELD;
+ }
+
+ *ppFieldData = &((*pFieldList)[i]);
+ return S_OK;
+ }
+ }
+
+ // Hmmm... we didn't find the field on this class. See if the field really belongs to this class or not.
+ mdTypeDef classTok;
+
+ hr = pImport->GetFieldProps(fldToken, &classTok, NULL, 0, NULL, NULL, NULL, 0, NULL, NULL, NULL);
+ IfFailThrow(hr);
+
+ if (classTok == (mdTypeDef) classToken)
+ {
+ // Well, the field belongs in this class. The assumption is that the Runtime optimized the field away.
+ ThrowHR(CORDBG_E_FIELD_NOT_AVAILABLE);
+ }
+
+ // Well, the field doesn't even belong to this class...
+ ThrowHR(E_INVALIDARG);
+}
+