summaryrefslogtreecommitdiff
path: root/src/debug/di/divalue.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/di/divalue.cpp')
-rw-r--r--src/debug/di/divalue.cpp4564
1 files changed, 4564 insertions, 0 deletions
diff --git a/src/debug/di/divalue.cpp b/src/debug/di/divalue.cpp
new file mode 100644
index 0000000000..50ecd68aa9
--- /dev/null
+++ b/src/debug/di/divalue.cpp
@@ -0,0 +1,4564 @@
+// 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: DIValue.cpp
+//
+
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "primitives.h"
+
+// copy from a MemoryRange to dest
+// Arguments:
+// input: source - MemoryRange describing the start address and size of the source buffer
+// output: dest - address of the buffer to which the source buffer is copied
+// Note: the buffer for dest must be allocated by the caller and must be large enough to hold the
+// bytes from the source buffer.
+void localCopy(void * dest, MemoryRange source)
+{
+ _ASSERTE(dest != NULL);
+ _ASSERTE(source.StartAddress() != NULL);
+
+ memcpy(dest, source.StartAddress(), source.Size());
+}
+
+// for an inheritance graph of the ICDValue types, // See file:./ICorDebugValueTypes.vsd for a diagram of the types.
+
+/* ------------------------------------------------------------------------- *
+ * CordbValue class
+ * ------------------------------------------------------------------------- */
+
+CordbValue::CordbValue(CordbAppDomain * appdomain,
+ CordbType * type,
+ CORDB_ADDRESS id,
+ bool isLiteral,
+ NeuterList * pList)
+ : CordbBase(
+ ((appdomain != NULL) ? (appdomain->GetProcess()) : (type->GetProcess())),
+ (UINT_PTR)id, enumCordbValue),
+ m_appdomain(appdomain),
+ m_type(type), // implicit InternalAddRef
+ //m_sigCopied(false),
+ m_size(0),
+ m_isLiteral(isLiteral)
+{
+ HRESULT hr = S_OK;
+
+ _ASSERTE(GetProcess() != NULL);
+
+ // Add to a neuter list. If none is provided, use the ExitProcess list as a default.
+ // The main neuter lists of interest here are:
+ // - CordbProcess::GetContinueNeuterList() - Shortest. Neuter when the process continues.
+ // - CordbAppDomain::GetExitNeuterList() - Middle. Neuter when the AD exits. Since most Values (except globals) are in
+ // a specific AD, this almost catches all; and keeps us safe in AD-unload scenarios.
+ // - CordbProcess::GetExitNeuterList() - Worst. Doesn't neuter until the process exits (or we detach).
+ // This could be a long time.
+ if (pList == NULL)
+ {
+ pList = GetProcess()->GetExitNeuterList();
+ }
+
+
+ EX_TRY
+ {
+ pList->Add(GetProcess(), this);
+ }
+ EX_CATCH_HRESULT(hr);
+ SetUnrecoverableIfFailed(GetProcess(), hr);
+} // CordbValue::CordbValue
+
+CordbValue::~CordbValue()
+{
+ DTOR_ENTRY(this);
+
+ _ASSERTE(this->IsNeutered());
+
+ _ASSERTE(m_type == NULL);
+} // CordbValue::~CordbValue
+
+void CordbValue::Neuter()
+{
+ m_appdomain = NULL;
+ m_type.Clear();
+
+ ValueHome * pValueHome = GetValueHome();
+ if (pValueHome != NULL)
+ {
+ pValueHome->Clear();
+ }
+ CordbBase::Neuter();
+} // CordbValue::Neuter
+
+// Helper for code:CordbValue::CreateValueByType. Create a new instance of CordbGenericValue
+// Arguments:
+// input: pAppdomain - appdomain to which the value belongs
+// pType - type of the value
+// remoteValue - remote address and size of the value
+// localValue - local address and size of the value
+// ppRemoteRegAddr - register address of the value
+// output: ppValue - the newly created instance of an ICDValue
+// Notes:
+// - only one of the three locations will be non-NULL
+// - Throws
+/* static */
+void CordbValue::CreateGenericValue(CordbAppDomain * pAppdomain,
+ CordbType * pType,
+ TargetBuffer remoteValue,
+ MemoryRange localValue,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr,
+ ICorDebugValue** ppValue)
+{
+ LOG((LF_CORDB,LL_INFO100000,"CV::CreateValueByType CreateGenericValue\n"));
+ RSSmartPtr<CordbGenericValue> pGenValue;
+ // A generic value
+ // By using a RSSmartPtr we ensure that in both success and failure cases,
+ // this object is cleaned up properly (deleted or not depending on ref counts).
+ // Specifically, the object has probably been placed on a neuter list so we
+ // can't delete it (but this is a detail we shouldn't rely on)
+ pGenValue.Assign(new CordbGenericValue(pAppdomain,
+ pType,
+ remoteValue,
+ ppRemoteRegAddr));
+
+ pGenValue->Init(localValue); // throws
+
+ pGenValue->AddRef();
+ *ppValue = (ICorDebugValue *)(ICorDebugGenericValue *)pGenValue;
+} // CordbValue::CreateGenericValue
+
+// create a new instance of CordbVCObjectValue or CordbReferenceValue
+// Arguments:
+// input: pAppdomain - appdomain to which the value belongs
+// pType - type of the value
+// boxed - indicates whether the value is boxed
+// remoteValue - remote address and size of the value
+// localValue - local address and size of the value
+// ppRemoteRegAddr - register address of the value
+// output: ppValue - the newly created instance of an ICDValue
+// Notes:
+// - only one of the three locations will be non-NULL
+// - Throws error codes from reading process memory
+/* static */
+void CordbValue::CreateVCObjOrRefValue(CordbAppDomain * pAppdomain,
+ CordbType * pType,
+ bool boxed,
+ TargetBuffer remoteValue,
+ MemoryRange localValue,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr,
+ ICorDebugValue** ppValue)
+
+{
+ HRESULT hr = S_OK;
+ LOG((LF_CORDB,LL_INFO1000000,"CV::CreateValueByType Creating ReferenceValue\n"));
+
+ // We either have a boxed or unboxed value type, or we have a value that's not a value type.
+ // For an unboxed value type, we'll create an instance of CordbVCObjectValue. Otherwise, we'll
+ // create an instance of CordbReferenceValue.
+
+ // do we have a value type?
+ bool isVCObject = pType->IsValueType(); // throws
+
+ if (!boxed && isVCObject)
+ {
+ RSSmartPtr<CordbVCObjectValue> pVCValue(new CordbVCObjectValue(pAppdomain,
+ pType,
+ remoteValue,
+ ppRemoteRegAddr));
+
+ IfFailThrow(pVCValue->Init(localValue));
+
+ pVCValue->AddRef();
+ *ppValue = (ICorDebugValue*)(ICorDebugObjectValue*)pVCValue;
+ }
+ else
+ {
+ // either the value is boxed or it's not a value type
+ RSSmartPtr<CordbReferenceValue> pRef;
+ hr = CordbReferenceValue::Build(pAppdomain,
+ pType,
+ remoteValue,
+ localValue,
+ VMPTR_OBJECTHANDLE::NullPtr(),
+ ppRemoteRegAddr, // Home
+ &pRef);
+ IfFailThrow(hr);
+ hr = pRef->QueryInterface(__uuidof(ICorDebugValue), (void**)ppValue);
+ _ASSERTE(SUCCEEDED(hr));
+ }
+} // CordbValue::CreateVCObjOrRefValue
+
+//
+// Create the proper ICDValue instance based on the given element type.
+// Arguments:
+// input: pAppdomain - appdomain to which the value belongs
+// pType - type of the value
+// boxed - indicates whether the value is boxed
+// remoteValue - remote address and size of the value
+// localValue - local address and size of the value
+// ppRemoteRegAddr - register address of the value
+// output: ppValue - the newly created instance of an ICDValue
+// Notes:
+// - Only one of the three locations, remoteValue, localValue or ppRemoteRegAddr, will be non-NULL.
+// - Throws.
+/*static*/ void CordbValue::CreateValueByType(CordbAppDomain * pAppdomain,
+ CordbType * pType,
+ bool boxed,
+ TargetBuffer remoteValue,
+ MemoryRange localValue,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr,
+ ICorDebugValue** ppValue)
+{
+ INTERNAL_SYNC_API_ENTRY(pAppdomain->GetProcess()); //
+
+ // We'd really hope that our callers give us a valid appdomain, but in case
+ // they don't, we'll fail gracefully.
+ if ((pAppdomain != NULL) && pAppdomain->IsNeutered())
+ {
+ STRESS_LOG1(LF_CORDB, LL_EVERYTHING, "CVBT using neutered AP, %p\n", pAppdomain);
+ ThrowHR(E_INVALIDARG);
+ }
+
+ LOG((LF_CORDB,LL_INFO100000,"CV::CreateValueByType\n"));
+
+ *ppValue = NULL;
+
+ switch(pType->m_elementType)
+ {
+ case ELEMENT_TYPE_BOOLEAN:
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_R4:
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_R8:
+ case ELEMENT_TYPE_I:
+ case ELEMENT_TYPE_U:
+ {
+ CreateGenericValue(pAppdomain, pType, remoteValue, localValue, ppRemoteRegAddr, ppValue); // throws
+ break;
+ }
+
+ case ELEMENT_TYPE_CLASS:
+ case ELEMENT_TYPE_OBJECT:
+ case ELEMENT_TYPE_STRING:
+ case ELEMENT_TYPE_PTR:
+ case ELEMENT_TYPE_BYREF:
+ case ELEMENT_TYPE_TYPEDBYREF:
+ case ELEMENT_TYPE_ARRAY:
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_FNPTR:
+ {
+ CreateVCObjOrRefValue(pAppdomain, pType, boxed, remoteValue, localValue, ppRemoteRegAddr, ppValue); // throws
+ break;
+ }
+
+ default:
+ _ASSERTE(!"Bad value type!");
+ ThrowHR(E_FAIL);
+ }
+} // CordbValue::CreateValueByType
+
+// Create the proper ICDValue instance based on the given remote heap object
+// Arguments:
+// pAppDomain - the app domain the remote object is in
+// vmObj - the remote object to get an ICDValue for
+ICorDebugValue* CordbValue::CreateHeapValue(CordbAppDomain* pAppDomain, VMPTR_Object vmObj)
+{
+ IDacDbiInterface* pDac = pAppDomain->GetProcess()->GetDAC();
+
+ TargetBuffer objBuffer = pDac->GetObjectContents(vmObj);
+ VOID* pRemoteAddr = CORDB_ADDRESS_TO_PTR(objBuffer.pAddress);
+ // This creates a local reference that has a remote address in it. Ie &pRemoteAddr is an address
+ // in the host address space and pRemoteAddr is an address in the target.
+ MemoryRange localReferenceDescription(&pRemoteAddr, sizeof(pRemoteAddr));
+ RSSmartPtr<CordbReferenceValue> pRefValue;
+ IfFailThrow(CordbReferenceValue::Build(pAppDomain,
+ NULL,
+ EMPTY_BUFFER,
+ localReferenceDescription,
+ VMPTR_OBJECTHANDLE::NullPtr(),
+ NULL,
+ &pRefValue));
+
+ // Dereference our temporary reference value to construct the heap value we want
+ ICorDebugValue* pExtValue;
+ IfFailThrow(pRefValue->Dereference(&pExtValue));
+ return pExtValue;
+}
+
+// Gets the size om bytes of a value from its type. If the value is complex, we assume it is represented as
+// a reference, since this is called for values that have been found on the stack, as an element of an
+// array (represented as CordbArrayValue) or field of an object (CordbObjectValue) or the result of a
+// func eval. For unboxed value types, we get the size of the entire value (it is not represented as a
+// reference).
+// Examples:
+// - int on the stack
+// => sizeof(int)
+// - int as a field in an object on the heap
+// =>sizeof(int)
+// - Boxed int on the heap
+// => size of a pointer
+// - Class Point { int x; int y}; // class will have a method table / object header which may increase size.
+// => size of a pointer
+// - Struct Point {int x; int y; }; // unboxed struct may not necessarily have the object header.
+// => 2 * sizeof(int)
+// - List<int>
+// => size of a pointer
+// Arguments: pType - the type of the value
+// boxing - indicates whether the value is boxed or not
+// Return Value: the size of the value
+// Notes: Throws
+// In general, this returns the unboxed size of the value, but if we have a type
+// that represents a non-generic and it's not an unboxed value type, we know that
+// it will be represented as a reference, so we return the size of a pointer instead.
+/* static */
+ULONG32 CordbValue::GetSizeForType(CordbType * pType, BoxedValue boxing)
+{
+ ULONG32 size = 0;
+
+ switch(pType->m_elementType)
+ {
+ case ELEMENT_TYPE_BOOLEAN:
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_R4:
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_R8:
+ case ELEMENT_TYPE_I:
+ case ELEMENT_TYPE_U: pType->GetUnboxedObjectSize(&size); break;
+
+ case ELEMENT_TYPE_CLASS:
+ case ELEMENT_TYPE_OBJECT:
+ case ELEMENT_TYPE_STRING:
+ case ELEMENT_TYPE_PTR:
+ case ELEMENT_TYPE_BYREF:
+ case ELEMENT_TYPE_TYPEDBYREF:
+ case ELEMENT_TYPE_ARRAY:
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_FNPTR: {
+ bool isUnboxedVCObject = false;
+
+ if (boxing == kUnboxed)
+ {
+ isUnboxedVCObject = pType->IsValueType(); // throws
+ }
+ if (!isUnboxedVCObject)
+ {
+ // if it's not an unboxed value type (we're in the case
+ // for compound types), then it's a reference
+ // and we just want to return the size of a pointer
+ size = sizeof(void *);
+ }
+ else
+ {
+ pType->GetUnboxedObjectSize(&size);
+ }
+ } break;
+
+ default:
+ _ASSERTE(!"Bad value type!");
+}
+ return size;
+} // CordbValue::GetSizeForType
+
+
+HRESULT CordbValue::CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint)
+{
+ VALIDATE_POINTER_TO_OBJECT(ppBreakpoint, ICorDebugValueBreakpoint **);
+
+ return E_NOTIMPL;
+} // CordbValue::CreateBreakpoint
+
+// gets the exact type of a value
+// Arguments:
+// input: none (uses m_type field)
+// output: ppType - an instance of ICDType representing the exact type of the value
+// Return Value:
+HRESULT CordbValue::GetExactType(ICorDebugType **ppType)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(ppType, ICorDebugType **);
+ FAIL_IF_NEUTERED(this);
+
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ *ppType = static_cast<ICorDebugType*> (m_type);
+
+ if (*ppType != NULL)
+ (*ppType)->AddRef();
+
+ return S_OK;
+} // CordbValue::GetExactType
+
+// CreateHandle for a heap object.
+// @todo: How to prevent this being called by non-heap object?
+// Arguments:
+// input: handleType - type of the handle to be created
+// output: ppHandle - on success, the newly created handle
+// Return Value: S_OK on success or E_INVALIDARG, E_OUTOFMEMORY, or CORDB_E_HELPER_MAY_DEADLOCK
+HRESULT CordbValue::InternalCreateHandle(CorDebugHandleType handleType,
+ ICorDebugHandleValue ** ppHandle)
+{
+ INTERNAL_SYNC_API_ENTRY(GetProcess());
+ LOG((LF_CORDB,LL_INFO1000,"CV::CreateHandle\n"));
+
+ DebuggerIPCEvent event;
+ CordbProcess *process;
+ BOOL fStrong = FALSE;
+
+ // @dbgtodo- , as part of inspection, convert this path to throwing.
+ if (ppHandle == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ *ppHandle = NULL;
+
+ if (handleType == HANDLE_STRONG)
+ {
+ fStrong = TRUE;
+ }
+ else
+ {
+ _ASSERTE(handleType == HANDLE_WEAK_TRACK_RESURRECTION);
+ }
+
+
+ // Create the ICorDebugHandleValue object
+ RSInitHolder<CordbHandleValue> pHandle(new (nothrow) CordbHandleValue(m_appdomain, m_type, handleType) );
+
+ if (pHandle == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ // Send the event to create the handle.
+ process = m_appdomain->GetProcess();
+ _ASSERTE(process != NULL);
+
+ process->InitIPCEvent(&event,
+ DB_IPCE_CREATE_HANDLE,
+ true,
+ m_appdomain->GetADToken());
+
+ CORDB_ADDRESS addr = GetValueHome() != NULL ? GetValueHome()->GetAddress() : NULL;
+ event.CreateHandle.objectToken = CORDB_ADDRESS_TO_PTR(addr);
+ event.CreateHandle.fStrong = fStrong;
+
+ // Note: two-way event here...
+ HRESULT hr = process->SendIPCEvent(&event, sizeof(DebuggerIPCEvent));
+ hr = WORST_HR(hr, event.hr);
+
+ if (SUCCEEDED(hr))
+ {
+ _ASSERTE(event.type == DB_IPCE_CREATE_HANDLE_RESULT);
+
+ // Initialize the handle value object.
+ hr = pHandle->Init(event.CreateHandleResult.vmObjectHandle);
+ }
+
+ if (!SUCCEEDED(hr))
+ {
+ // Free the handle from the left-side.
+ pHandle->Dispose();
+
+ // The RSInitHolder will neuter and delete it.
+ return hr;
+ }
+
+ // Pass out the new handle value object.
+ pHandle.TransferOwnershipExternal(ppHandle);
+
+ return S_OK;
+} // CordbValue::InternalCreateHandle
+
+/* ------------------------------------------------------------------------- *
+ * Generic Value class
+ * ------------------------------------------------------------------------- */
+
+//
+// CordbGenericValue constructor that builds a generic value from
+// a remote address or register.
+// Arguments:
+// input: pAppdomain - the app domain to which the value belongs
+// pType - the type of the value
+// remoteValue - buffer (and size) of the remote location where
+// the value resides. This may be NULL if the value
+// is enregistered.
+// ppRemoteRegAddr - information describing the register in which the
+// value resides. This may be NULL--only one of
+// ppRemoteRegAddr and remoteValue will be non-NULL,
+// depending on whether the value is in a register or
+// memory.
+CordbGenericValue::CordbGenericValue(CordbAppDomain * pAppdomain,
+ CordbType * pType,
+ TargetBuffer remoteValue,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr)
+ : CordbValue(pAppdomain, pType, remoteValue.pAddress, false),
+ m_pValueHome(NULL)
+{
+ _ASSERTE(pType->m_elementType != ELEMENT_TYPE_END);
+ _ASSERTE(pType->m_elementType != ELEMENT_TYPE_VOID);
+ _ASSERTE(pType->m_elementType < ELEMENT_TYPE_MAX);
+
+ // We can fill in the size now for generic values.
+ ULONG32 size;
+ HRESULT hr;
+ hr = pType->GetUnboxedObjectSize(&size);
+ _ASSERTE (!FAILED(hr));
+ m_size = size;
+
+ // now instantiate the value home
+ NewHolder<ValueHome> pHome(NULL);
+ if (remoteValue.IsEmpty())
+ {
+ pHome = (new RegisterValueHome(pAppdomain->GetProcess(), ppRemoteRegAddr));
+ }
+ else
+ {
+ pHome = (new RemoteValueHome(pAppdomain->GetProcess(), remoteValue));
+ }
+ m_pValueHome = pHome.GetValue(); // throws
+ pHome.SuppressRelease();
+} // CordbGenericValue::CordbGenericValue
+
+//
+// CordbGenericValue constructor that builds an empty generic value
+// from just an element type. Used for literal values for func evals
+// only.
+// Arguments:
+// input: pType - the type of the value
+CordbGenericValue::CordbGenericValue(CordbType * pType)
+ : CordbValue(NULL, pType, NULL, true),
+ m_pValueHome(NULL)
+{
+ // The only purpose of a literal value is to hold a RS literal value.
+ ULONG32 size;
+ HRESULT hr;
+ hr = pType->GetUnboxedObjectSize(&size);
+ _ASSERTE (!FAILED(hr));
+ m_size = size;
+
+ memset(m_pCopyOfData, 0, m_size);
+
+ // there is no value home for a literal so we leave it as NULL
+} // CordbGenericValue::CordbGenericValue
+
+// destructor
+CordbGenericValue::~CordbGenericValue()
+{
+ if (m_pValueHome != NULL)
+ {
+ delete m_pValueHome;
+ m_pValueHome = NULL;
+}
+} // CordbGenericValue::~CordbGenericValue
+
+HRESULT CordbGenericValue::QueryInterface(REFIID id, void **pInterface)
+{
+ if (id == IID_ICorDebugValue)
+ {
+ *pInterface = static_cast<ICorDebugValue*>(static_cast<ICorDebugGenericValue*>(this));
+ }
+ else if (id == IID_ICorDebugValue2)
+ {
+ *pInterface = static_cast<ICorDebugValue2*>(this);
+ }
+ else if (id == IID_ICorDebugValue3)
+ {
+ *pInterface = static_cast<ICorDebugValue3*>(this);
+ }
+ else if (id == IID_ICorDebugGenericValue)
+ {
+ *pInterface = static_cast<ICorDebugGenericValue*>(this);
+ }
+ else if (id == IID_IUnknown)
+ {
+ *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugGenericValue*>(this));
+ }
+ else
+ {
+ *pInterface = NULL;
+ return E_NOINTERFACE;
+ }
+
+ ExternalAddRef();
+ return S_OK;
+} // CordbGenericValue::QueryInterface
+
+//
+// initialize a generic value by copying the necessary data, either
+// from the remote process or from another value in this process.
+// Argument:
+// input: localValue - RS location of value to be copied. This could be NULL or it
+// could be a field from the cached copy of a CordbVCObjectValue or CordbObjectValue
+// instance or an element from the cached copy of a CordbArrayValue instance
+// Note: Throws error codes from reading process memory
+void CordbGenericValue::Init(MemoryRange localValue)
+{
+ INTERNAL_SYNC_API_ENTRY(this->GetProcess());
+
+ if(!m_isLiteral)
+ {
+ // If neither localValue.StartAddress nor m_remoteValue.pAddress are set, then all that means
+ // is that we've got a pre-initialized 64-bit value.
+ if (localValue.StartAddress() != NULL)
+ {
+ // Copy the data out of the local address space.
+ localCopy(m_pCopyOfData, localValue);
+ }
+ else
+ {
+ m_pValueHome->GetValue(MemoryRange(m_pCopyOfData, m_size)); // throws
+ }
+ }
+} // CordbGenericValue::Init
+
+// gets the value (i.e., number, boolean or pointer value) for this instance of CordbGenericValue
+// Arguments:
+// output: pTo - the starting address of a buffer in which the value will be written. This buffer must
+// be guaranteed by the caller to be large enough to hold the value. There is no way for
+// us to check here if it is. This must be non-NULL.
+// Return Value: S_OK on success or E_INVALIDARG if the pTo is NULL
+HRESULT CordbGenericValue::GetValue(void *pTo)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(pTo, BYTE, m_size, false, true);
+
+ _ASSERTE(m_pCopyOfData != NULL);
+ // Copy out the value
+ memcpy(pTo, m_pCopyOfData, m_size);
+
+ return S_OK;
+} // CordbGenericValue::GetValue
+
+// Sets the value of this instance of CordbGenericValue
+// Arguments:
+// input: pFrom - pointer to a buffer holding the new value. We assume this is the same size as the
+// original value; we have no way to check. This must be non-NULL.
+// Return Value: S_OK on success or E_INVALIDARG if the pFrom is NULL
+HRESULT CordbGenericValue::SetValue(void *pFrom)
+{
+ HRESULT hr = S_OK;
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(pFrom, BYTE, m_size, true, false);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ // We only need to send to the left side to update values that are
+ // object references. For generic values, we can simply do a write
+ // memory.
+
+ EX_TRY
+ {
+ if(!m_isLiteral)
+ {
+ m_pValueHome->SetValue(MemoryRange(pFrom, m_size), m_type); // throws
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+ IfFailRet(hr);
+
+ // That worked, so update the copy of the value we have in
+ // m_copyOfData.
+ memcpy(m_pCopyOfData, pFrom, m_size);
+
+ return hr;
+} // CordbGenericValue::SetValue
+
+// copies the value from this instance of CordbGenericValue iff the value represents a literal
+// Arguments:
+// output: pBuffer - pointer to the beginning of a caller-allocated buffer.This buffer must
+// be guaranteed by the caller to be large enough to hol
+// d the value. There is no way for
+// us to check here if it is. This must be non-NULL.
+// Return Value: true iff this is a literal value and pBuffer is a valid writeable address
+bool CordbGenericValue::CopyLiteralData(BYTE *pBuffer)
+{
+ INTERNAL_SYNC_API_ENTRY(this->GetProcess());
+ _ASSERTE(pBuffer != NULL);
+
+ // If this is a RS fabrication, copy the literal data into the
+ // given buffer and return true.
+ if (m_isLiteral)
+ {
+ _ASSERTE(m_size <= 8);
+ memcpy(pBuffer, m_pCopyOfData, m_size);
+ return true;
+ }
+ else
+ return false;
+} // CordbGenericValue::CopyLiteralData
+
+/* ------------------------------------------------------------------------- *
+ * Reference Value class
+ * ------------------------------------------------------------------------- */
+
+// constructor
+// Arguments:
+// input: pAppdomain - appdomain to which the value belongs
+// pType - the type of the referent (the object pointed to)
+// localValue - the RS address and size of the buffer from which the reference
+// will be copied. This will be NULL if either remoteValue,
+// ppRemoteRegAddr or vmObjectHandle is non-NULL. Otherwise, it will
+// point into the local cached copy of another instance of ICDValue
+// remoteValue - the LS address and size of the buffer from which the reference
+// will be copied. This will be NULL if either localValue,
+// ppRemoteRegAddr, or vmObjectHandle is non-NULL.
+// ppRemoteRegAddr - information about the register location of the buffer from which
+// the reference will be copied. This will be NULL if either localValue,
+// remoteValue, or vmObjectHandle is non-NULL.
+// vmObjectHandle - a LS object handle holding the reference. This will be NULL if either
+// localValue, remoteValue, or ppRemoteRegAddr is non-NULL.
+// Note: this may throw OOM
+CordbReferenceValue::CordbReferenceValue(CordbAppDomain * pAppdomain,
+ CordbType * pType,
+ MemoryRange localValue,
+ TargetBuffer remoteValue,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr,
+ VMPTR_OBJECTHANDLE vmObjectHandle)
+ : CordbValue(pAppdomain, pType, remoteValue.pAddress, false,
+ // We'd like to change this to be a ContinueList so it gets neutered earlier,
+ // but it may be a breaking change
+ pAppdomain->GetSweepableExitNeuterList()),
+
+ m_realTypeOfTypedByref(NULL)
+{
+ memset(&m_info, 0, sizeof(m_info));
+
+ LOG((LF_CORDB,LL_EVERYTHING,"CRV::CRV: this:0x%x\n",this));
+ m_size = sizeof(void *);
+
+ // now instantiate the value home
+ NewHolder<ValueHome> pHome(NULL);
+
+ if (!vmObjectHandle.IsNull())
+ {
+ pHome = (new HandleValueHome(pAppdomain->GetProcess(), vmObjectHandle));
+ m_valueHome.SetObjHandleFlag(false);
+ }
+
+ else if (remoteValue.IsEmpty())
+ {
+ pHome = (new RegisterValueHome(pAppdomain->GetProcess(), ppRemoteRegAddr));
+ m_valueHome.SetObjHandleFlag(true);
+
+ }
+ else
+ {
+ pHome = (new RefRemoteValueHome(pAppdomain->GetProcess(), remoteValue));
+}
+ m_valueHome.m_pHome = pHome.GetValue(); // throws
+ pHome.SuppressRelease();
+} // CordbReferenceValue::CordbReferenceValue
+
+// CordbReferenceValue constructor that builds an empty reference value
+// from just an element type. Used for literal values for func evals
+// only.
+// Arguments:
+// input: pType - the type of the value
+CordbReferenceValue::CordbReferenceValue(CordbType * pType)
+ : CordbValue(NULL, pType, NULL, true, pType->GetAppDomain()->GetSweepableExitNeuterList())
+{
+ memset(&m_info, 0, sizeof(m_info));
+
+ // The only purpose of a literal value is to hold a RS literal value.
+ m_size = sizeof(void*);
+
+ // there is no value home for a literal
+ m_valueHome.m_pHome = NULL;
+} // CordbReferenceValue::CordbReferenceValue
+
+// copies the value from this instance of CordbReferenceValue iff the value represents a literal
+// Arguments:
+// output: pBuffer - pointer to the beginning of a caller-allocated buffer.This buffer must
+// be guaranteed by the caller to be large enough to hold the value.
+// There is no way for us to check here if it is. This must be non-NULL.
+// Return Value: true iff this is a literal value and pBuffer is a valid writeable address
+bool CordbReferenceValue::CopyLiteralData(BYTE *pBuffer)
+{
+ _ASSERTE(pBuffer != NULL);
+
+ // If this is a RS fabrication, then its a null reference.
+ if (m_isLiteral)
+ {
+ void *n = NULL;
+ memcpy(pBuffer, &n, sizeof(n));
+ return true;
+ }
+ else
+ return false;
+} // CordbReferenceValue::CopyLiteralData
+
+// destructor
+CordbReferenceValue::~CordbReferenceValue()
+{
+ DTOR_ENTRY(this);
+
+ LOG((LF_CORDB,LL_EVERYTHING,"CRV::~CRV: this:0x%x\n",this));
+
+ _ASSERTE(IsNeutered());
+} // CordbReferenceValue::~CordbReferenceValue
+
+void CordbReferenceValue::Neuter()
+{
+ if (m_valueHome.m_pHome != NULL)
+ {
+ m_valueHome.m_pHome->Clear();
+ delete m_valueHome.m_pHome;
+ m_valueHome.m_pHome = NULL;
+ }
+
+ m_realTypeOfTypedByref = NULL;
+ CordbValue::Neuter();
+} // CordbReferenceValue::Neuter
+
+
+HRESULT CordbReferenceValue::QueryInterface(REFIID id, void **pInterface)
+{
+ if (id == IID_ICorDebugValue)
+ {
+ *pInterface = static_cast<ICorDebugValue*>(static_cast<ICorDebugReferenceValue*>(this));
+ }
+ else if (id == IID_ICorDebugValue2)
+ {
+ *pInterface = static_cast<ICorDebugValue2*>(this);
+ }
+ else if (id == IID_ICorDebugValue3)
+ {
+ *pInterface = static_cast<ICorDebugValue3*>(this);
+ }
+ else if (id == IID_ICorDebugReferenceValue)
+ {
+ *pInterface = static_cast<ICorDebugReferenceValue*>(this);
+ }
+ else if (id == IID_IUnknown)
+ {
+ *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugReferenceValue*>(this));
+ }
+ else
+ {
+ *pInterface = NULL;
+ return E_NOINTERFACE;
+ }
+
+ ExternalAddRef();
+ return S_OK;
+} // CordbReferenceValue::QueryInterface
+
+// gets the type of the referent of the object ref
+// Arguments:
+// output: pType - the type of the value. The caller must guarantee that pType is non-null.
+// Return Value: S_OK on success, E_INVALIDARG on failure
+HRESULT CordbReferenceValue::GetType(CorElementType *pType)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(pType, CorElementType *);
+
+ if( m_type == NULL )
+ {
+ // We may not have a CordbType if we were created from a GC handle to NULL
+ _ASSERTE( m_info.objTypeData.elementType == ELEMENT_TYPE_CLASS );
+ _ASSERTE(!m_valueHome.ObjHandleIsNull());
+ _ASSERTE( m_info.objRef == NULL );
+ *pType = m_info.objTypeData.elementType;
+ }
+ else
+ {
+ // The element type stored in both places should match
+ _ASSERTE( m_info.objTypeData.elementType == m_type->m_elementType );
+ *pType = m_type->m_elementType;
+ }
+
+ return S_OK;
+} // CordbReferenceValue::GetType
+
+// gets the remote (LS) address of the reference. This may return NULL if the
+// reference is a literal or resides in a register.
+// Arguments:
+// output: pAddress - the LS location of the reference. The caller must guarantee pAddress is non-null,
+// but the contents may be null after the call if the reference is enregistered or is
+// the value of a field or element of some other Cordb*Value instance.
+// Return Value: S_OK on success or E_INVALIDARG if pAddress is null
+HRESULT CordbReferenceValue::GetAddress(CORDB_ADDRESS *pAddress)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(pAddress, CORDB_ADDRESS *);
+
+ *pAddress = m_valueHome.m_pHome ? m_valueHome.m_pHome->GetAddress() : NULL;
+ return (S_OK);
+}
+
+// Determines whether the reference is null
+// Arguments:
+// output - pfIsNull - pointer to a BOOL that will be set to true iff this represents a
+// null reference
+// Return Value: S_OK on success or E_INVALIDARG if pfIsNull is null
+HRESULT CordbReferenceValue::IsNull(BOOL * pfIsNull)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(pfIsNull, BOOL *);
+
+ if (m_isLiteral || (m_info.objRef == NULL))
+ *pfIsNull = TRUE;
+ else
+ *pfIsNull = FALSE;
+
+ return S_OK;
+}
+
+// gets the value (object address) of this CordbReferenceValue
+// Arguments:
+// output: pTo - reference value
+// Return Value: S_OK on success or E_INVALIDARG if pAddress is null
+HRESULT CordbReferenceValue::GetValue(CORDB_ADDRESS *pAddress)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(pAddress, CORDB_ADDRESS *);
+ FAIL_IF_NEUTERED(this);
+
+ // Copy out the value, which is simply the value the object reference.
+ if (m_isLiteral)
+ *pAddress = NULL;
+ else
+ *pAddress = PTR_TO_CORDB_ADDRESS(m_info.objRef);
+
+ return S_OK;
+}
+
+// sets the value of the reference
+// Arguments:
+// input: address - the new reference--this must be a LS address
+// Return Value: S_OK on success or E_INVALIDARG or write process memory errors
+// Note: We make no effort to ensure that the new reference is of the same type as the old one.
+// We simply assume it is. As long as this assumption is correct, we only need to update information about
+// the referent if it's a string (its length can change).
+
+// @dbgtodo Microsoft inspection: consider whether it's worthwhile to verify that the type of the new referent is
+// the same as the type of the existing one. We'd have to do most of the work for a call to InitRef to do
+// this, since we need to know the type of the new referent.
+HRESULT CordbReferenceValue::SetValue(CORDB_ADDRESS address)
+{
+ PUBLIC_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ HRESULT hr = S_OK;
+
+ // If this is a heap object, ideally we'd prevent violations of AppDomain isolation
+ // here. However, we have no reliable way of determining what AppDomain the address is in.
+
+ // Can't change literal refs.
+ if (m_isLiteral)
+ {
+ return E_INVALIDARG;
+ }
+
+ // Either we know the type, or it's a handle to a null value
+ _ASSERTE((m_type != NULL) ||
+ (!m_valueHome.ObjHandleIsNull() && (m_info.objRef == NULL)));
+
+ EX_TRY
+ {
+ m_valueHome.m_pHome->SetValue(MemoryRange(&address, sizeof(void *)), m_type); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (SUCCEEDED(hr))
+ {
+ // That worked, so update the copy of the value we have in
+ // our local cache.
+ m_info.objRef = CORDB_ADDRESS_TO_PTR(address);
+
+
+ if (m_info.objTypeData.elementType == ELEMENT_TYPE_STRING)
+ {
+ // update information about the string
+ InitRef(MemoryRange(&m_info.objRef, sizeof (void *)));
+ }
+
+ // All other data in m_info is no longer valid, and we may have invalidated other
+ // ICDRVs at this address. We have to invalidate all cached debuggee data.
+ m_appdomain->GetProcess()->m_continueCounter++;
+ }
+
+ return hr;
+} // CordbReferenceValue::SetValue
+
+HRESULT CordbReferenceValue::DereferenceStrong(ICorDebugValue **ppValue)
+{
+ return E_NOTIMPL;
+}
+
+// Get a new ICDValue instance to represent the referent of this object ref.
+// Arguments:
+// output: ppValue - the new ICDValue instance
+// Return Value: S_OK on success or E_INVALIDARG
+HRESULT CordbReferenceValue::Dereference(ICorDebugValue **ppValue)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+
+ // Can't dereference literal refs.
+ if (m_isLiteral)
+ return E_INVALIDARG;
+
+ VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ HRESULT hr = S_OK;
+
+ if (m_continueCounterLastSync != m_appdomain->GetProcess()->m_continueCounter)
+ {
+ IfFailRet(InitRef(MemoryRange(NULL, 0)));
+ }
+
+ EX_TRY
+ {
+ // We may know ahead of time (depending on the reference type) if
+ // the reference is bad.
+ if ((m_info.objRefBad) || (m_info.objRef == NULL))
+ {
+ ThrowHR(CORDBG_E_BAD_REFERENCE_VALUE);
+ }
+
+ hr = DereferenceCommon(m_appdomain, m_type, m_realTypeOfTypedByref, &m_info, ppValue);
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+
+}
+
+//-----------------------------------------------------------------------------
+// Common helper to dereferefence.
+// Parameters:
+// pAppDomain, pType, pInfo - necessary paramters to create the value
+// pRealTypeOfTypedByref - type for a potential TypedByRef. Can be NULL if we know
+// that we're not a typed-byref (this is true if we're definitely an object handle)
+// ppValue - outparameter for newly created value. This will get an Ext AddRef.
+//-----------------------------------------------------------------------------
+HRESULT CordbReferenceValue::DereferenceCommon(
+ CordbAppDomain * pAppDomain,
+ CordbType * pType,
+ CordbType * pRealTypeOfTypedByref,
+ DebuggerIPCE_ObjectData * pInfo,
+ ICorDebugValue **ppValue
+)
+{
+ INTERNAL_SYNC_API_ENTRY(pAppDomain->GetProcess());
+
+ // pCachedObject may be NULL if we're not caching.
+ _ASSERTE(pType != NULL);
+ _ASSERTE(pAppDomain != NULL);
+ _ASSERTE(pInfo != NULL);
+ _ASSERTE(ppValue != NULL);
+
+ HRESULT hr = S_OK;
+ *ppValue = NULL; // just to be safe.
+
+ switch(pType->m_elementType)
+ {
+ case ELEMENT_TYPE_CLASS:
+ case ELEMENT_TYPE_OBJECT:
+ case ELEMENT_TYPE_STRING:
+ {
+ LOG((LF_CORDB, LL_INFO1000, "DereferenceInternal: type class/object/string\n"));
+ // An object value (possibly a string value, too.) If the class of this object is a value class,
+ // then we have a reference to a boxed object. So we create a box instead of an object value.
+ bool isBoxedVCObject = false;
+
+ if ((pType->m_pClass != NULL) && (pType->m_elementType != ELEMENT_TYPE_STRING))
+ {
+ EX_TRY
+ {
+ isBoxedVCObject = pType->m_pClass->IsValueClass();
+ }
+ EX_CATCH_HRESULT(hr);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ }
+
+ if (isBoxedVCObject)
+ {
+ TargetBuffer remoteValue(PTR_TO_CORDB_ADDRESS(pInfo->objRef), (ULONG)pInfo->objSize);
+ EX_TRY
+ {
+ RSSmartPtr<CordbBoxValue> pBoxValue(new CordbBoxValue(
+ pAppDomain,
+ pType,
+ remoteValue,
+ (ULONG32)pInfo->objSize,
+ pInfo->objOffsetToVars));
+ pBoxValue->ExternalAddRef();
+ *ppValue = (ICorDebugValue*)(ICorDebugBoxValue*)pBoxValue;
+ }
+ EX_CATCH_HRESULT(hr);
+ }
+ else
+ {
+ RSSmartPtr<CordbObjectValue> pObj;
+ TargetBuffer remoteValue(PTR_TO_CORDB_ADDRESS(pInfo->objRef), (ULONG)pInfo->objSize);
+ // Note: we call Init() by default when we create (or refresh) a reference value, so we
+ // never have to do it again.
+ EX_TRY
+ {
+ pObj.Assign(new CordbObjectValue(pAppDomain, pType, remoteValue, pInfo));
+ IfFailThrow(pObj->Init());
+
+ pObj->ExternalAddRef();
+ *ppValue = static_cast<ICorDebugValue*>( static_cast<ICorDebugObjectValue*>(pObj) );
+ }
+ EX_CATCH_HRESULT(hr);
+ } // boxed?
+
+ break;
+ }
+
+ case ELEMENT_TYPE_ARRAY:
+ case ELEMENT_TYPE_SZARRAY:
+ {
+ LOG((LF_CORDB, LL_INFO1000, "DereferenceInternal: type array/szarray\n"));
+ TargetBuffer remoteValue(PTR_TO_CORDB_ADDRESS(pInfo->objRef), (ULONG)pInfo->objSize); // sizeof(void *)?
+ EX_TRY
+ {
+ RSSmartPtr<CordbArrayValue> pArrayValue(new CordbArrayValue(
+ pAppDomain,
+ pType,
+ pInfo,
+ remoteValue));
+
+ IfFailThrow(pArrayValue->Init());
+
+ pArrayValue->ExternalAddRef();
+ *ppValue = (ICorDebugValue*)(ICorDebugArrayValue*)pArrayValue;
+ }
+ EX_CATCH_HRESULT(hr);
+
+ break;
+ }
+
+ case ELEMENT_TYPE_BYREF:
+ case ELEMENT_TYPE_PTR:
+ {
+ //_ASSERTE(pInfo->objToken.IsNull()); // can't get this type w/ an object handle
+
+ LOG((LF_CORDB, LL_INFO1000, "DereferenceInternal: type byref/ptr\n"));
+ CordbType *ptrType;
+ pType->DestUnaryType(&ptrType);
+
+ CorElementType et = ptrType->m_elementType;
+
+ if (et == ELEMENT_TYPE_VOID)
+ {
+ *ppValue = NULL;
+ return CORDBG_S_VALUE_POINTS_TO_VOID;
+ }
+
+ TargetBuffer remoteValue(pInfo->objRef, GetSizeForType(ptrType, kUnboxed));
+ // Create a value for what this reference points to. Note:
+ // this could be almost any type of value.
+ EX_TRY
+ {
+ CordbValue::CreateValueByType(
+ pAppDomain,
+ ptrType,
+ false,
+ remoteValue,
+ MemoryRange(NULL, 0), // local value
+ NULL,
+ ppValue); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+
+ break;
+ }
+
+ case ELEMENT_TYPE_TYPEDBYREF:
+ {
+ //_ASSERTE(pInfo->objToken.IsNull()); // can't get this type w/ an object handle
+ _ASSERTE(pRealTypeOfTypedByref != NULL);
+
+ LOG((LF_CORDB, LL_INFO1000, "DereferenceInternal: type typedbyref\n"));
+
+ TargetBuffer remoteValue(pInfo->objRef, sizeof(void *));
+ // Create the value for what this reference points
+ // to.
+ EX_TRY
+ {
+ CordbValue::CreateValueByType(
+ pAppDomain,
+ pRealTypeOfTypedByref,
+ false,
+ remoteValue,
+ MemoryRange(NULL, 0), // local value
+ NULL,
+ ppValue); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+
+ break;
+ }
+
+ case ELEMENT_TYPE_FNPTR:
+ // Function pointers cannot be dereferenced; only the pointer value itself
+ // may be inspected--not what it points to.
+ *ppValue = NULL;
+ return CORDBG_E_VALUE_POINTS_TO_FUNCTION;
+
+ default:
+ LOG((LF_CORDB, LL_INFO1000, "DereferenceInternal: Fail!\n"));
+ _ASSERTE(!"Bad reference type!");
+ hr = E_FAIL;
+ break;
+ }
+
+ return hr;
+}
+
+// static helper to build a CordbReferenceValue from a general variable home.
+// We can find the CordbType from the object instance.
+HRESULT CordbReferenceValue::Build(CordbAppDomain * appdomain,
+ CordbType * type,
+ TargetBuffer remoteValue,
+ MemoryRange localValue,
+ VMPTR_OBJECTHANDLE vmObjectHandle,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr,
+ CordbReferenceValue** ppValue)
+{
+ HRESULT hr = S_OK;
+
+ // We can find the AD from an object handle (but not a normal object), so the AppDomain may
+ // be NULL if if it's an OH.
+ //_ASSERTE((appdomain != NULL) || objectRefsInHandles);
+
+ // A reference, possibly to an object or value class
+ // Weak by default
+ EX_TRY
+ {
+ RSSmartPtr<CordbReferenceValue> pRefValue(new CordbReferenceValue(appdomain,
+ type,
+ localValue,
+ remoteValue,
+ ppRemoteRegAddr,
+ vmObjectHandle));
+ IfFailThrow(pRefValue->InitRef(localValue));
+
+ pRefValue->InternalAddRef();
+ *ppValue = pRefValue;
+ }
+ EX_CATCH_HRESULT(hr)
+ return hr;
+}
+
+//-----------------------------------------------------------------------------
+// Static helper to build a CordbReferenceValue from a GCHandle
+// The LS can actually determine an AppDomain from an OBJECTHandles, however, the RS
+// should already have this infromation too, so we pass it in.
+// We also supply the AppDomain here because it provides the CordbValue with
+// process affinity.
+// Note that the GC handle may point to a NULL reference, in which case we should still create
+// an appropriate ICorDebugReferenceValue for which IsNull returns TRUE.
+//-----------------------------------------------------------------------------
+HRESULT CordbReferenceValue::BuildFromGCHandle(
+ CordbAppDomain *pAppDomain,
+ VMPTR_OBJECTHANDLE gcHandle,
+ ICorDebugReferenceValue ** pOutRef
+)
+{
+ _ASSERTE(pAppDomain != NULL);
+ _ASSERTE(pOutRef != NULL);
+
+ CordbProcess * pProc;
+ pProc = pAppDomain->GetProcess();
+ INTERNAL_SYNC_API_ENTRY(pProc);
+
+ HRESULT hr = S_OK;
+
+ *pOutRef = NULL;
+
+ // Make sure we even have a GC handle.
+ // Also, We may have a handle, but its contents may be null.
+ if (gcHandle.IsNull())
+ {
+ // We've seen this assert fire in the wild, but have never gotten a repro.
+ // so we'll include a runtime check to avoid the AV.
+ _ASSERTE(false || !"We got a bad reference value.");
+ return CORDBG_E_BAD_REFERENCE_VALUE;
+ }
+
+ // Now that we've got an AppDomain, we can go ahead and create the reference value normally.
+
+ RSSmartPtr<CordbReferenceValue> pRefValue;
+ TargetBuffer remoteValue;
+ EX_TRY
+ {
+ remoteValue.Init(pProc->GetDAC()->GetHandleAddressFromVmHandle(gcHandle), sizeof(void *));
+ }
+ EX_CATCH_HRESULT(hr);
+ IfFailRet(hr);
+
+ hr = CordbReferenceValue::Build(
+ pAppDomain,
+ NULL, // unknown type
+ remoteValue, // CORDB_ADDRESS remoteAddress,
+ MemoryRange(NULL, 0),
+ gcHandle, // objectRefsInHandles,
+ NULL, // EnregisteredValueHome * pRemoteRegAddr,
+ &pRefValue);
+
+ if (SUCCEEDED(hr))
+ {
+ pRefValue->QueryInterface(__uuidof(ICorDebugReferenceValue), (void**)pOutRef);
+ }
+
+ return hr;
+}
+
+// Helper function for SanityCheckPointer. Make an attempt to read memory at the address which is the value
+// of the reference.
+// Arguments: none
+// Notes:
+// - Throws
+// - m_info.objRefBad must be set to true before calling this function. If we throw, we'll
+// never end up setting m_info.objRefBad, but throwing indicates that the reference is
+// indeed bad. Only if we exit normally will we end up setting m_info.objRefBad to false.
+void CordbReferenceValue::TryDereferencingTarget()
+{
+ _ASSERTE(!!m_info.objRefBad == true);
+ // First get the referent type
+ CordbType * pReferentType;
+ m_type->DestUnaryType(&pReferentType);
+
+ // Next get the size
+ ULONG32 dataSize, sizeToRead;
+ IfFailThrow(pReferentType->GetUnboxedObjectSize(&dataSize));
+ if (dataSize <= 0)
+ sizeToRead = 1; // Read at least one byte.
+ else if (dataSize >= 8)
+ sizeToRead = 8; // Read at most eight bytes--this is just a perf improvement. Even if we read
+ // all the bytes, we are only able to determine that we can read those bytes,
+ // we can't really tell if the data we are reading is actually the data we
+ // want.
+ else sizeToRead = dataSize;
+
+ // Now see if we can read from the address where the object is supposed to be
+ BYTE dummy[8];
+
+ // Get a target buffer with the remote address and size of the object--since we don't know if the
+ // address if valid, this could throw or return a size that's complete garbage
+ TargetBuffer object(m_info.objRef, sizeToRead);
+
+ // now read target memory. This may throw ...
+ GetProcess()->SafeReadBuffer(object, dummy);
+
+} // CordbReferenceValue::TryDereferencingTarget
+
+// Do a sanity check on the pointer which is the value of the object reference. We can't efficiently ensure that
+// the pointer is really good, so we settle for a quick check just to make sure the memory at the address is
+// readable. We're actually just checking that we can dereference the pointer.
+// Arguments:
+// input: type - the type of the pointer to which the object reference points.
+// output: none, but fills in m_info.objRefBad
+// Note: Throws
+void CordbReferenceValue::SanityCheckPointer (CorElementType type)
+{
+ m_info.objRefBad = TRUE;
+ if (type != ELEMENT_TYPE_FNPTR)
+ {
+ // We should never dereference a function pointer, so all references
+ // are considered "bad."
+ if (m_info.objRef != NULL)
+ {
+ if (type == ELEMENT_TYPE_PTR)
+ {
+ // The only way to tell if the reference in PTR is bad or
+ // not is to try to deref the thing.
+ TryDereferencingTarget();
+ }
+ } // !m_info.m_basicData.m_vmObject.IsNull()
+ // else Null refs are considered "bad".
+ } // type != ELEMENT_TYPE_FNPTR
+
+ // we made it without throwing, so we'll assume (perhaps wrongly) that the ref is good
+ m_info.objRefBad = FALSE;
+
+} // CordbReferenceValue::SanityCheckPointer
+
+// get information about the reference when it's not an object address but another kind of pointer type:
+// ELEMENT_TYPE_BYREF, ELEMENT_TYPE_PTR or ELEMENT_TYPE_FNPTR
+// Arguments:
+// input: type - type of the referent
+// localValue - starting address and length of a local buffer containing the object ref
+// Notes:
+// - fills in the m_info field of "this"
+// - Throws (errors from reading process memory)
+void CordbReferenceValue::GetPointerData(CorElementType type, MemoryRange localValue)
+{
+ HRESULT hr = S_OK;
+ // Fill in the type since we will not be getting it from the DAC
+ m_info.objTypeData.elementType = type;
+
+ // First get the objRef
+ if (localValue.StartAddress() != NULL)
+ {
+ // localValue represents a buffer containing a copy of the objectRef that exists locally. It could be a
+ // component of a container type residing within a local cached copy belonging to some other
+ // Cordb*Value instance representing the container type. In this case it will be a field, array
+ // element, or referent of a different object reference for that other Cordb*Value instance. It
+ // could also be a pointer to the value of a local register display of the frame from which this object
+ // ref comes.
+
+ // For example, if we have a value class (represented by a CordbVCObject instance) with a field
+ // that is an object pointer, localValue will contain a pointer to that field in the local
+ // cache of the CordbVCObjectValue instance (CordbVCObjectValue::m_pObjectCopy).
+
+ // Note, though, that pLocalValue holds the address of a target object. We will cache
+ // the contents of pLocalValue (the object ref) here for efficiency of read access, but if we
+ // want to set the reference later (e.g., we want the object ref to point to NULL instead of an
+ // object), we'll have to set the object ref in the target, not our local copy.
+ // Host memory Target memory
+ // --------------- |
+ // CordbVCObjectValue::m_copyOfObject ----> | |
+ // | ... | |
+ // | |
+ // |---------------| | Object
+ // localAddress ---> | object addr |-------------> --------------
+ // |---------------| | ---> | |
+ // | ... | | | |
+ // --------------- | | --------------
+ // |
+ // CordbReferenceValue::m_info.objRef ---> --------------- | |
+ // | object addr |---------
+ // --------------- |
+
+ _ASSERTE(localValue.Size() == sizeof(void *));
+ localCopy(&(m_info.objRef), localValue);
+ }
+ else
+ {
+ // we have a non-local location, so we'll get the value of the ref from its home
+
+ // do some preinitialization in case we get an exception
+ EX_TRY
+ {
+ m_valueHome.m_pHome->GetValue(MemoryRange(&(m_info.objRef), sizeof(void*))); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+ if (FAILED(hr))
+ {
+ m_info.objRef = NULL;
+ m_info.objRefBad = TRUE;
+ ThrowHR(hr);
+ }
+ }
+
+ EX_TRY
+ {
+ // If we made it this far, we need to sanity check the pointer--we'll just see if we can
+ // read at that address
+ SanityCheckPointer(type);
+ }
+ EX_CATCH_HRESULT(hr); // we don't need to do anything here, m_info.objRefBad will have been set to true
+
+} // CordbReferenceValue::GetPointerData
+
+// Helper function for CordbReferenceValue::GetObjectData: Sets default values for the fields in pObjectData
+// before processing begins. Not all will necessarily be initialized during processing.
+// Arguments:
+// input: objectType - type of the referent of the objRef being examined
+// output: pObjectData - information about the reference to be initialized
+void PreInitObjectData(DebuggerIPCE_ObjectData * pObjectData, void * objAddress, CorElementType objectType)
+{
+ _ASSERTE(pObjectData != NULL);
+
+ memset(pObjectData, 0, sizeof(DebuggerIPCE_ObjectData));
+ pObjectData->objRef = objAddress;
+ pObjectData->objTypeData.elementType = objectType;
+
+} // PreInitObjectData
+
+// get basic object specific data when a reference points to an object, plus extra data if the object is an
+// array or string
+// Arguments:
+// input: pProcess - process to which the object belongs
+// objectAddress - pointer to the TypedByRef object (this is the value of the object reference
+// or handle.
+// type - the type of the object referenced
+// vmAppDomain - appdomain to which the object belongs
+// output: pInfo - filled with information about the object to which the TypedByRef refers.
+// Note: Throws
+/* static */
+void CordbReferenceValue::GetObjectData(CordbProcess * pProcess,
+ void * objectAddress,
+ CorElementType type,
+ VMPTR_AppDomain vmAppdomain,
+ DebuggerIPCE_ObjectData * pInfo)
+{
+ IDacDbiInterface *pInterface = pProcess->GetDAC();
+ CORDB_ADDRESS objTargetAddr = PTR_TO_CORDB_ADDRESS(objectAddress);
+
+ // make sure we don't end up with old garbage values in case the reference is bad
+ PreInitObjectData(pInfo, objectAddress, type);
+
+ pInterface->GetBasicObjectInfo(objTargetAddr, type, vmAppdomain, pInfo);
+
+ if (!pInfo->objRefBad)
+ {
+ // for certain referent types, we need a bit more information:
+ if (pInfo->objTypeData.elementType == ELEMENT_TYPE_STRING)
+ {
+ pInterface->GetStringData(objTargetAddr, pInfo);
+ }
+ else if ((pInfo->objTypeData.elementType == ELEMENT_TYPE_ARRAY) ||
+ (pInfo->objTypeData.elementType == ELEMENT_TYPE_SZARRAY))
+ {
+ pInterface->GetArrayData(objTargetAddr, pInfo);
+ }
+ }
+
+} // CordbReferenceValue::GetObjectData
+
+// get information about a TypedByRef object when the reference is the address of a TypedByRef structure.
+// Arguments:
+// input: pProcess - process to which the object belongs
+// pTypedByRef - pointer to the TypedByRef object (this is the value of the object reference or
+// handle.
+// type - the type of the object referenced
+// vmAppDomain - appdomain to which the object belongs
+// output: pInfo - filled with information about the object to which the TypedByRef refers.
+// Note: Throws
+/* static */
+void CordbReferenceValue::GetTypedByRefData(CordbProcess * pProcess,
+ CORDB_ADDRESS pTypedByRef,
+ CorElementType type,
+ VMPTR_AppDomain vmAppDomain,
+ DebuggerIPCE_ObjectData * pInfo)
+{
+
+ // make sure we don't end up with old garbage values since we don't set all the values for TypedByRef objects
+ PreInitObjectData(pInfo, CORDB_ADDRESS_TO_PTR(pTypedByRef), type);
+
+ // Though pTypedByRef is the value of the object ref represented by an instance of CordbReferenceValue,
+ // it is not the address of an object, as we would ordinarily expect. Instead, in the special case of
+ // TypedByref objects, it is actually the address of the TypedByRef struct which contains the
+ // type and the object address.
+
+ pProcess->GetDAC()->GetTypedByRefInfo(pTypedByRef, vmAppDomain, pInfo);
+} // CordbReferenceValue::GetTypedByRefData
+
+// get the address of the object referenced
+// Arguments: none
+// Return Value: the address of the object referenced (i.e., the value of the object ref)
+// Note: Throws
+void * CordbReferenceValue::GetObjectAddress(MemoryRange localValue)
+{
+ void * objectAddress;
+ if (localValue.StartAddress() != NULL)
+ {
+ // the object ref comes from a local cached copy
+ _ASSERTE(localValue.Size() == sizeof(void *));
+ memcpy(&objectAddress, localValue.StartAddress(), localValue.Size());
+ }
+ else
+ {
+ _ASSERTE(m_valueHome.m_pHome != NULL);
+ m_valueHome.m_pHome->GetValue(MemoryRange(&objectAddress, sizeof(void *))); // throws
+ }
+ return objectAddress;
+} // CordbReferenceValue::GetObjectAddress
+
+// update type information after initializing -- when we initialize, we may get more exact type information
+// than we previously had
+// Arguments: none--uses and updates data members
+// Note: Throws
+void CordbReferenceValue::UpdateTypeInfo()
+{
+ // If the object type that we got back is different than the one we sent, then it means that we
+ // orignally had a CLASS and now have something more specific, like a SDARRAY, MDARRAY, or STRING or
+ // a constructed type.
+ // Update our signature accordingly, which is okay since we always have a copy of our sig. This
+ // ensures that the reference's signature accurately reflects what the Runtime knows it's pointing
+ // to.
+ //
+ // GENERICS: do this for all types: for example, an array might have been discovered to be a more
+ // specific kind of array (String[] where an Object[] was expected).
+ CordbType *newtype;
+
+ IfFailThrow(CordbType::TypeDataToType(m_appdomain, &m_info.objTypeData, &newtype));
+
+ _ASSERTE(newtype->m_elementType != ELEMENT_TYPE_VALUETYPE);
+ m_type.Assign(newtype); // implicit Release + AddRef
+
+ // For typed-byref's the act of dereferencing the object also reveals to us
+ // what the "real" type of the object is...
+ if (m_info.objTypeData.elementType == ELEMENT_TYPE_TYPEDBYREF)
+{
+ IfFailThrow(CordbType::TypeDataToType(m_appdomain,
+ &m_info.typedByrefInfo.typedByrefType,
+ &m_realTypeOfTypedByref));
+ }
+} // CordbReferenceValue::UpdateTypeInfo
+
+// Initialize this CordbReferenceValue. This may involve inspecting the LS to get information about the
+// referent.
+// Arguments:
+// input: localValue - buffer address and size of the RS location of the reference. (This may be NULL
+// if the reference didn't come from a local cached copy. See
+// code:CordbReferenceValue::GetPointerData for further explanation of local locations.)
+// Return Value: S_OK on success or E_INVALIDARG or write process memory errors on failure
+
+HRESULT CordbReferenceValue::InitRef(MemoryRange localValue)
+{
+ INTERNAL_SYNC_API_ENTRY(this->GetProcess());
+
+ HRESULT hr = S_OK;
+ CordbProcess * pProcess = GetProcess();
+
+ // Simple init needed for literal refs. Literals may have a null process / appdomain ptr.
+ if (m_isLiteral)
+ {
+ _ASSERTE(m_type != NULL);
+ m_info.objTypeData.elementType = m_type->m_elementType;
+ return hr;
+ }
+
+ _ASSERTE((pProcess->GetShim() == NULL) || pProcess->GetSynchronized());
+
+ // If the helper thread is dead, then pretend this is a bad reference.
+ if (GetProcess()->m_helperThreadDead)
+ {
+ m_info.objRef = NULL;
+ m_info.objRefBad = TRUE;
+ return hr;
+ }
+
+ m_continueCounterLastSync = pProcess->m_continueCounter;
+
+ // If no type provided, then it's b/c we're a class and we'll get the type when we get Created.
+ CorElementType type = (m_type != NULL) ? (m_type->m_elementType) : ELEMENT_TYPE_CLASS;
+ _ASSERTE (type != ELEMENT_TYPE_GENERICINST);
+ _ASSERTE (type != ELEMENT_TYPE_VAR);
+ _ASSERTE (type != ELEMENT_TYPE_MVAR);
+
+ EX_TRY
+ {
+ if ((type == ELEMENT_TYPE_BYREF) ||
+ (type == ELEMENT_TYPE_PTR) ||
+ (type == ELEMENT_TYPE_FNPTR))
+ {
+ // we know the size is just the size of a pointer, so we can just read process memory to get the
+ // information we need
+ GetPointerData(type, localValue);
+ }
+ else // we have to get more information about the object from the DAC
+ {
+ if (type == ELEMENT_TYPE_TYPEDBYREF)
+ {
+ _ASSERTE(m_valueHome.m_pHome != NULL);
+ GetTypedByRefData(pProcess,
+ m_valueHome.m_pHome->GetAddress(),
+ type,
+ m_appdomain->GetADToken(),
+ &m_info);
+ }
+ else
+ {
+ GetObjectData(pProcess, GetObjectAddress(localValue), type, m_appdomain->GetADToken(), &m_info);
+ }
+
+ // if we got (what we believe is probably) a good reference, we should update the type info
+ if (!m_info.objRefBad)
+ {
+ // we may have gotten back a more specific type than we had previously
+ UpdateTypeInfo();
+ }
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+} // CordbReferenceValue::InitRef
+
+/* ------------------------------------------------------------------------- *
+ * Object Value class
+ * ------------------------------------------------------------------------- */
+
+
+// validate a CordbObjectValue to ensure it hasn't been neutered
+#define COV_VALIDATE_OBJECT() do { \
+ BOOL bValid; \
+ HRESULT hr; \
+ if (FAILED(hr = IsValid(&bValid))) \
+ return hr; \
+ \
+ if (!bValid) \
+ { \
+ return CORDBG_E_INVALID_OBJECT; \
+ } \
+ }while(0)
+
+// constructor
+// Arguments:
+// input: pAppDomain - the appdomain to which the object belongs
+// pType - the type of the object
+// remoteValue - the LS address and size of the object
+// pObjectData - other information about the object, most importantly, the offset to the
+// fields of the object
+CordbObjectValue::CordbObjectValue(CordbAppDomain * pAppdomain,
+ CordbType * pType,
+ TargetBuffer remoteValue,
+ DebuggerIPCE_ObjectData *pObjectData )
+ : CordbValue(pAppdomain, pType, remoteValue.pAddress,
+ false, pAppdomain->GetProcess()->GetContinueNeuterList()),
+ m_info(*pObjectData),
+ m_pObjectCopy(NULL), m_objectLocalVars(NULL), m_stringBuffer(NULL),
+ m_valueHome(pAppdomain->GetProcess(), remoteValue),
+ m_fIsExceptionObject(FALSE), m_fIsRcw(FALSE)
+{
+ _ASSERTE(pAppdomain != NULL);
+
+ m_size = m_info.objSize;
+
+ HRESULT hr = S_FALSE;
+
+ ALLOW_DATATARGET_MISSING_MEMORY
+ (
+ hr = IsExceptionObject();
+ );
+
+ if (hr == S_OK)
+ m_fIsExceptionObject = TRUE;
+
+ hr = S_FALSE;
+ ALLOW_DATATARGET_MISSING_MEMORY
+ (
+ hr = IsRcw();
+ );
+
+ if (hr == S_OK)
+ m_fIsRcw = TRUE;
+} // CordbObjectValue::CordbObjectValue
+
+// destructor
+CordbObjectValue::~CordbObjectValue()
+{
+ DTOR_ENTRY(this);
+
+ _ASSERTE(IsNeutered());
+} // CordbObjectValue::~CordbObjectValue
+
+void CordbObjectValue::Neuter()
+{
+ // Destroy the copy of the object.
+ if (m_pObjectCopy != NULL)
+ {
+ delete [] m_pObjectCopy;
+ m_pObjectCopy = NULL;
+ }
+
+ CordbValue::Neuter();
+} // CordbObjectValue::Neuter
+
+HRESULT CordbObjectValue::QueryInterface(REFIID id, void **pInterface)
+{
+ if (id == IID_ICorDebugValue)
+ {
+ *pInterface = static_cast<ICorDebugValue*>(static_cast<ICorDebugObjectValue*>(this));
+ }
+ else if (id == IID_ICorDebugValue2)
+ {
+ *pInterface = static_cast<ICorDebugValue2*>(this);
+ }
+ else if (id == IID_ICorDebugValue3)
+ {
+ *pInterface = static_cast<ICorDebugValue3*>(this);
+ }
+ else if (id == IID_ICorDebugObjectValue)
+ {
+ *pInterface = static_cast<ICorDebugObjectValue*>(this);
+ }
+ else if (id == IID_ICorDebugObjectValue2)
+ {
+ *pInterface = static_cast<ICorDebugObjectValue2*>(this);
+ }
+ else if (id == IID_ICorDebugGenericValue)
+ {
+ *pInterface = static_cast<ICorDebugGenericValue*>(this);
+ }
+ else if (id == IID_ICorDebugHeapValue)
+ {
+ *pInterface = static_cast<ICorDebugHeapValue*>(this);
+ }
+ else if (id == IID_ICorDebugHeapValue2)
+ {
+ *pInterface = static_cast<ICorDebugHeapValue2*>(this);
+ }
+ else if (id == IID_ICorDebugHeapValue3)
+ {
+ *pInterface = static_cast<ICorDebugHeapValue3*>(this);
+ }
+ else if ((id == IID_ICorDebugStringValue) &&
+ (m_info.objTypeData.elementType == ELEMENT_TYPE_STRING))
+ {
+ *pInterface = static_cast<ICorDebugStringValue*>(this);
+ }
+ else if (id == IID_ICorDebugExceptionObjectValue && m_fIsExceptionObject)
+ {
+ *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugExceptionObjectValue*>(this));
+ }
+ else if (id == IID_ICorDebugComObjectValue && m_fIsRcw)
+ {
+ *pInterface = static_cast<ICorDebugComObjectValue*>(this);
+ }
+ else if (id == IID_IUnknown)
+ {
+ *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugObjectValue*>(this));
+ }
+ else
+ {
+ *pInterface = NULL;
+ return E_NOINTERFACE;
+ }
+
+ ExternalAddRef();
+ return S_OK;
+} // CordbObjectValue::QueryInterface
+
+// gets the type of the object
+// Arguments:
+// output: pType - the type of the value. The caller must guarantee that pType is non-null.
+// Return Value: S_OK on success, E_INVALIDARG on failure
+HRESULT CordbObjectValue::GetType(CorElementType *pType)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ return (CordbValue::GetType(pType));
+} // CordbObjectValue::GetType
+
+// gets the size of the object
+// Arguments:
+// output: pSize - the size of the value. The caller must guarantee that pSize is non-null.
+// Return Value: S_OK on success, E_INVALIDARG on failure
+HRESULT CordbObjectValue::GetSize(ULONG32 *pSize)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ return (CordbValue::GetSize(pSize));
+} // CordbObjectValue::GetSize
+
+// gets the size of the object
+// Arguments:
+// output: pSize - the size of the value. The caller must guarantee that pSize is non-null.
+// Return Value: S_OK on success, E_INVALIDARG on failure
+HRESULT CordbObjectValue::GetSize64(ULONG64 *pSize)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ return (CordbValue::GetSize64(pSize));
+} // CordbObjectValue::GetSize64
+
+
+// gets the remote (LS) address of the object. This may return NULL if the
+// object is a literal or resides in a register.
+// Arguments:
+// output: pAddress - the LS address (the contents should not be null since objects
+// aren't enregistered nor are they fields or elements of other
+// types). The caller must ensure that pAddress is not null.
+// Return Value: S_OK on success or E_INVALIDARG if pAddress is null
+HRESULT CordbObjectValue::GetAddress(CORDB_ADDRESS *pAddress)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ COV_VALIDATE_OBJECT();
+ VALIDATE_POINTER_TO_OBJECT(pAddress, CORDB_ADDRESS *);
+
+ *pAddress = m_valueHome.GetAddress();
+ return (S_OK);
+} // CordbObjectValue::GetAddress
+
+HRESULT CordbObjectValue::CreateBreakpoint(ICorDebugValueBreakpoint ** ppBreakpoint)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ COV_VALIDATE_OBJECT();
+
+ return (CordbValue::CreateBreakpoint(ppBreakpoint));
+}
+
+// determine if "this" is still valid (i.e., not neutered)
+// Arguments:
+// output: pfIsValid - true iff "this" is still not neutered
+// Return Value: S_OK or E_INVALIDARG
+HRESULT CordbObjectValue::IsValid(BOOL * pfIsValid)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(pfIsValid, BOOL *);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ // We're neutered on continue, so we're valid up until the time we're neutered
+ (*pfIsValid) = TRUE;
+ return S_OK;
+}
+
+HRESULT CordbObjectValue::CreateRelocBreakpoint(
+ ICorDebugValueBreakpoint **ppBreakpoint)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(ppBreakpoint, ICorDebugValueBreakpoint **);
+
+ COV_VALIDATE_OBJECT();
+
+ return E_NOTIMPL;
+}
+
+/*
+* Creates a handle of the given type for this heap value.
+*
+* Not Implemented In-Proc.
+*/
+HRESULT CordbObjectValue::CreateHandle(
+ CorDebugHandleType handleType,
+ ICorDebugHandleValue ** ppHandle)
+{
+ PUBLIC_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ return CordbValue::InternalCreateHandle(handleType, ppHandle);
+} // CreateHandle
+
+// Get class information for this object
+// Arguments:
+// output: ppClass - ICDClass instance for this object
+// Return Value: S_OK if success, CORDBG_E_CLASS_NOT_LOADED, E_INVALIDARG, OOM on failure
+HRESULT CordbObjectValue::GetClass(ICorDebugClass **ppClass)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(ppClass, ICorDebugClass **);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ HRESULT hr = S_OK;
+ if (m_type->m_pClass == NULL)
+ {
+ if (FAILED(hr = m_type->Init(FALSE)))
+ return hr;
+ }
+
+ _ASSERTE(m_type->m_pClass);
+ *ppClass = (ICorDebugClass*) m_type->m_pClass;
+
+ if (*ppClass != NULL)
+ (*ppClass)->AddRef();
+
+ return hr;
+} // CordbObjectValue::GetClass
+
+
+
+
+
+//-----------------------------------------------------------------------------
+//
+// Public API to get instance field of the given type in the object and returns an ICDValue for it.
+//
+// Arguments:
+// pType - The type containing the field token.
+// fieldDef - The field's metadata def.
+// ppValue - OUT: the ICDValue for the field.
+//
+// Returns:
+// S_OK on success. E_INVALIDARG, CORDBG_E_ENC_HANGING_FIELD, CORDBG_E_FIELD_NOT_INSTANCE or OOM on
+// failure
+//
+// Notes:
+// This is for instance fields only.
+// Lookup on code:CordbType::GetStaticFieldValue to get static fields.
+// This is generics aware.
+HRESULT CordbObjectValue::GetFieldValueForType(ICorDebugType * pType,
+ mdFieldDef fieldDef,
+ ICorDebugValue ** ppValue)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(pType, ICorDebugType *);
+ VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **);
+
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ COV_VALIDATE_OBJECT();
+
+ CordbType * pCordbType = NULL;
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ BOOL fSyncBlockField = FALSE;
+ SIZE_T fldOffset;
+
+ //
+ // <TODO>@todo: need to ensure that pType is really on the class
+ // hierarchy of m_class!!!</TODO>
+ //
+ if (pType == NULL)
+ {
+ pCordbType = m_type;
+ }
+ else
+ {
+ pCordbType = static_cast<CordbType *>(pType);
+ }
+
+ // Validate the token.
+ if (pCordbType->m_pClass == NULL)
+ {
+ ThrowHR(E_INVALIDARG);
+ }
+ IMetaDataImport * pImport = pCordbType->m_pClass->GetModule()->GetMetaDataImporter();
+
+ if (!pImport->IsValidToken(fieldDef))
+ {
+ ThrowHR(E_INVALIDARG);
+ }
+
+ FieldData * pFieldData;
+
+ #ifdef _DEBUG
+ pFieldData = NULL;
+ #endif
+
+ hr = pCordbType->GetFieldInfo(fieldDef, &pFieldData);
+
+ // If we couldn't get field info because the field was added with EnC
+ if (hr == CORDBG_E_ENC_HANGING_FIELD)
+ {
+ // The instance field hangs off the syncblock, get its address
+ hr = pCordbType->m_pClass->GetEnCHangingField(fieldDef, &pFieldData, this);
+
+ if (SUCCEEDED(hr))
+ {
+ fSyncBlockField = TRUE;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ _ASSERTE(pFieldData != NULL);
+
+ if (pFieldData->m_fFldIsStatic)
+ {
+ ThrowHR(CORDBG_E_FIELD_NOT_INSTANCE);
+ }
+
+ // Compute the remote address, too, so that SetValue will work.
+ // Note that if pFieldData is a syncBlock field, fldOffset will have been cooked
+ // to produce the correct result here.
+ _ASSERTE(pFieldData->OkToGetOrSetInstanceOffset());
+ fldOffset = pFieldData->GetInstanceOffset();
+
+ CordbModule * pModule = pCordbType->m_pClass->GetModule();
+
+ SigParser sigParser;
+ IfFailThrow(pFieldData->GetFieldSignature(pModule, &sigParser));
+
+ CordbType * pFieldType;
+ IfFailThrow(CordbType::SigToType(pModule, &sigParser, &(pCordbType->m_inst), &pFieldType));
+
+ ULONG32 size = GetSizeForType(pFieldType, kUnboxed);
+
+ void * localAddr = NULL;
+ if (!fSyncBlockField)
+ {
+ // verify that the field starts and ends before the end of m_pObjectCopy
+ _ASSERTE(m_info.objOffsetToVars + fldOffset < m_size);
+ _ASSERTE(m_info.objOffsetToVars + fldOffset + size <= m_size);
+ localAddr = m_objectLocalVars + fldOffset;
+ }
+
+ // pass the computed local field address, but don't claim we have a local addr if the fldOffset
+ // has been cooked to point us to a sync block field.
+ m_valueHome.CreateInternalValue(pFieldType,
+ m_info.objOffsetToVars + fldOffset,
+ localAddr,
+ size,
+ ppValue); // throws
+ }
+
+ // If we can't get it b/c it's a constant, then say so.
+ hr = CordbClass::PostProcessUnavailableHRESULT(hr, pImport, fieldDef);
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+} // CordbObjectValue::GetFieldValueForType
+
+// Public implementation of ICorDebugObjectValue::GetFieldValue
+// Arguments:
+// input: pClass - class information for this object
+// fieldDef - the field token for the requested field
+// output: ppValue - instance of ICDValue created to represent the field
+// Return Value: S_OK on success, E_INVALIDARG, CORDBG_E_ENC_HANGING_FIELD, CORDBG_E_FIELD_NOT_INSTANCE
+// or OOM on failure
+HRESULT CordbObjectValue::GetFieldValue(ICorDebugClass *pClass,
+ mdFieldDef fieldDef,
+ ICorDebugValue **ppValue)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ VALIDATE_POINTER_TO_OBJECT(pClass, ICorDebugClass *);
+ VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **);
+
+ COV_VALIDATE_OBJECT();
+
+ HRESULT hr;
+ _ASSERTE(m_type);
+
+ if (m_type->m_elementType != ELEMENT_TYPE_CLASS &&
+ m_type->m_elementType != ELEMENT_TYPE_VALUETYPE)
+ {
+ return E_INVALIDARG;
+ }
+
+ // mdFieldDef may specify a field within a base class. mdFieldDef tokens are unique throughout a module.
+ // So we still need a metadata scope to resolve the mdFieldDef. We can infer the scope from pClass.
+ // Beware that this Type may be derived from a type in another module, and so the incoming
+ // fieldDef has to be resolved in the metadata scope of pClass.
+
+ RSExtSmartPtr<CordbType> relevantType;
+
+ // This object has an ICorDebugType which has the type-parameters for generics.
+ // ICorDebugClass provided by the caller does not have type-parameters. So we resolve that
+ // by using the provided ICDClass with the type parameters from this object's ICDType.
+ if (FAILED (hr= m_type->GetParentType((CordbClass *) pClass, &relevantType)))
+ {
+ return hr;
+ }
+ // Upon exit relevantType will either be the appropriate type for the
+ // class we're looking for.
+
+ hr = GetFieldValueForType(relevantType, fieldDef, ppValue);
+ // GetParentType adds one reference to relevantType., Holder dtor releases
+ return hr;
+
+} // CordbObjectValue::GetFieldValue
+
+HRESULT CordbObjectValue::GetVirtualMethod(mdMemberRef memberRef,
+ ICorDebugFunction **ppFunction)
+{
+ VALIDATE_POINTER_TO_OBJECT(ppFunction, ICorDebugFunction **);
+ FAIL_IF_NEUTERED(this);
+ COV_VALIDATE_OBJECT();
+
+ return E_NOTIMPL;
+} // CordbObjectValue::GetVirtualMethod
+
+HRESULT CordbObjectValue::GetVirtualMethodAndType(mdMemberRef memberRef,
+ ICorDebugFunction **ppFunction,
+ ICorDebugType **ppType)
+{
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(ppFunction, ICorDebugFunction **);
+ VALIDATE_POINTER_TO_OBJECT(ppFunction, ICorDebugType **);
+
+ COV_VALIDATE_OBJECT();
+
+ return E_NOTIMPL;
+} // CordbObjectValue::GetVirtualMethodAndType
+
+HRESULT CordbObjectValue::GetContext(ICorDebugContext **ppContext)
+{
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(ppContext, ICorDebugContext **);
+
+ COV_VALIDATE_OBJECT();
+
+ return E_NOTIMPL;
+} // CordbObjectValue::GetContext
+
+// determines whether this represents a value class-- always returns false
+// Arguments:
+// output: pfIsValueClass - always false; CordbVCObjectValue is used to represent
+// value classes, so by definition, a CordbObjectValue instance
+// does not represent a value class
+// Return Value: S_OK
+//
+HRESULT CordbObjectValue::IsValueClass(BOOL * pfIsValueClass)
+{
+ FAIL_IF_NEUTERED(this);
+ COV_VALIDATE_OBJECT();
+
+ if (pfIsValueClass) // don't assign to a null pointer!
+ *pfIsValueClass = FALSE;
+
+ return S_OK;
+} // CordbObjectValue::IsValueClass
+
+HRESULT CordbObjectValue::GetManagedCopy(IUnknown **ppObject)
+{
+ // GetManagedCopy() is deprecated. In the case where the version of
+ // the debugger doesn't match the version of the debuggee, the two processes
+ // might have dangerously different notions of the layout of an object.
+
+ // This function is deprecated
+ return E_NOTIMPL;
+} // CordbObjectValue::GetManagedCopy
+
+HRESULT CordbObjectValue::SetFromManagedCopy(IUnknown *pObject)
+{
+ // Deprecated for the same reason as GetManagedCopy()
+ return E_NOTIMPL;
+} // CordbObjectValue::SetFromManagedCopy
+
+// gets a copy of the value
+// Arguments:
+// output: pTo - buffer to hold the object copy. The caller must guarantee that this
+// is non-null and the buffer is large enough to hold the object
+// Return Value: S_OK or CORDBG_E_INVALID_OBJECT, CORDBG_E_OBJECT_NEUTERED, or E_INVALIDARG on failure
+//
+HRESULT CordbObjectValue::GetValue(void *pTo)
+{
+ FAIL_IF_NEUTERED(this);
+ COV_VALIDATE_OBJECT();
+
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(pTo, BYTE, m_size, false, true);
+
+ // Copy out the value, which is the whole object.
+ memcpy(pTo, m_pObjectCopy, m_size);
+
+ return S_OK;
+} // CordbObjectValue::GetValue
+
+HRESULT CordbObjectValue::SetValue(void *pFrom)
+{
+ // You're not allowed to set a whole object at once.
+ return E_INVALIDARG;
+} // CordbObjectValue::SetValue
+
+// If this instance of CordbObjectValue is actually a string, get its length
+// Arguments:
+// output: pcchString - the count of characters in the string
+// Return Value: S_OK or CORDBG_E_INVALID_OBJECT, CORDBG_E_OBJECT_NEUTERED, or E_INVALIDARG on failure
+// Note: if the object is not really a string, the value in pcchString will be garbage on exit
+HRESULT CordbObjectValue::GetLength(ULONG32 *pcchString)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(pcchString, SIZE_T *);
+ FAIL_IF_NEUTERED(this);
+
+ _ASSERTE(m_info.objTypeData.elementType == ELEMENT_TYPE_STRING);
+
+ COV_VALIDATE_OBJECT();
+
+ *pcchString = (ULONG32)m_info.stringInfo.length;
+ return S_OK;
+} // CordbObjectValue::GetLength
+
+// If this instance of CordbObjectValue represents a string, extract the string and its length.
+// If cchString is less than the length of the string, we'll return only the first cchString characters
+// but pcchString will still hold the full length. If cchString is more than the string length, we'll
+// return only string length characters.
+// Arguments:
+// input: cchString - the maximum number of characters to return, including NULL terminator
+// output: pcchString - the actual length of the string, excluding NULL terminator (this may be greater than cchString)
+// szString - a buffer holding the string. The memory for this must be allocated and
+// managed by the caller and must have space for at least cchString characters
+// Return Value: S_OK or CORDBG_E_INVALID_OBJECT, CORDBG_E_OBJECT_NEUTERED, or E_INVALIDARG on failure
+HRESULT CordbObjectValue::GetString(ULONG32 cchString,
+ ULONG32 *pcchString,
+ __out_ecount_opt(cchString) WCHAR szString[])
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(szString, WCHAR, cchString, true, true);
+ VALIDATE_POINTER_TO_OBJECT(pcchString, SIZE_T *);
+
+ _ASSERTE(m_info.objTypeData.elementType == ELEMENT_TYPE_STRING);
+
+ COV_VALIDATE_OBJECT();
+
+ if ((szString == NULL) || (cchString == 0))
+ return E_INVALIDARG;
+
+ // Add 1 to include null terminator
+ SIZE_T len = m_info.stringInfo.length + 1;
+
+ // adjust length to the size of the buffer
+ if (cchString < len)
+ len = cchString;
+
+ memcpy(szString, m_stringBuffer, len * 2);
+ *pcchString = (ULONG32)m_info.stringInfo.length;
+
+ return S_OK;
+} // CordbObjectValue::GetString
+
+// Initialize an instance of CordbObjectValue, filling in the m_pObjectCopy field and, if appropriate,
+// string information.
+// Arguments: none
+// ReturnValue: S_OK on success or E_OUTOFMEMORY or read process memory errors on failure
+HRESULT CordbObjectValue::Init()
+{
+ INTERNAL_SYNC_API_ENTRY(this->GetProcess()); //
+ LOG((LF_CORDB,LL_INFO1000,"Invoking COV::Init\n"));
+
+ HRESULT hr = S_OK;
+
+ _ASSERTE (m_info.objTypeData.elementType != ELEMENT_TYPE_GENERICINST);
+ _ASSERTE (m_info.objTypeData.elementType != ELEMENT_TYPE_VAR);
+ _ASSERTE (m_info.objTypeData.elementType != ELEMENT_TYPE_MVAR);
+
+ // Copy the entire object over to this process.
+ m_pObjectCopy = new (nothrow) BYTE[m_size];
+
+ if (m_pObjectCopy == NULL)
+ return E_OUTOFMEMORY;
+
+ EX_TRY
+ {
+ m_valueHome.GetValue(MemoryRange(m_pObjectCopy, m_size)); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+ IfFailRet(hr);
+
+ // Compute offsets in bytes to the locals and to a string if this is a
+ // string object.
+ m_objectLocalVars = m_pObjectCopy + m_info.objOffsetToVars;
+
+ if (m_info.objTypeData.elementType == ELEMENT_TYPE_STRING)
+ m_stringBuffer = m_pObjectCopy + m_info.stringInfo.offsetToStringBase;
+
+ return hr;
+} // CordbObjectValue::Init
+
+// CordbObjectValue::GetThreadOwningMonitorLock
+// If a managed thread owns the monitor lock on this object then *ppThread
+// will point to that thread and S_OK will be returned. The thread object is valid
+// until the thread exits. *pAcquisitionCount will indicate the number of times
+// this thread would need to release the lock before it returns to being
+// unowned.
+// If no managed thread owns the monitor lock on this object then *ppThread
+// and pAcquisitionCount will be unchanged and S_FALSE returned.
+// If ppThread or pAcquisitionCount is not a valid pointer the result is
+// undefined.
+// If any error occurs such that it cannot be determined which, if any, thread
+// owns the monitor lock on this object then a failing HRESULT will be returned
+HRESULT CordbObjectValue::GetThreadOwningMonitorLock(ICorDebugThread **ppThread, DWORD *pAcquisitionCount)
+{
+ PUBLIC_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ return CordbHeapValue3Impl::GetThreadOwningMonitorLock(GetProcess(),
+ GetValueHome()->GetAddress(),
+ ppThread,
+ pAcquisitionCount);
+}
+
+// CordbObjectValue::GetMonitorEventWaitList
+// Provides an ordered list of threads which are queued on the event associated
+// with a monitor lock. The first thread in the list is the first thread which
+// will be released by the next call to Monitor.Pulse, the next thread in the list
+// will be released on the following call, and so on.
+// If this list is non-empty S_OK will be returned, if it is empty S_FALSE
+// will be returned (the enumeration is still valid, just empty).
+// In either case the enumeration interface is only usable for the duration
+// of the current synchronized state, however the threads interfaces dispensed
+// from it are valid until the thread exits.
+// If ppThread is not a valid pointer the result is undefined.
+// If any error occurs such that it cannot be determined which, if any, threads
+// are waiting for the monitor then a failing HRESULT will be returned
+HRESULT CordbObjectValue::GetMonitorEventWaitList(ICorDebugThreadEnum **ppThreadEnum)
+{
+ PUBLIC_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ return CordbHeapValue3Impl::GetMonitorEventWaitList(GetProcess(),
+ GetValueHome()->GetAddress(),
+ ppThreadEnum);
+}
+
+HRESULT CordbObjectValue::EnumerateExceptionCallStack(ICorDebugExceptionObjectCallStackEnum** ppCallStackEnum)
+{
+ if (!ppCallStackEnum)
+ return E_INVALIDARG;
+
+ *ppCallStackEnum = NULL;
+
+ HRESULT hr = S_OK;
+ CorDebugExceptionObjectStackFrame* pStackFrames = NULL;
+
+ PUBLIC_API_BEGIN(this);
+
+ CORDB_ADDRESS objAddr = m_valueHome.GetAddress();
+
+ IDacDbiInterface* pDAC = GetProcess()->GetDAC();
+ VMPTR_Object vmObj = pDAC->GetObject(objAddr);
+
+ DacDbiArrayList<DacExceptionCallStackData> dacStackFrames;
+
+ pDAC->GetStackFramesFromException(vmObj, dacStackFrames);
+ int stackFramesLength = dacStackFrames.Count();
+
+ if (stackFramesLength > 0)
+ {
+ pStackFrames = new CorDebugExceptionObjectStackFrame[stackFramesLength];
+ for (int index = 0; index < stackFramesLength; ++index)
+ {
+ DacExceptionCallStackData& currentDacFrame = dacStackFrames[index];
+ CorDebugExceptionObjectStackFrame& currentStackFrame = pStackFrames[index];
+
+ CordbAppDomain* pAppDomain = GetProcess()->LookupOrCreateAppDomain(currentDacFrame.vmAppDomain);
+ CordbModule* pModule = pAppDomain->LookupOrCreateModule(currentDacFrame.vmDomainFile);
+
+ hr = pModule->QueryInterface(IID_ICorDebugModule, reinterpret_cast<void**>(&currentStackFrame.pModule));
+ _ASSERTE(SUCCEEDED(hr));
+
+ currentStackFrame.ip = currentDacFrame.ip;
+ currentStackFrame.methodDef = currentDacFrame.methodDef;
+ currentStackFrame.isLastForeignExceptionFrame = currentDacFrame.isLastForeignExceptionFrame;
+ }
+ }
+
+ CordbExceptionObjectCallStackEnumerator* callStackEnum = new CordbExceptionObjectCallStackEnumerator(GetProcess(), pStackFrames, stackFramesLength);
+ GetProcess()->GetContinueNeuterList()->Add(GetProcess(), callStackEnum);
+
+ hr = callStackEnum->QueryInterface(IID_ICorDebugExceptionObjectCallStackEnum, reinterpret_cast<void**>(ppCallStackEnum));
+ _ASSERTE(SUCCEEDED(hr));
+
+ PUBLIC_API_END(hr);
+
+ if (pStackFrames)
+ delete[] pStackFrames;
+
+ return hr;
+}
+
+HRESULT CordbObjectValue::IsExceptionObject()
+{
+ HRESULT hr = S_OK;
+
+ if (m_info.objTypeData.elementType != ELEMENT_TYPE_CLASS)
+ {
+ hr = S_FALSE;
+ }
+ else
+ {
+ CORDB_ADDRESS objAddr = m_valueHome.GetAddress();
+
+ if (objAddr == NULL)
+ {
+ // object is a literal
+ hr = S_FALSE;
+ }
+ else
+ {
+ IDacDbiInterface* pDAC = GetProcess()->GetDAC();
+
+ VMPTR_Object vmObj = pDAC->GetObject(objAddr);
+ BOOL fIsException = pDAC->IsExceptionObject(vmObj);
+
+ if (!fIsException)
+ hr = S_FALSE;
+ }
+ }
+
+ return hr;
+}
+
+HRESULT CordbObjectValue::IsRcw()
+{
+ HRESULT hr = S_OK;
+
+ if (m_info.objTypeData.elementType != ELEMENT_TYPE_CLASS)
+ {
+ hr = S_FALSE;
+ }
+ else
+ {
+ CORDB_ADDRESS objAddr = m_valueHome.GetAddress();
+
+ if (objAddr == NULL)
+ {
+ // object is a literal
+ hr = S_FALSE;
+ }
+ else
+ {
+ IDacDbiInterface* pDAC = GetProcess()->GetDAC();
+
+ VMPTR_Object vmObj = pDAC->GetObject(objAddr);
+ BOOL fIsRcw = pDAC->IsRcw(vmObj);
+
+ if (!fIsRcw)
+ hr = S_FALSE;
+ }
+ }
+
+ return hr;
+}
+
+HRESULT CordbObjectValue::GetCachedInterfaceTypes(
+ BOOL bIInspectableOnly,
+ ICorDebugTypeEnum * * ppInterfacesEnum)
+{
+#if !defined(FEATURE_COMINTEROP)
+
+ return E_NOTIMPL;
+
+#else
+
+ HRESULT hr = S_OK;
+
+ PUBLIC_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ VALIDATE_POINTER_TO_OBJECT(ppInterfacesEnum, ICorDebugTypeEnum **);
+
+ _ASSERTE(m_fIsRcw);
+
+ EX_TRY
+ {
+ *ppInterfacesEnum = NULL;
+
+ NewArrayHolder<CordbType*> pItfs(NULL);
+
+ // retrieve interface types
+ DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> dacInterfaces;
+
+ IDacDbiInterface* pDAC = GetProcess()->GetDAC();
+
+ CORDB_ADDRESS objAddr = m_valueHome.GetAddress();
+ VMPTR_Object vmObj = pDAC->GetObject(objAddr);
+
+ // retrieve type info from LS
+ pDAC->GetRcwCachedInterfaceTypes(vmObj, m_appdomain->GetADToken(),
+ bIInspectableOnly, &dacInterfaces);
+
+ // synthesize CordbType instances
+ int cItfs = dacInterfaces.Count();
+ if (cItfs > 0)
+ {
+ pItfs = new CordbType*[cItfs];
+ for (int n = 0; n < cItfs; ++n)
+ {
+ hr = CordbType::TypeDataToType(m_appdomain,
+ &(dacInterfaces[n]),
+ &pItfs[n]);
+ }
+ }
+
+ // build a type enumerator
+ CordbTypeEnum* pTypeEnum = CordbTypeEnum::Build(m_appdomain, GetProcess()->GetContinueNeuterList(), cItfs, pItfs);
+ if ( pTypeEnum == NULL )
+ {
+ IfFailThrow(E_OUTOFMEMORY);
+ }
+
+ (*ppInterfacesEnum) = static_cast<ICorDebugTypeEnum*> (pTypeEnum);
+ pTypeEnum->ExternalAddRef();
+
+ }
+ EX_CATCH_HRESULT(hr);
+
+ return hr;
+
+#endif
+}
+
+HRESULT CordbObjectValue::GetCachedInterfacePointers(
+ BOOL bIInspectableOnly,
+ ULONG32 celt,
+ ULONG32 *pcEltFetched,
+ CORDB_ADDRESS * ptrs)
+{
+#if !defined(FEATURE_COMINTEROP)
+
+ return E_NOTIMPL;
+
+#else
+
+ PUBLIC_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ _ASSERTE(m_fIsRcw);
+
+ if (pcEltFetched == NULL && (ptrs == NULL || celt == 0))
+ return E_INVALIDARG;
+
+ HRESULT hr = S_OK;
+ ULONG32 cItfs = 0;
+
+ // retrieve interface types
+
+ CORDB_ADDRESS objAddr = m_valueHome.GetAddress();
+
+ DacDbiArrayList<CORDB_ADDRESS> dacItfPtrs;
+ EX_TRY
+ {
+ IDacDbiInterface* pDAC = GetProcess()->GetDAC();
+ VMPTR_Object vmObj = pDAC->GetObject(objAddr);
+
+ // retrieve type info from LS
+ pDAC->GetRcwCachedInterfacePointers(vmObj, bIInspectableOnly, &dacItfPtrs);
+ }
+ EX_CATCH_HRESULT(hr);
+ IfFailRet(hr);
+
+ // synthesize CordbType instances
+ cItfs = (ULONG32)dacItfPtrs.Count();
+
+ if (pcEltFetched != NULL && ptrs == NULL)
+ {
+ *pcEltFetched = cItfs;
+ return S_OK;
+ }
+
+ if (pcEltFetched != NULL)
+ {
+ *pcEltFetched = (cItfs <= celt ? cItfs : celt);
+ }
+
+ if (ptrs != NULL && *pcEltFetched > 0)
+ {
+ for (ULONG32 i = 0; i < *pcEltFetched; ++i)
+ ptrs[i] = dacItfPtrs[i];
+ }
+
+ return (*pcEltFetched == celt ? S_OK : S_FALSE);
+
+#endif
+}
+
+
+/* ------------------------------------------------------------------------- *
+ * Value Class Object
+ * ------------------------------------------------------------------------- */
+
+// constructor
+// Arguments:
+// input: pAppdomain - app domain to which the value belongs
+// pType - type information for the value
+// remoteValue - buffer describing the target location of the value
+// ppRemoteRegAddr - describes the register information if the value resides in a register
+// Note: May throw E_OUTOFMEMORY
+CordbVCObjectValue::CordbVCObjectValue(CordbAppDomain * pAppdomain,
+ CordbType * pType,
+ TargetBuffer remoteValue,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr)
+
+ // We'd like to neuter this on Continue (not just exit), but it may be a breaking change,
+ // especially for ValueTypes that don't have any GC refs in them.
+ : CordbValue(pAppdomain,
+ pType,
+ remoteValue.pAddress,
+ false,
+ pAppdomain->GetSweepableExitNeuterList()),
+ m_pObjectCopy(NULL),
+ m_pValueHome(NULL)
+{
+ // instantiate the value home
+ NewHolder<ValueHome> pHome(NULL);
+
+ if (remoteValue.IsEmpty())
+ {
+ pHome = (new RegisterValueHome(pAppdomain->GetProcess(), ppRemoteRegAddr));
+ }
+ else
+ {
+ pHome = (new VCRemoteValueHome(pAppdomain->GetProcess(), remoteValue));
+ }
+ m_pValueHome = pHome.GetValue(); // throws
+ pHome.SuppressRelease();
+} // CordbVCObjectValue::CordbVCObjectValue
+
+// destructor
+CordbVCObjectValue::~CordbVCObjectValue()
+{
+ DTOR_ENTRY(this);
+
+ _ASSERTE(IsNeutered());
+
+ // Destroy the copy of the object.
+ if (m_pObjectCopy != NULL)
+ {
+ delete [] m_pObjectCopy;
+ m_pObjectCopy = NULL;
+ }
+
+ // destroy the value home
+ if (m_pValueHome != NULL)
+ {
+ delete m_pValueHome;
+ m_pValueHome = NULL;
+}
+} // CordbVCObjectValue::~CordbVCObjectValue
+
+HRESULT CordbVCObjectValue::QueryInterface(REFIID id, void **pInterface)
+{
+ if (id == IID_ICorDebugValue)
+ {
+ *pInterface = static_cast<ICorDebugValue*>(static_cast<ICorDebugObjectValue*>(this));
+ }
+ else if (id == IID_ICorDebugValue2)
+ {
+ *pInterface = static_cast<ICorDebugValue2*>(this);
+ }
+ else if (id == IID_ICorDebugValue3)
+ {
+ *pInterface = static_cast<ICorDebugValue3*>(this);
+ }
+ else if (id == IID_ICorDebugObjectValue)
+ {
+ *pInterface = static_cast<ICorDebugObjectValue*>(this);
+ }
+ else if (id == IID_ICorDebugObjectValue2)
+
+ {
+ *pInterface = static_cast<ICorDebugObjectValue2*>(this);
+ }
+ else if (id == IID_ICorDebugGenericValue)
+ {
+ *pInterface = static_cast<ICorDebugGenericValue*>(this);
+ }
+ else if (id == IID_IUnknown)
+ {
+ *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugObjectValue*>(this));
+ }
+ else
+ {
+ *pInterface = NULL;
+ return E_NOINTERFACE;
+ }
+
+ ExternalAddRef();
+ return S_OK;
+} // CordbVCObjectValue::QueryInterface
+
+// returns the basic type of the ICDValue
+// Arguments:
+// output: pType - the type of the ICDValue (always E_T_VALUETYPE)
+// ReturnValue: S_OK on success or E_INVALIDARG if pType is NULL
+HRESULT CordbVCObjectValue::GetType(CorElementType *pType)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(pType, CorElementType *);
+
+ *pType = ELEMENT_TYPE_VALUETYPE;
+ return S_OK;
+} // CordbVCObjectValue::GetType
+
+// public API to get the CordbClass field
+// Arguments:
+// output: ppClass - holds a pointer to the ICDClass instance belonging to this
+// Return Value: S_OK on success, CORDBG_E_OBJECT_NEUTERED or synchronization errors on failure
+HRESULT CordbVCObjectValue::GetClass(ICorDebugClass **ppClass)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ *ppClass = (ICorDebugClass*) GetClass();
+
+ if (*ppClass != NULL)
+ (*ppClass)->AddRef();
+
+ return S_OK;
+} // CordbVCObjectValue::GetClass
+
+// internal method to get the CordbClass field
+// Arguments: none
+// ReturnValue: the instance of CordbClass belonging to this VC object
+CordbClass *CordbVCObjectValue::GetClass()
+{
+ CordbClass *tycon;
+ Instantiation inst;
+ m_type->DestConstructedType(&tycon, &inst);
+ return tycon;
+} // CordbVCObjectValue::GetClass
+
+//-----------------------------------------------------------------------------
+//
+// Finds the given field of the given type in the object and returns an ICDValue for it.
+//
+// Arguments:
+// pType - The type of the field
+// fieldDef - The field's metadata def.
+// ppValue - OUT: the ICDValue for the field.
+//
+// Returns:
+// S_OK on success, CORDBG_E_OBJECT_NEUTERED, E_INVALIDARG, CORDBG_E_ENC_HANGING_FIELD, or various other
+// failure codes
+HRESULT CordbVCObjectValue::GetFieldValueForType(ICorDebugType * pType,
+ mdFieldDef fieldDef,
+ ICorDebugValue ** ppValue)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ HRESULT hr = S_OK;
+ EX_TRY
+ {
+ // Validate the token.
+ if ((m_type->m_pClass == NULL) || !m_type->m_pClass->GetModule()->GetMetaDataImporter()->IsValidToken(fieldDef))
+ {
+ ThrowHR(E_INVALIDARG);
+ }
+
+
+ CordbType * pCordbType;
+
+ //
+ // <TODO>@todo: need to ensure that pClass is really on the class
+ // hierarchy of m_class!!!</TODO>
+ //
+ if (pType == NULL)
+ {
+ pCordbType = m_type;
+ }
+ else
+ {
+ pCordbType = static_cast<CordbType *> (pType);
+ }
+
+ FieldData * pFieldData;
+
+ #ifdef _DEBUG
+ pFieldData = NULL;
+ #endif
+
+ hr = pCordbType->GetFieldInfo(fieldDef, &pFieldData);
+ _ASSERTE(hr != CORDBG_E_ENC_HANGING_FIELD);
+
+ // If we get back CORDBG_E_ENC_HANGING_FIELD we'll just fail -
+ // value classes should not be able to add fields once they're loaded,
+ // since the new fields _can't_ be contiguous with the old fields,
+ // and having all the fields contiguous is kinda the point of a V.C.
+ IfFailThrow(hr);
+
+ _ASSERTE(pFieldData != NULL);
+
+ CordbModule * pModule = pCordbType->m_pClass->GetModule();
+
+ SigParser sigParser;
+ IfFailThrow(pFieldData->GetFieldSignature(pModule, &sigParser));
+
+ // <TODO>
+ // How can I assert that I have exactly one field?
+ // </TODO>
+ CordbType * pFieldType;
+
+ IfFailThrow(CordbType::SigToType(pModule, &sigParser, &(pCordbType->m_inst), &pFieldType));
+
+ _ASSERTE(pFieldData->OkToGetOrSetInstanceOffset());
+ // Compute the address of the field contents in our local object cache
+ SIZE_T fieldOffset = pFieldData->GetInstanceOffset();
+ ULONG32 size = GetSizeForType(pFieldType, kUnboxed);
+
+ // verify that the field starts before the end of m_pObjectCopy
+ _ASSERTE(fieldOffset < m_size);
+ _ASSERTE(fieldOffset + size <= m_size);
+
+ m_pValueHome->CreateInternalValue(pFieldType,
+ fieldOffset,
+ m_pObjectCopy + fieldOffset,
+ size,
+ ppValue); // throws
+
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+} // CordbVCObjectValue::GetFieldValueForType
+
+// gets an ICDValue to represent a field of the VC object
+// Arguments:
+// input: pClass - the class information for this object (needed to get the parent class information)
+// fieldDef - field token for the desired field
+// output: ppValue - on success, the ICDValue representing the desired field
+// Return Value: S_OK on success, CORDBG_E_OBJECT_NEUTERED, CORDBG_E_CLASS_NOT_LOADED, E_INVALIDARG, OOM,
+// CORDBG_E_ENC_HANGING_FIELD, or various other failure codes
+HRESULT CordbVCObjectValue::GetFieldValue(ICorDebugClass *pClass,
+ mdFieldDef fieldDef,
+ ICorDebugValue **ppValue)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ VALIDATE_POINTER_TO_OBJECT(pClass, ICorDebugClass *);
+ VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **);
+
+ HRESULT hr;
+ _ASSERTE(m_type);
+
+ if (m_type->m_elementType != ELEMENT_TYPE_CLASS &&
+ m_type->m_elementType != ELEMENT_TYPE_VALUETYPE)
+ {
+ return E_INVALIDARG;
+ }
+
+ RSExtSmartPtr<CordbType> relevantType;
+
+ if (FAILED (hr= m_type->GetParentType((CordbClass *) pClass, &relevantType)))
+ {
+ return hr;
+ }
+ // Upon exit relevantType will either be the appropriate type for the
+ // class we're looking for.
+
+ hr = GetFieldValueForType(relevantType, fieldDef, ppValue);
+ // GetParentType ands one reference to relevantType, holder dtor releases that.
+ return hr;
+
+} // CordbVCObjectValue::GetFieldValue
+
+// get a copy of the VC object
+// Arguments:
+// output: pTo - a caller-allocated buffer to hold the copy
+// Return Value: S_OK on success, CORDBG_E_OBJECT_NEUTERED on failure
+// Note: The caller must ensure the buffer is large enough to hold the value (by a previous call to GetSize)
+// and is responsible for allocation and deallocation.
+HRESULT CordbVCObjectValue::GetValue(void *pTo)
+{
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(pTo, BYTE, m_size, false, true);
+ FAIL_IF_NEUTERED(this);
+
+ // Copy out the value, which is the whole object.
+ memcpy(pTo, m_pObjectCopy, m_size);
+
+ return S_OK;
+} // CordbVCObjectValue::GetValue
+
+// set the value of a VC object
+// Arguments:
+// input: pSrc - buffer containing the new value. Allocated and managed by the caller.
+// Return Value: S_OK on success, CORDBG_E_OBJECT_NEUTERED, synchronization errors, E_INVALIDARG, write
+// process memory errors, CORDBG_E_CLASS_NOT_LOADED or OOM on failure
+HRESULT CordbVCObjectValue::SetValue(void * pSrc)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ HRESULT hr = S_OK;
+
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(pSrc, BYTE, m_size, true, false);
+
+ // Can't change literals...
+ if (m_isLiteral)
+ return E_INVALIDARG;
+
+ if (m_type)
+ {
+ IfFailRet(m_type->Init(FALSE));
+ }
+
+ EX_TRY
+ {
+ m_pValueHome->SetValue(MemoryRange(pSrc, m_size), m_type); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+ if (SUCCEEDED(hr))
+ {
+ // That worked, so update the copy of the value we have over here.
+ memcpy(m_pObjectCopy, pSrc, m_size);
+ }
+
+ return hr;
+} // CordbVCObjectValue::SetValue
+
+HRESULT CordbVCObjectValue::GetVirtualMethod(mdMemberRef memberRef,
+ ICorDebugFunction **ppFunction)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT CordbVCObjectValue::GetVirtualMethodAndType(mdMemberRef memberRef,
+ ICorDebugFunction **ppFunction,
+ ICorDebugType **ppType)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT CordbVCObjectValue::GetContext(ICorDebugContext **ppContext)
+{
+ return E_NOTIMPL;
+}
+
+// self-identifier--always returns true as long as pbIsValueClass is non-Null
+HRESULT CordbVCObjectValue::IsValueClass(BOOL *pbIsValueClass)
+{
+ if (pbIsValueClass)
+ *pbIsValueClass = TRUE;
+
+ return S_OK;
+} // CordbVCObjectValue::IsValueClass
+
+HRESULT CordbVCObjectValue::GetManagedCopy(IUnknown **ppObject)
+{
+ // This function is deprecated
+ return E_NOTIMPL;
+}
+
+HRESULT CordbVCObjectValue::SetFromManagedCopy(IUnknown *pObject)
+{
+ // This function is deprecated
+ return E_NOTIMPL;
+}
+
+ //
+// CordbVCObjectValue::Init
+//
+// Description
+// Initializes the Right-Side's representation of a Value Class object.
+// Parameters
+// input: localValue - buffer containing the value if this instance of CordbObjectValue
+// was a field or array element of an existing value, otherwise this
+// will have a start address equal to NULL
+// Returns
+// HRESULT
+// S_OK if the function completed normally
+// failing HR otherwise
+// Exceptions
+// None
+//
+HRESULT CordbVCObjectValue::Init(MemoryRange localValue)
+{
+ HRESULT hr = S_OK;
+
+ INTERNAL_SYNC_API_ENTRY(this->GetProcess()); //
+
+ // Get the object size from the class
+ ULONG32 size;
+ IfFailRet( m_type->GetUnboxedObjectSize(&size) );
+ m_size = size;
+
+ // Copy the entire object over to this process.
+ m_pObjectCopy = new (nothrow) BYTE[m_size];
+
+ if (m_pObjectCopy == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (localValue.StartAddress() != NULL)
+ {
+ // The data is already in the local address space. Go ahead and copy it
+ // from there.
+ // localValue.StartAddress points to:
+ // 1. A field from the local cached copy belonging to an instance of CordbVCObjectValue (different
+ // instance from "this") or CordbObjectValue
+ // 2. An element in the locally cached subrange of an array belonging to an instance of CordbArrayValue
+ // 3. The address of a particular register in the register display of an instance of CordbNativeFrame
+ // for an enregistered value type. In this case, it's possible that the size of the value is
+ // smaller than the size of a full register. For that reason, we can't just use localValue.Size()
+ // as the number of bytes to copy, because only enough space for the value has been allocated.
+ _ASSERTE(localValue.Size() >= m_size);
+ localCopy(m_pObjectCopy, MemoryRange(localValue.StartAddress(), m_size));
+ return S_OK;
+ }
+
+ EX_TRY
+ {
+ m_pValueHome->GetValue(MemoryRange(m_pObjectCopy, m_size)); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+} // CordbVCObjectValue::Init
+
+/* ------------------------------------------------------------------------- *
+ * Box Value class
+ * ------------------------------------------------------------------------- */
+
+// constructor
+// Arguments:
+// input: appdomain - app domain to which the value belongs
+// type - type information for the boxed value
+// remoteValue - buffer describing the remote location of the value
+// size - size of the value
+// offsetToVars - offset from the beginning of the value to the first field of the value
+CordbBoxValue::CordbBoxValue(CordbAppDomain *appdomain,
+ CordbType *type,
+ TargetBuffer remoteValue,
+ ULONG32 size,
+ SIZE_T offsetToVars)
+ : CordbValue(appdomain, type, remoteValue.pAddress, false, appdomain->GetProcess()->GetContinueNeuterList()),
+ m_offsetToVars(offsetToVars),
+ m_valueHome(appdomain->GetProcess(), remoteValue)
+{
+ m_size = size;
+} // CordbBoxValue::CordbBoxValue
+
+// destructor
+CordbBoxValue::~CordbBoxValue()
+{
+ DTOR_ENTRY(this);
+ _ASSERTE(IsNeutered());
+} // CordbBoxValue::~CordbBoxValue
+
+HRESULT CordbBoxValue::QueryInterface(REFIID id, void **pInterface)
+{
+ if (id == IID_ICorDebugValue)
+ {
+ *pInterface = static_cast<ICorDebugValue*>(static_cast<ICorDebugBoxValue*>(this));
+ }
+ else if (id == IID_ICorDebugValue2)
+ {
+ *pInterface = static_cast<ICorDebugValue2*>(this);
+ }
+ else if (id == IID_ICorDebugValue3)
+ {
+ *pInterface = static_cast<ICorDebugValue3*>(this);
+ }
+ else if (id == IID_ICorDebugBoxValue)
+ {
+ *pInterface = static_cast<ICorDebugBoxValue*>(this);
+ }
+ else if (id == IID_ICorDebugGenericValue)
+ {
+ *pInterface = static_cast<ICorDebugGenericValue*>(this);
+ }
+ else if (id == IID_ICorDebugHeapValue)
+ {
+ *pInterface = static_cast<ICorDebugHeapValue*>(this);
+ }
+ else if (id == IID_ICorDebugHeapValue2)
+ {
+ *pInterface = static_cast<ICorDebugHeapValue2*>(this);
+ }
+ else if (id == IID_ICorDebugHeapValue3)
+ {
+ *pInterface = static_cast<ICorDebugHeapValue3*>(this);
+ }
+ else if (id == IID_IUnknown)
+ {
+ *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugBoxValue*>(this));
+ }
+ else
+ {
+ *pInterface = NULL;
+ return E_NOINTERFACE;
+ }
+
+ ExternalAddRef();
+ return S_OK;
+} // CordbBoxValue::QueryInterface
+
+// returns the basic type of the ICDValue
+// Arguments:
+// output: pType - the type of the ICDValue (always E_T_CLASS)
+// ReturnValue: S_OK on success or E_INVALIDARG if pType is NULL
+HRESULT CordbBoxValue::GetType(CorElementType *pType)
+{
+ VALIDATE_POINTER_TO_OBJECT(pType, CorElementType *);
+
+ *pType = ELEMENT_TYPE_CLASS;
+
+ return (S_OK);
+} // CordbBoxValue::GetType
+
+HRESULT CordbBoxValue::IsValid(BOOL *pbValid)
+{
+ VALIDATE_POINTER_TO_OBJECT(pbValid, BOOL *);
+
+ // <TODO>@todo: implement tracking of objects across collections.</TODO>
+
+ return E_NOTIMPL;
+}
+
+HRESULT CordbBoxValue::CreateRelocBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint)
+{
+ VALIDATE_POINTER_TO_OBJECT(ppBreakpoint, ICorDebugValueBreakpoint **);
+
+ return E_NOTIMPL;
+}
+
+// Creates a handle of the given type for this heap value.
+// Not Implemented In-Proc.
+// Create a handle for a heap object.
+// @todo: How to prevent this being called by non-heap object?
+// Arguments:
+// input: handleType - type of the handle to be created
+// output: ppHandle - on success, the newly created handle
+// Return Value: S_OK on success or E_INVALIDARG, E_OUTOFMEMORY, or CORDB_E_HELPER_MAY_DEADLOCK
+HRESULT CordbBoxValue::CreateHandle(
+ CorDebugHandleType handleType,
+ ICorDebugHandleValue ** ppHandle)
+{
+ PUBLIC_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ return CordbValue::InternalCreateHandle(handleType, ppHandle);
+} // CordbBoxValue::CreateHandle
+
+HRESULT CordbBoxValue::GetValue(void *pTo)
+{
+ // Can't get a whole copy of a box.
+ return E_INVALIDARG;
+}
+
+HRESULT CordbBoxValue::SetValue(void *pFrom)
+{
+ // You're not allowed to set a box value.
+ return E_INVALIDARG;
+}
+
+// gets the unboxed value from this boxed value
+// Arguments:
+// output: ppObject - pointer to an instance of ICDValue representing the unboxed value, unless ppObject
+// is NULL
+// Return Value: S_OK on success or a variety of possible failures: OOM, E_FAIL, errors from
+// ReadProcessMemory.
+HRESULT CordbBoxValue::GetObject(ICorDebugObjectValue **ppObject)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(ppObject, ICorDebugObjectValue **);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ ULONG32 size;
+ m_type->GetUnboxedObjectSize(&size);
+
+ HRESULT hr = S_OK;
+ EX_TRY
+ {
+ m_valueHome.CreateInternalValue(m_type,
+ m_offsetToVars,
+ NULL,
+ size,
+ reinterpret_cast<ICorDebugValue **>(ppObject)); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+} // CordbBoxValue::GetObject
+
+// If a managed thread owns the monitor lock on this object then *ppThread
+// will point to that thread and S_OK will be returned. The thread object is valid
+// until the thread exits. *pAcquisitionCount will indicate the number of times
+// this thread would need to release the lock before it returns to being
+// unowned.
+// If no managed thread owns the monitor lock on this object then *ppThread
+// and pAcquisitionCount will be unchanged and S_FALSE returned.
+// If ppThread or pAcquisitionCount is not a valid pointer the result is
+// undefined.
+// If any error occurs such that it cannot be determined which, if any, thread
+// owns the monitor lock on this object then a failing HRESULT will be returned
+HRESULT CordbBoxValue::GetThreadOwningMonitorLock(ICorDebugThread **ppThread, DWORD *pAcquisitionCount)
+{
+ PUBLIC_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ return CordbHeapValue3Impl::GetThreadOwningMonitorLock(GetProcess(),
+ GetValueHome()->GetAddress(),
+ ppThread,
+ pAcquisitionCount);
+}
+
+// Provides an ordered list of threads which are queued on the event associated
+// with a monitor lock. The first thread in the list is the first thread which
+// will be released by the next call to Monitor.Pulse, the next thread in the list
+// will be released on the following call, and so on.
+// If this list is non-empty S_OK will be returned, if it is empty S_FALSE
+// will be returned (the enumeration is still valid, just empty).
+// In either case the enumeration interface is only usable for the duration
+// of the current synchronized state, however the threads interfaces dispensed
+// from it are valid until the thread exits.
+// If ppThread is not a valid pointer the result is undefined.
+// If any error occurs such that it cannot be determined which, if any, threads
+// are waiting for the monitor then a failing HRESULT will be returned
+HRESULT CordbBoxValue::GetMonitorEventWaitList(ICorDebugThreadEnum **ppThreadEnum)
+{
+ PUBLIC_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ return CordbHeapValue3Impl::GetMonitorEventWaitList(GetProcess(),
+ GetValueHome()->GetAddress(),
+ ppThreadEnum);
+}
+
+
+/* ------------------------------------------------------------------------- *
+ * Array Value class
+ * ------------------------------------------------------------------------- */
+
+// The size of the buffer we allocate to hold array elements.
+// Note that since we must be able to hold at least one element, we may
+// allocate larger than the cache size here.
+// Also, this cache doesn't include a small header used to store the rank vectors
+#ifdef _DEBUG
+// For debug, use a small size to cause more churn
+ #define ARRAY_CACHE_SIZE (1000)
+#else
+// For release, guess 4 pages should be enough. Subtract some bytes to store
+// the header so that that doesn't push us onto another page. (We guess a reasonable
+// header size, but it's ok if it's larger).
+ #define ARRAY_CACHE_SIZE (4 * 4096 - 24)
+#endif
+
+// constructor
+// Arguments:
+// input:
+// pAppDomain - app domain to which the value belongs
+// pType - type information for the value
+// pObjectInfo - array specific type information
+// remoteValue - buffer describing the remote location of the value
+CordbArrayValue::CordbArrayValue(CordbAppDomain * pAppdomain,
+ CordbType * pType,
+ DebuggerIPCE_ObjectData * pObjectInfo,
+ TargetBuffer remoteValue)
+ : CordbValue(pAppdomain,
+ pType,
+ remoteValue.pAddress,
+ false,
+ pAppdomain->GetProcess()->GetContinueNeuterList()),
+ m_info(*pObjectInfo),
+ m_pObjectCopy(NULL),
+ m_valueHome(pAppdomain->GetProcess(), remoteValue)
+{
+ m_size = m_info.objSize;
+ pType->DestUnaryType(&m_elemtype);
+
+// Set range to illegal values to force a load on first access
+ m_idxLower = m_idxUpper = (SIZE_T) -1;
+} // CordbArrayValue::CordbArrayValue
+
+// destructor
+CordbArrayValue::~CordbArrayValue()
+{
+ DTOR_ENTRY(this);
+ _ASSERTE(IsNeutered());
+
+ // Destroy the copy of the object.
+ if (m_pObjectCopy != NULL)
+ delete [] m_pObjectCopy;
+} // CordbArrayValue::~CordbArrayValue
+
+HRESULT CordbArrayValue::QueryInterface(REFIID id, void **pInterface)
+{
+ if (id == IID_ICorDebugValue)
+ {
+ *pInterface = static_cast<ICorDebugValue*>(static_cast<ICorDebugArrayValue*>(this));
+ }
+ else if (id == IID_ICorDebugValue2)
+ {
+ *pInterface = static_cast<ICorDebugValue2*>(this);
+ }
+ else if (id == IID_ICorDebugValue3)
+ {
+ *pInterface = static_cast<ICorDebugValue3*>(this);
+ }
+ else if (id == IID_ICorDebugArrayValue)
+ {
+ *pInterface = static_cast<ICorDebugArrayValue*>(this);
+ }
+ else if (id == IID_ICorDebugGenericValue)
+ {
+ *pInterface = static_cast<ICorDebugGenericValue*>(this);
+ }
+ else if (id == IID_ICorDebugHeapValue)
+ {
+ *pInterface = static_cast<ICorDebugHeapValue*>(this);
+ }
+ else if (id == IID_ICorDebugHeapValue2)
+ {
+ *pInterface = static_cast<ICorDebugHeapValue2*>(this);
+ }
+ else if (id == IID_ICorDebugHeapValue3)
+ {
+ *pInterface = static_cast<ICorDebugHeapValue3*>(this);
+ }
+ else if (id == IID_IUnknown)
+ {
+ *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugArrayValue*>(this));
+ }
+ else
+ {
+ *pInterface = NULL;
+ return E_NOINTERFACE;
+ }
+
+ ExternalAddRef();
+ return S_OK;
+} // CordbArrayValue::QueryInterface
+
+// gets the type of the array elements
+// Arguments:
+// output: pType - the element type unless pType is NULL
+// Return Value: S_OK on success or E_INVALIDARG if pType is null
+HRESULT CordbArrayValue::GetElementType(CorElementType *pType)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(pType, CorElementType *);
+
+ *pType = m_elemtype->m_elementType;
+ return S_OK;
+} // CordbArrayValue::GetElementType
+
+
+// gets the rank of the array
+// Arguments:
+// output: pnRank - the rank of the array unless pnRank is null
+// Return Value: S_OK on success or E_INVALIDARG if pnRank is null
+HRESULT CordbArrayValue::GetRank(ULONG32 *pnRank)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(pnRank, SIZE_T *);
+
+ // Rank info is duplicated for sanity checking - double check it here.
+ _ASSERTE(m_info.arrayInfo.rank == m_type->m_rank);
+ *pnRank = m_type->m_rank;
+ return S_OK;
+} // CordbArrayValue::GetRank
+
+// gets the number of elements in the array
+// Arguments:
+// output: pnCount - the number of dimensions for the array unless pnCount is null
+// Return Value: S_OK on success or E_INVALIDARG if pnCount is null
+HRESULT CordbArrayValue::GetCount(ULONG32 *pnCount)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(pnCount, ULONG32 *);
+
+ *pnCount = (ULONG32)m_info.arrayInfo.componentCount;
+ return S_OK;
+} // CordbArrayValue::GetCount
+
+// get the size of each dimension of the array
+// Arguments:
+// input: cdim - the number of dimensions about which to get dimensions--this must be the same as the rank
+// output: dims - an array to hold the sizes of the dimensions of the array--this is allocated and
+// managed by the caller
+// Return Value: S_OK on success or E_INVALIDARG
+HRESULT CordbArrayValue::GetDimensions(ULONG32 cdim, ULONG32 dims[])
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(dims, SIZE_T, cdim, true, true);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ // Rank info is duplicated for sanity checking - double check it here.
+ _ASSERTE(m_info.arrayInfo.rank == m_type->m_rank);
+ if (cdim != m_type->m_rank)
+ return E_INVALIDARG;
+
+ // SDArrays don't have bounds info, so return the component count.
+ if (cdim == 1)
+ dims[0] = (ULONG32)m_info.arrayInfo.componentCount;
+ else
+ {
+ _ASSERTE(m_info.arrayInfo.offsetToUpperBounds != 0);
+ _ASSERTE(m_arrayUpperBase != NULL);
+
+ // The upper bounds info in the array is the true size of each
+ // dimension.
+ for (unsigned int i = 0; i < cdim; i++)
+ dims[i] = m_arrayUpperBase[i];
+ }
+
+ return S_OK;
+} // CordbArrayValue::GetDimensions
+
+//
+// indicates whether the array has base indices
+// Arguments:
+// output: pbHasBaseIndices - true iff the array has more than one dimension and pbHasBaseIndices is not null
+// Return Value: S_OK on success or E_INVALIDARG if pbHasBaseIndices is null
+HRESULT CordbArrayValue::HasBaseIndicies(BOOL *pbHasBaseIndices)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(pbHasBaseIndices, BOOL *);
+
+ *pbHasBaseIndices = m_info.arrayInfo.offsetToLowerBounds != 0;
+ return S_OK;
+} // CordbArrayValue::HasBaseIndicies
+
+// gets the base indices for a multidimensional array
+// Arguments:
+// input: cdim - the number of dimensions (this must be the same as the actual rank of the array)
+// indices - an array to hold the base indices for the array dimensions (allocated and managed
+// by the caller, it must have space for cdim elements)
+// Return Value: S_OK on success or E_INVALIDARG if cdim is not equal to the array rank or indices is null
+HRESULT CordbArrayValue::GetBaseIndicies(ULONG32 cdim, ULONG32 indices[])
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(indices, SIZE_T, cdim, true, true);
+
+ // Rank info is duplicated for sanity checking - double check it here.
+ _ASSERTE(m_info.arrayInfo.rank == m_type->m_rank);
+ if ((cdim != m_type->m_rank) ||
+ (m_info.arrayInfo.offsetToLowerBounds == 0))
+ return E_INVALIDARG;
+
+ _ASSERTE(m_arrayLowerBase != NULL);
+
+ for (unsigned int i = 0; i < cdim; i++)
+ indices[i] = m_arrayLowerBase[i];
+
+ return S_OK;
+} // CordbArrayValue::GetBaseIndicies
+
+// Get an element at the position indicated by the values in indices (one index for each dimension)
+// Arguments:
+// input: cdim - the number of dimensions and thus the number of elements in indices. This must match
+// the actual rank of the array value.
+// indices - an array of indices to specify the position of the element. For example, to get a[2][1][0],
+// indices would contain 2, 1, and 0 in that order.
+// output: ppValue - an ICDValue representing the element, unless an error occurs
+// Return Value: S_OK on success or E_INVALIDARG if cdim != rank, indices is NULL or ppValue is NULL
+// or a variety of possible failures: OOM, E_FAIL, errors from
+// ReadProcessMemory.
+HRESULT CordbArrayValue::GetElement(ULONG32 cdim,
+ ULONG32 indices[],
+ ICorDebugValue **ppValue)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(indices, SIZE_T, cdim, true, true);
+ VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ *ppValue = NULL;
+
+ // Rank info is duplicated for sanity checking - double check it here.
+ _ASSERTE(m_info.arrayInfo.rank == m_type->m_rank);
+ if ((cdim != m_type->m_rank) || (indices == NULL))
+ return E_INVALIDARG;
+
+ // If the array has lower bounds, adjust the indices.
+ if (m_info.arrayInfo.offsetToLowerBounds != 0)
+ {
+ _ASSERTE(m_arrayLowerBase != NULL);
+
+ for (unsigned int i = 0; i < cdim; i++)
+ indices[i] -= m_arrayLowerBase[i];
+ }
+
+ SIZE_T offset = 0;
+
+ // SDArrays don't have upper bounds
+ if (cdim == 1)
+ {
+ offset = indices[0];
+
+ // Bounds check
+ if (offset >= m_info.arrayInfo.componentCount)
+ return E_INVALIDARG;
+ }
+ else
+ {
+ _ASSERTE(m_info.arrayInfo.offsetToUpperBounds != 0);
+ _ASSERTE(m_arrayUpperBase != NULL);
+
+ // Calculate the offset in bytes for all dimensions.
+ SIZE_T multiplier = 1;
+
+ for (int i = cdim - 1; i >= 0; i--)
+ {
+ // Bounds check
+ if (indices[i] >= m_arrayUpperBase[i])
+ return E_INVALIDARG;
+
+ offset += indices[i] * multiplier;
+ multiplier *= m_arrayUpperBase[i];
+ }
+
+ _ASSERTE(offset < m_info.arrayInfo.componentCount);
+ }
+
+ return GetElementAtPosition((ULONG32)offset, ppValue);
+} // CordbArrayValue::GetElement
+
+// get an ICDValue to represent the element at a given position
+// Arguments:
+// input: nPosition - the offset from the beginning of the array to the element
+// output: ppValue - the ICDValue representing the array element on success
+// Return Value: S_OK on success, E_INVALIDARG or a variety of possible failures: OOM, E_FAIL, errors from
+// ReadProcessMemory.
+HRESULT CordbArrayValue::GetElementAtPosition(ULONG32 nPosition,
+ ICorDebugValue **ppValue)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ if (nPosition >= m_info.arrayInfo.componentCount)
+ {
+ *ppValue = NULL;
+ return E_INVALIDARG;
+ }
+
+ // Rank info is duplicated for sanity checking - double check it here.
+ _ASSERTE(m_info.arrayInfo.rank == m_type->m_rank);
+
+ // The header consists of two DWORDs for each dimension, representing the upper and lower bound for that dimension. A
+ // vector of lower bounds comes first, followed by a vector of upper bounds. We want to copy a range of
+ // elements into m_pObjectCopy following these vectors, so we need to compute the address where the
+ // vectors end and the elements begin.
+ const int cbHeader = 2 * m_type->m_rank * sizeof(DWORD);
+ HRESULT hr = S_OK;
+
+ // Ensure that the proper subset is in the cache. m_idxLower and m_idxUpper are initialized to -1, so the
+ // first time we hit this condition check, it will evaluate to true. We will set these inside the
+ // consequent to the range starting at nPosition and ending at the last available cache position. Thus,
+ // after the first time we hit this, we are asking if nPosition lies outside the range we've cached.
+ if (nPosition < m_idxLower || nPosition >= m_idxUpper)
+ {
+ const SIZE_T cbElemSize = m_info.arrayInfo.elementSize;
+ SIZE_T len = 1;
+
+ if (cbElemSize != 0)
+ {
+ // the element size could be bigger than the cache, but we want len to be at least 1.
+ len = max(ARRAY_CACHE_SIZE / cbElemSize, len);
+ }
+ else _ASSERTE(cbElemSize != 0);
+
+ m_idxLower = nPosition;
+ m_idxUpper = min(m_idxLower + len, m_info.arrayInfo.componentCount);
+ _ASSERTE(m_idxLower < m_idxUpper);
+
+ SIZE_T cbOffsetFrom = m_info.arrayInfo.offsetToArrayBase + m_idxLower * cbElemSize;
+
+ SIZE_T cbSize = (m_idxUpper - m_idxLower) * cbElemSize; // we'll copy the largest range of ellements possible
+
+ _ASSERTE(cbSize <= m_info.objSize);
+ // Copy the proper subrange of the array over
+ EX_TRY
+ {
+ m_valueHome.GetInternalValue(MemoryRange(m_pObjectCopy + cbHeader, cbSize), cbOffsetFrom); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+ IfFailRet(hr);
+ }
+
+ SIZE_T size = m_info.arrayInfo.elementSize;
+ _ASSERTE(size <= m_info.objSize);
+
+ SIZE_T offset = m_info.arrayInfo.offsetToArrayBase + (nPosition * size);
+ void * localAddress = m_pObjectCopy + cbHeader + ((nPosition - m_idxLower) * size);
+
+ EX_TRY
+ {
+ m_valueHome.CreateInternalValue(m_elemtype,
+ offset,
+ localAddress,
+ (ULONG32)size,
+ ppValue); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+
+} // CordbArrayValue::GetElementAtPosition
+
+HRESULT CordbArrayValue::IsValid(BOOL *pbValid)
+{
+ VALIDATE_POINTER_TO_OBJECT(pbValid, BOOL *);
+
+ // <TODO>@todo: implement tracking of objects across collections.</TODO>
+
+ return E_NOTIMPL;
+}
+
+HRESULT CordbArrayValue::CreateRelocBreakpoint(
+ ICorDebugValueBreakpoint **ppBreakpoint)
+{
+ VALIDATE_POINTER_TO_OBJECT(ppBreakpoint, ICorDebugValueBreakpoint **);
+
+ return E_NOTIMPL;
+}
+
+// Creates a handle of the given type for this heap value.
+// Not Implemented In-Proc.
+// Arguments:
+// input: handleType - type of the handle to be created
+// output: ppHandle - on success, the newly created handle
+// Return Value: S_OK on success or E_INVALIDARG, E_OUTOFMEMORY, or CORDB_E_HELPER_MAY_DEADLOCK
+HRESULT CordbArrayValue::CreateHandle(
+ CorDebugHandleType handleType,
+ ICorDebugHandleValue ** ppHandle)
+{
+ PUBLIC_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ return CordbValue::InternalCreateHandle(handleType, ppHandle);
+} // CordbArrayValue::CreateHandle
+
+// get a copy of the array
+// Arguments
+// output: pTo - pointer to a caller-allocated and managed buffer to hold the copy. The caller must guarantee
+// that this is large enough to hold the entire array
+// Return Value: S_OK on success, E_INVALIDARG or read process memory errors on failure
+HRESULT CordbArrayValue::GetValue(void *pTo)
+{
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(pTo, void *, 1, false, true);
+ FAIL_IF_NEUTERED(this);
+
+ HRESULT hr = S_OK;
+ EX_TRY
+ {
+ // Copy out the value, which is the whole array.
+ // There's no lazy-evaluation here, so this could be rather large
+ m_valueHome.GetValue(MemoryRange(pTo, m_size)); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+} // CordbArrayValue::GetValue
+
+HRESULT CordbArrayValue::SetValue(void *pFrom)
+{
+ // You're not allowed to set a whole array at once.
+ return E_INVALIDARG;
+}
+
+// initialize a new instance of CordbArrayValue
+// Arguments: none
+// Return Value: S_OK on success or E_OUTOFMEMORY or read process memory errors on failure
+// Note: we are only initializing information about the array (rank, sizes, dimensions, etc) here. We will not
+// attempt to read array contents until we receive a request to do so.
+HRESULT CordbArrayValue::Init()
+{
+ INTERNAL_SYNC_API_ENTRY(this->GetProcess()); //
+ HRESULT hr = S_OK;
+
+ SIZE_T cbVector = m_info.arrayInfo.rank * sizeof(DWORD);
+ _ASSERTE(cbVector <= m_info.objSize);
+
+ int cbHeader = 2 * (int)cbVector;
+
+ // Find largest data size that will fit in cache
+ SIZE_T cbData = m_info.arrayInfo.componentCount * m_info.arrayInfo.elementSize;
+ if (cbData > ARRAY_CACHE_SIZE)
+ {
+ cbData = (ARRAY_CACHE_SIZE / m_info.arrayInfo.elementSize)
+ * m_info.arrayInfo.elementSize;
+ }
+
+ if (cbData < m_info.arrayInfo.elementSize)
+ {
+ cbData = m_info.arrayInfo.elementSize;
+ }
+
+ // Allocate memory
+ m_pObjectCopy = new (nothrow) BYTE[cbHeader + cbData];
+ if (m_pObjectCopy == NULL)
+ return E_OUTOFMEMORY;
+
+
+ m_arrayLowerBase = NULL;
+ m_arrayUpperBase = NULL;
+
+ // Copy base vectors into header. (Offsets are 0 if the vectors aren't used)
+ if (m_info.arrayInfo.offsetToLowerBounds != 0)
+ {
+ m_arrayLowerBase = (DWORD*)(m_pObjectCopy);
+ EX_TRY
+ {
+ m_valueHome.GetInternalValue(MemoryRange(m_arrayLowerBase, cbVector),
+ m_info.arrayInfo.offsetToLowerBounds); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+ IfFailRet(hr);
+ }
+
+
+ if (m_info.arrayInfo.offsetToUpperBounds != 0)
+ {
+ m_arrayUpperBase = (DWORD*)(m_pObjectCopy + cbVector);
+ EX_TRY
+ {
+ m_valueHome.GetInternalValue(MemoryRange(m_arrayUpperBase, cbVector),
+ m_info.arrayInfo.offsetToUpperBounds); // throws
+ }
+ EX_CATCH_HRESULT(hr);
+ IfFailRet(hr);
+ }
+
+ // That's all for now. We'll do lazy-evaluation for the array contents.
+
+ return hr;
+} // CordbArrayValue::Init
+
+// CordbArrayValue::GetThreadOwningMonitorLock
+// If a managed thread owns the monitor lock on this object then *ppThread
+// will point to that thread and S_OK will be returned. The thread object is valid
+// until the thread exits. *pAcquisitionCount will indicate the number of times
+// this thread would need to release the lock before it returns to being
+// unowned.
+// If no managed thread owns the monitor lock on this object then *ppThread
+// and pAcquisitionCount will be unchanged and S_FALSE returned.
+// If ppThread or pAcquisitionCount is not a valid pointer the result is
+// undefined.
+// If any error occurs such that it cannot be determined which, if any, thread
+// owns the monitor lock on this object then a failing HRESULT will be returned
+HRESULT CordbArrayValue::GetThreadOwningMonitorLock(ICorDebugThread **ppThread, DWORD *pAcquisitionCount)
+{
+ PUBLIC_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ return CordbHeapValue3Impl::GetThreadOwningMonitorLock(GetProcess(),
+ GetValueHome()->GetAddress(), ppThread, pAcquisitionCount);
+}
+
+// CordbArrayValue::GetMonitorEventWaitList
+// Provides an ordered list of threads which are queued on the event associated
+// with a monitor lock. The first thread in the list is the first thread which
+// will be released by the next call to Monitor.Pulse, the next thread in the list
+// will be released on the following call, and so on.
+// If this list is non-empty S_OK will be returned, if it is empty S_FALSE
+// will be returned (the enumeration is still valid, just empty).
+// In either case the enumeration interface is only usable for the duration
+// of the current synchronized state, however the threads interfaces dispensed
+// from it are valid until the thread exits.
+// If ppThread is not a valid pointer the result is undefined.
+// If any error occurs such that it cannot be determined which, if any, threads
+// are waiting for the monitor then a failing HRESULT will be returned
+HRESULT CordbArrayValue::GetMonitorEventWaitList(ICorDebugThreadEnum **ppThreadEnum)
+{
+ PUBLIC_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+
+ return CordbHeapValue3Impl::GetMonitorEventWaitList(GetProcess(),
+ GetValueHome()->GetAddress(),
+ ppThreadEnum);
+}
+
+/* ------------------------------------------------------------------------- *
+ * Handle Value
+ * ------------------------------------------------------------------------- */
+// constructor
+// Arguments:
+// input:
+// pAppDomain - app domain to which the value belongs
+// pType - type information for the value
+// handleType - indicates whether we are constructing a strong or weak handle
+CordbHandleValue::CordbHandleValue(
+ CordbAppDomain * pAppdomain,
+ CordbType * pType, // The type of object that we create handle on
+ CorDebugHandleType handleType) // strong or weak handle
+ : CordbValue(pAppdomain, pType, NULL, false,
+ pAppdomain->GetSweepableExitNeuterList()
+ )
+{
+ m_vmHandle = VMPTR_OBJECTHANDLE::NullPtr();
+ m_fCanBeValid = TRUE;
+
+ m_handleType = handleType;
+ m_size = sizeof(void*);
+} // CordbHandleValue::CordbHandleValue
+
+//-----------------------------------------------------------------------------
+// Assign internal handle to the given value, and update pertinent counters
+//
+// Arguments:
+// handle - non-null CLR ObjectHandle that this CordbHandleValue will represent
+//
+// Notes:
+// Call code:CordbHandleValue::ClearHandle to clear the handle value.
+void CordbHandleValue::AssignHandle(VMPTR_OBJECTHANDLE handle)
+{
+ _ASSERTE(GetProcess()->ThreadHoldsProcessLock());
+ _ASSERTE(m_vmHandle.IsNull());
+
+ // Use code:CordbHandleValue::ClearHandle to clear the handle value.
+ _ASSERTE(!handle.IsNull());
+
+ m_vmHandle = handle;
+ GetProcess()->IncrementOutstandingHandles();
+}
+
+//-----------------------------------------------------------------------------
+// Clear the handle value
+//
+// Assumptions:
+// Caller only clears if not already cleared.
+//
+// Notes:
+// This is the inverse of code:CordbHandleValue::AssignHandle
+void CordbHandleValue::ClearHandle()
+{
+ _ASSERTE(GetProcess()->ThreadHoldsProcessLock());
+ _ASSERTE(!m_vmHandle.IsNull());
+
+ m_vmHandle = VMPTR_OBJECTHANDLE::NullPtr();
+ GetProcess()->DecrementOutstandingHandles();
+}
+
+// initialize a new instance of CordbHandleValue
+// Arguments:
+// input: pHandle - non-null CLR ObjectHandle that this CordbHandleValue will represent
+// Return Value: S_OK on success or CORDBG_E_TARGET_INCONSISTENT, E_INVALIDARG, read process memory errors.
+HRESULT CordbHandleValue::Init(VMPTR_OBJECTHANDLE pHandle)
+{
+ INTERNAL_SYNC_API_ENTRY(GetProcess());
+ HRESULT hr = S_OK;
+
+ {
+ RSLockHolder lockHolder(GetProcess()->GetProcessLock());
+ // If it is a strong handle, m_pHandle will not be NULL unless Dispose method is called.
+ // If it is a weak handle, m_pHandle can be NULL when Dispose is called.
+ AssignHandle(pHandle);
+ }
+
+ // This will init m_info.
+ IfFailRet(RefreshHandleValue());
+
+ // objRefBad is currently overloaded to mean that 1) the object ref is invalid, or 2) the object ref is NULL.
+ // NULL is clearly not a bad object reference, but in either case we have no more type data to work with,
+ // so don't attempt to assign more specific type information to the reference.
+ if (!m_info.objRefBad)
+ {
+ // We need to get the type info from the left side.
+ CordbType *newtype;
+
+ IfFailRet(CordbType::TypeDataToType(m_appdomain, &m_info.objTypeData, &newtype));
+
+ m_type.Assign(newtype);
+ }
+
+ return hr;
+} // CordbHandleValue::Init
+
+// destructor
+CordbHandleValue::~CordbHandleValue()
+{
+ DTOR_ENTRY(this);
+
+ _ASSERTE(IsNeutered());
+} // CordbHandleValue::~CordbHandleValue
+
+// Free left-side resources, mainly the GC handle keeping the object alive.
+void CordbHandleValue::NeuterLeftSideResources()
+{
+ Dispose();
+
+ RSLockHolder lockHolder(GetProcess()->GetProcessLock());
+ Neuter();
+} // CordbHandleValue::NeuterLeftSideResources
+
+// Neuter
+// Notes:
+// CordbHandleValue may hold Left-Side resources via the GC handle.
+// By the time we neuter it, those resources must have been freed,
+// either explicitly by calling code:CordbHandleValue::Dispose, or
+// implicitly by the left-side process exiting.
+void CordbHandleValue::Neuter()
+{
+ // CordbHandleValue is on the AppDomainExit neuter list.
+
+ // We should have cleaned up our Left-side resource by now (m_vmHandle
+ // should be null). If AppDomain / Process has already exited, then the LS
+ // already cleaned them up for us, and so we don't worry about them.
+ bool fAppDomainIsAlive = (m_appdomain != NULL && !m_appdomain->IsNeutered());
+ if (fAppDomainIsAlive)
+ {
+ BOOL fTargetIsDead = !GetProcess()->IsSafeToSendEvents() || GetProcess()->m_exiting;
+ if (!fTargetIsDead)
+ {
+ _ASSERTE(m_vmHandle.IsNull());
+ }
+ }
+
+ CordbValue::Neuter();
+} // CordbHandleValue::Neuter
+
+// Helper: Refresh the handle value object.
+// Gets information about the object to which the handle points.
+// Arguments: none
+// Return Value: S_OK on success, CORDBG_E_HANDLE_HAS_BEEN_DISPOSED, CORDBG_E_BAD_REFERENCE_VALUE,
+// errors from read process memory.
+HRESULT CordbHandleValue::RefreshHandleValue()
+{
+ INTERNAL_SYNC_API_ENTRY(this->GetProcess()); //
+ _ASSERTE(m_appdomain != NULL);
+ _ASSERTE(!m_appdomain->IsNeutered());
+
+ // If Dispose has been called, don't bother to refresh handle value.
+ if (m_vmHandle.IsNull())
+ {
+ return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED;
+ }
+
+ // If weak handle and the object was dead, no point to refresh the handle value
+ if (m_fCanBeValid == FALSE)
+ {
+ return CORDBG_E_BAD_REFERENCE_VALUE;
+ }
+
+ HRESULT hr = S_OK;
+ CorElementType type = m_type->m_elementType;
+
+ _ASSERTE((m_pProcess != NULL));
+
+ _ASSERTE (type != ELEMENT_TYPE_GENERICINST);
+ _ASSERTE (type != ELEMENT_TYPE_VAR);
+ _ASSERTE (type != ELEMENT_TYPE_MVAR);
+
+ CordbProcess * pProcess = GetProcess();
+ void * objectAddress = NULL;
+ CORDB_ADDRESS objectHandle = 0;
+
+ EX_TRY
+ {
+ objectHandle = pProcess->GetDAC()->GetHandleAddressFromVmHandle(m_vmHandle);
+ if (type != ELEMENT_TYPE_TYPEDBYREF)
+ {
+ pProcess->SafeReadBuffer(TargetBuffer(objectHandle, sizeof(void *)), (BYTE *)&objectAddress);
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+ IfFailRet(hr);
+ EX_TRY
+ {
+ if (type == ELEMENT_TYPE_TYPEDBYREF)
+ {
+ CordbReferenceValue::GetTypedByRefData(pProcess,
+ objectHandle,
+ type,
+ m_appdomain->GetADToken(),
+ &m_info);
+ }
+ else
+ {
+ CordbReferenceValue::GetObjectData(pProcess,
+ objectAddress,
+ type,
+ m_appdomain->GetADToken(),
+ &m_info);
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+ IfFailRet(hr);
+
+ // If reference is already gone bad or reference is NULL,
+ // don't bother to refetch in the future.
+ //
+ if ((m_info.objRefBad) || (m_info.objRef == NULL))
+ {
+ m_fCanBeValid = FALSE;
+ }
+
+ return hr;
+}
+ // CordbHandleValue::RefreshHandleValue
+
+HRESULT CordbHandleValue::QueryInterface(REFIID id, void **pInterface)
+{
+ VALIDATE_POINTER_TO_OBJECT(pInterface, void **);
+
+ if (id == IID_ICorDebugValue)
+ {
+ *pInterface = static_cast<ICorDebugValue*>(this);
+ }
+ else if (id == IID_ICorDebugValue2)
+ {
+ *pInterface = static_cast<ICorDebugValue2*>(this);
+ }
+ else if (id == IID_ICorDebugValue3)
+ {
+ *pInterface = static_cast<ICorDebugValue3*>(this);
+ }
+ else if (id == IID_ICorDebugReferenceValue)
+ {
+ *pInterface = static_cast<ICorDebugReferenceValue*>(this);
+ }
+ else if (id == IID_ICorDebugHandleValue)
+ {
+ *pInterface = static_cast<ICorDebugHandleValue*>(this);
+ }
+ else if (id == IID_IUnknown)
+ {
+ *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugHandleValue*>(this));
+ }
+ else
+ {
+ *pInterface = NULL;
+ return E_NOINTERFACE;
+ }
+
+ ExternalAddRef();
+ return S_OK;
+} // CordbHandleValue::QueryInterface
+
+
+// return handle type. Currently we have strong and weak.
+// Arguments:
+// output: pType - the handle type unless pType is null
+// Return Value: S_OK on success or E_INVALIDARG or CORDBG_E_HANDLE_HAS_BEEN_DISPOSED on failure
+HRESULT CordbHandleValue::GetHandleType(CorDebugHandleType *pType)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(pType, CorDebugHandleType *);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ _ASSERTE(m_appdomain != NULL);
+ _ASSERTE(!m_appdomain->IsNeutered());
+
+ if (m_vmHandle.IsNull())
+ {
+ // handle has been disposed!
+ return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED;
+ }
+ *pType = m_handleType;
+ return S_OK;
+} // CordbHandleValue::GetHandleType
+
+// Dispose will cause handle to be recycled.
+// Arguments: none
+// Return Value: S_OK on success, CORDBG_E_HANDLE_HAS_BEEN_DISPOSED or errors from the
+// DB_IPCE_DISPOSE_HANDLE event
+
+// @dbgtodo Microsoft inspection: remove the dispose handle hresults when the IPC events are eliminated
+HRESULT CordbHandleValue::Dispose()
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ _ASSERTE(m_appdomain != NULL);
+ _ASSERTE(!m_appdomain->IsNeutered());
+
+ HRESULT hr = S_OK;
+ DebuggerIPCEvent event;
+ CordbProcess *process;
+
+ process = GetProcess();
+
+ // Process should still be alive because it would have neutered us if it became invalid.
+ _ASSERTE(process != NULL);
+
+ VMPTR_OBJECTHANDLE vmObjHandle = VMPTR_OBJECTHANDLE::NullPtr();
+ {
+ RSLockHolder lockHolder(GetProcess()->GetProcessLock());
+ if (m_vmHandle.IsNull())
+ {
+ // handle has been disposed!
+ return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED;
+ }
+
+ vmObjHandle = m_vmHandle;
+ ClearHandle(); // set m_pHandle to null.
+
+ if (process->m_exiting)
+ {
+ // process is exiting. Don't do anything
+ return S_OK;
+ }
+ }
+
+ // recycle the handle to EE
+ process->InitIPCEvent(&event,
+ DB_IPCE_DISPOSE_HANDLE,
+ false,
+ m_appdomain->GetADToken());
+
+ event.DisposeHandle.vmObjectHandle = vmObjHandle;
+ if (m_handleType == HANDLE_STRONG)
+ {
+ event.DisposeHandle.fStrong = TRUE;
+ }
+ else
+ {
+ event.DisposeHandle.fStrong = FALSE;
+ }
+
+ // Note: one-way event here...
+ hr = process->SendIPCEvent(&event, sizeof(DebuggerIPCEvent));
+
+ hr = WORST_HR(hr, event.hr);
+
+ return hr;
+} // CordbHandleValue::Dispose
+
+// get the type of the object to which the handle points
+// Arguments:
+// output: pType - the object type on success
+// Return Value: S_OK on success, CORDBG_E_HANDLE_HAS_BEEN_DISPOSED, CORDBG_E_CLASS_NOT_LOADED or synchronization errors on
+// failure
+HRESULT CordbHandleValue::GetType(CorElementType *pType)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(pType, CorElementType *);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ _ASSERTE(m_appdomain != NULL);
+ _ASSERTE(!m_appdomain->IsNeutered());
+
+ HRESULT hr = S_OK;
+
+ if (m_vmHandle.IsNull())
+ {
+ return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED;
+ }
+
+ bool isBoxedVCObject = false;
+ if ((m_type->m_pClass != NULL) && (m_type->m_elementType != ELEMENT_TYPE_STRING))
+ {
+ EX_TRY
+ {
+ isBoxedVCObject = m_type->m_pClass->IsValueClass();
+ }
+ EX_CATCH_HRESULT(hr);
+ if (FAILED(hr))
+ return hr;
+ }
+
+ if (isBoxedVCObject)
+ {
+ // if we create the handle to a boxed value type, then the type is
+ // E_T_CLASS. m_type is the underlying value type. That is incorrect to
+ // return.
+ //
+ *pType = ELEMENT_TYPE_CLASS;
+ return S_OK;
+ }
+
+ return m_type->GetType(pType);
+} // CordbHandleValue::GetType
+
+// get the size of the handle-- this will always return the size of the handle itself (just pointer size), so
+// it's not particularly interesting.
+// Arguments:
+// output: pSize - the size of the handle (on success). This must be non-null. Memory management belongs
+// to the caller.
+// Return Value: S_OK on success, E_INVALIDARG (if pSize is null), or CORDBG_E_HANDLE_HAS_BEEN_DISPOSED on failure
+HRESULT CordbHandleValue::GetSize(ULONG32 *pSize)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(pSize, ULONG32 *);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ _ASSERTE(m_appdomain != NULL);
+ _ASSERTE(!m_appdomain->IsNeutered());
+
+ if (m_vmHandle.IsNull())
+ {
+ return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED;
+ }
+
+ if (m_size > ULONG_MAX)
+ {
+ *pSize = ULONG_MAX;
+ return (COR_E_OVERFLOW);
+ }
+
+ //return the size of reference
+ *pSize = (ULONG)m_size;
+ return S_OK;
+} // CordbHandleValue::GetSize
+
+// get the size of the handle-- this will always return the size of the handle itself (just pointer size), so
+// it's not particularly interesting.
+// Arguments:
+// output: pSize - the size of the handle (on success). This must be non-null. Memory management belongs
+// to the caller.
+// Return Value: S_OK on success, E_INVALIDARG (if pSize is null), or CORDBG_E_HANDLE_HAS_BEEN_DISPOSED on failure
+HRESULT CordbHandleValue::GetSize64(ULONG64 *pSize)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(pSize, ULONG64 *);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ _ASSERTE(m_appdomain != NULL);
+ _ASSERTE(!m_appdomain->IsNeutered());
+
+ if (m_vmHandle.IsNull())
+ {
+ return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED;
+ }
+
+ //return the size of reference
+ *pSize = m_size;
+ return S_OK;
+} // CordbHandleValue::GetSize
+
+// Get the target address of the handle
+// Arguments:
+// output: pAddress - handle address on success. This must be non-null and memory is managed by the caller
+// Return Value: S_OK on success or CORDBG_E_HANDLE_HAS_BEEN_DISPOSED or E_INVALIDARG on failure
+HRESULT CordbHandleValue::GetAddress(CORDB_ADDRESS *pAddress)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(pAddress, CORDB_ADDRESS *);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ _ASSERTE(m_appdomain != NULL);
+ _ASSERTE(!m_appdomain->IsNeutered());
+
+ if (m_vmHandle.IsNull())
+ {
+ return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED;
+ }
+
+ HRESULT hr = S_OK;
+ EX_TRY
+ {
+ *pAddress = GetProcess()->GetDAC()->GetHandleAddressFromVmHandle(m_vmHandle);
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+} // CordbHandleValue::GetAddress
+
+HRESULT CordbHandleValue::CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint)
+{
+ return E_NOTIMPL;
+} // CreateBreakpoint
+
+// indicates whether a handle is null
+// Arguments:
+// output: pbNull - true iff the handle is null and pbNull is non-null.Memory is managed by the caller
+// Return Value: S_OK on success or CORDBG_E_HANDLE_HAS_BEEN_DISPOSED or E_INVALIDARG, CORDBG_E_BAD_REFERENCE_VALUE,
+// errors from read process memory.
+HRESULT CordbHandleValue::IsNull(BOOL *pbNull)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(pbNull, BOOL *);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ _ASSERTE(m_appdomain != NULL);
+ _ASSERTE(!m_appdomain->IsNeutered());
+
+ HRESULT hr = S_OK;
+
+ *pbNull = FALSE;
+
+ if (m_vmHandle.IsNull())
+ {
+ return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED;
+ }
+
+
+ // Only return true if handle is long weak handle and is disposed.
+ if (m_handleType == HANDLE_WEAK_TRACK_RESURRECTION)
+ {
+ hr = RefreshHandleValue();
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ if (m_info.objRef == NULL)
+ {
+ *pbNull = TRUE;
+ }
+ }
+ else if (m_info.objRef == NULL)
+ {
+ *pbNull = TRUE;
+ }
+
+ // strong handle always return false for IsNull
+
+ return S_OK;
+} // CordbHandleValue::IsNull
+
+// gets a copy of the value of the handle
+// Arguments:
+// output: pValue - handle { on success. This must be non-null and memory is managed by the caller
+// Return Value: S_OK on success or CORDBG_E_HANDLE_HAS_BEEN_DISPOSED or E_INVALIDARG, CORDBG_E_BAD_REFERENCE_VALUE,
+// errors from read process memory.
+HRESULT CordbHandleValue::GetValue(CORDB_ADDRESS *pValue)
+{
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(pValue, CORDB_ADDRESS *);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ _ASSERTE(m_appdomain != NULL);
+ _ASSERTE(!m_appdomain->IsNeutered());
+
+ if (m_vmHandle.IsNull())
+ {
+ return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED;
+ }
+
+ RefreshHandleValue();
+ *pValue = PTR_TO_CORDB_ADDRESS(m_info.objRef);
+ return S_OK;
+} // CordbHandleValue::GetValue
+
+HRESULT CordbHandleValue::SetValue(CORDB_ADDRESS value)
+{
+ // do not support SetValue on Handle
+ return E_FAIL;
+} // CordbHandleValue::GetValue
+
+// get an ICDValue to represent the object to which the handle refers
+// Arguments:
+// output: ppValue - pointer to the ICDValue for the handle referent as long as ppValue is non-null
+// Return Value: S_OK on success or CORDBG_E_HANDLE_HAS_BEEN_DISPOSED or E_INVALIDARG, CORDBG_E_BAD_REFERENCE_VALUE,
+// errors from read process memory.
+HRESULT CordbHandleValue::Dereference(ICorDebugValue **ppValue)
+{
+ HRESULT hr = S_OK;
+ PUBLIC_REENTRANT_API_ENTRY(this);
+ VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **);
+ FAIL_IF_NEUTERED(this);
+ ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
+ _ASSERTE(m_appdomain != NULL);
+ _ASSERTE(!m_appdomain->IsNeutered());
+
+ *ppValue = NULL;
+
+ if (m_vmHandle.IsNull())
+ {
+ return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED;
+ }
+
+ hr = RefreshHandleValue();
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ if ((m_info.objRefBad) || (m_info.objRef == NULL))
+ {
+ return CORDBG_E_BAD_REFERENCE_VALUE;
+ }
+
+ EX_TRY
+ {
+ hr = CordbReferenceValue::DereferenceCommon(m_appdomain,
+ m_type,
+ NULL, // don't support typed-by-refs
+ &m_info,
+ ppValue);
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+} // CordbHandleValue::Dereference
+
+HRESULT CordbHandleValue::DereferenceStrong(ICorDebugValue **ppValue)
+{
+ return E_NOTIMPL;
+}
+
+// CordbHeapValue3Impl::GetThreadOwningMonitorLock
+// If a managed thread owns the monitor lock on this object then *ppThread
+// will point to that thread and S_OK will be returned. The thread object is valid
+// until the thread exits. *pAcquisitionCount will indicate the number of times
+// this thread would need to release the lock before it returns to being
+// unowned.
+// If no managed thread owns the monitor lock on this object then *ppThread
+// and pAcquisitionCount will be unchanged and S_FALSE returned.
+// If ppThread or pAcquisitionCount is not a valid pointer the result is
+// undefined.
+// If any error occurs such that it cannot be determined which, if any, thread
+// owns the monitor lock on this object then a failing HRESULT will be returned
+HRESULT CordbHeapValue3Impl::GetThreadOwningMonitorLock(CordbProcess* pProcess,
+ CORDB_ADDRESS remoteObjAddress,
+ ICorDebugThread **ppThread,
+ DWORD *pAcquisitionCount)
+{
+ HRESULT hr = S_OK;
+ EX_TRY
+ {
+ IDacDbiInterface *pDac = pProcess->GetDAC();
+ VMPTR_Object vmObj = pDac->GetObject(remoteObjAddress);
+ MonitorLockInfo info = pDac->GetThreadOwningMonitorLock(vmObj);
+ if(info.acquisitionCount == 0)
+ {
+ // unowned
+ *ppThread = NULL;
+ *pAcquisitionCount = 0;
+ hr = S_FALSE;
+ }
+ else
+ {
+ RSLockHolder lockHolder(pProcess->GetProcessLock());
+ CordbThread* pThread = pProcess->LookupOrCreateThread(info.lockOwner);
+ pThread->QueryInterface(__uuidof(ICorDebugThread), (VOID**) ppThread);
+ *pAcquisitionCount = info.acquisitionCount;
+ hr = S_OK;
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+}
+
+// A small helper for CordbHeapValue3Impl::GetMonitorEventWaitList that adds each enumerated thread to an array
+// Arguments:
+// vmThread - The thread to add
+// puserData - the array to add it to
+VOID ThreadEnumerationCallback(VMPTR_Thread vmThread, VOID* pUserData)
+{
+ CQuickArrayList<VMPTR_Thread>* pThreadList = (CQuickArrayList<VMPTR_Thread>*) pUserData;
+ pThreadList->Push(vmThread);
+}
+
+// CordbHeapValue3Impl::GetMonitorEventWaitList
+// Provides an ordered list of threads which are queued on the event associated
+// with a monitor lock. The first thread in the list is the first thread which
+// will be released by the next call to Monitor.Pulse, the next thread in the list
+// will be released on the following call, and so on.
+// If this list is non-empty S_OK will be returned, if it is empty S_FALSE
+// will be returned (the enumeration is still valid, just empty).
+// In either case the enumeration interface is only usable for the duration
+// of the current synchronized state, however the threads interfaces dispensed
+// from it are valid until the thread exits.
+// If ppThread is not a valid pointer the result is undefined.
+// If any error occurs such that it cannot be determined which, if any, threads
+// are waiting for the monitor then a failing HRESULT will be returned
+HRESULT CordbHeapValue3Impl::GetMonitorEventWaitList(CordbProcess* pProcess,
+ CORDB_ADDRESS remoteObjAddress,
+ ICorDebugThreadEnum **ppThreadEnum)
+{
+ HRESULT hr = S_OK;
+ RSSmartPtr<CordbThread> *rsThreads = NULL;
+ EX_TRY
+ {
+ IDacDbiInterface *pDac = pProcess->GetDAC();
+ VMPTR_Object vmObj = pDac->GetObject(remoteObjAddress);
+ CQuickArrayList<VMPTR_Thread> threads;
+ pDac->EnumerateMonitorEventWaitList(vmObj,
+ (IDacDbiInterface::FP_THREAD_ENUMERATION_CALLBACK)ThreadEnumerationCallback, (VOID*)&threads);
+
+ rsThreads = new RSSmartPtr<CordbThread>[threads.Size()];
+ {
+ RSLockHolder lockHolder(pProcess->GetProcessLock());
+ for(DWORD i = 0; i < threads.Size(); i++)
+ {
+ rsThreads[i].Assign(pProcess->LookupOrCreateThread(threads[i]));
+ }
+ }
+
+ CordbThreadEnumerator* threadEnum =
+ new CordbThreadEnumerator(pProcess, rsThreads, (DWORD)threads.Size());
+ pProcess->GetContinueNeuterList()->Add(pProcess, threadEnum);
+ threadEnum->QueryInterface(__uuidof(ICorDebugThreadEnum), (VOID**)ppThreadEnum);
+ if(threads.Size() == 0)
+ {
+ hr = S_FALSE;
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+ delete [] rsThreads;
+ return hr;
+}