1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | //***************************************************************************** |
5 | // File: DIValue.cpp |
6 | // |
7 | |
8 | // |
9 | //***************************************************************************** |
10 | #include "stdafx.h" |
11 | #include "primitives.h" |
12 | |
13 | // copy from a MemoryRange to dest |
14 | // Arguments: |
15 | // input: source - MemoryRange describing the start address and size of the source buffer |
16 | // output: dest - address of the buffer to which the source buffer is copied |
17 | // Note: the buffer for dest must be allocated by the caller and must be large enough to hold the |
18 | // bytes from the source buffer. |
19 | void localCopy(void * dest, MemoryRange source) |
20 | { |
21 | _ASSERTE(dest != NULL); |
22 | _ASSERTE(source.StartAddress() != NULL); |
23 | |
24 | memcpy(dest, source.StartAddress(), source.Size()); |
25 | } |
26 | |
27 | // for an inheritance graph of the ICDValue types, // See file:./ICorDebugValueTypes.vsd for a diagram of the types. |
28 | |
29 | /* ------------------------------------------------------------------------- * |
30 | * CordbValue class |
31 | * ------------------------------------------------------------------------- */ |
32 | |
33 | CordbValue::CordbValue(CordbAppDomain * appdomain, |
34 | CordbType * type, |
35 | CORDB_ADDRESS id, |
36 | bool isLiteral, |
37 | NeuterList * pList) |
38 | : CordbBase( |
39 | ((appdomain != NULL) ? (appdomain->GetProcess()) : (type->GetProcess())), |
40 | (UINT_PTR)id, enumCordbValue), |
41 | m_appdomain(appdomain), |
42 | m_type(type), // implicit InternalAddRef |
43 | //m_sigCopied(false), |
44 | m_size(0), |
45 | m_isLiteral(isLiteral) |
46 | { |
47 | HRESULT hr = S_OK; |
48 | |
49 | _ASSERTE(GetProcess() != NULL); |
50 | |
51 | // Add to a neuter list. If none is provided, use the ExitProcess list as a default. |
52 | // The main neuter lists of interest here are: |
53 | // - CordbProcess::GetContinueNeuterList() - Shortest. Neuter when the process continues. |
54 | // - CordbAppDomain::GetExitNeuterList() - Middle. Neuter when the AD exits. Since most Values (except globals) are in |
55 | // a specific AD, this almost catches all; and keeps us safe in AD-unload scenarios. |
56 | // - CordbProcess::GetExitNeuterList() - Worst. Doesn't neuter until the process exits (or we detach). |
57 | // This could be a long time. |
58 | if (pList == NULL) |
59 | { |
60 | pList = GetProcess()->GetExitNeuterList(); |
61 | } |
62 | |
63 | |
64 | EX_TRY |
65 | { |
66 | pList->Add(GetProcess(), this); |
67 | } |
68 | EX_CATCH_HRESULT(hr); |
69 | SetUnrecoverableIfFailed(GetProcess(), hr); |
70 | } // CordbValue::CordbValue |
71 | |
72 | CordbValue::~CordbValue() |
73 | { |
74 | DTOR_ENTRY(this); |
75 | |
76 | _ASSERTE(this->IsNeutered()); |
77 | |
78 | _ASSERTE(m_type == NULL); |
79 | } // CordbValue::~CordbValue |
80 | |
81 | void CordbValue::Neuter() |
82 | { |
83 | m_appdomain = NULL; |
84 | m_type.Clear(); |
85 | |
86 | ValueHome * pValueHome = GetValueHome(); |
87 | if (pValueHome != NULL) |
88 | { |
89 | pValueHome->Clear(); |
90 | } |
91 | CordbBase::Neuter(); |
92 | } // CordbValue::Neuter |
93 | |
94 | // Helper for code:CordbValue::CreateValueByType. Create a new instance of CordbGenericValue |
95 | // Arguments: |
96 | // input: pAppdomain - appdomain to which the value belongs |
97 | // pType - type of the value |
98 | // remoteValue - remote address and size of the value |
99 | // localValue - local address and size of the value |
100 | // ppRemoteRegAddr - register address of the value |
101 | // output: ppValue - the newly created instance of an ICDValue |
102 | // Notes: |
103 | // - only one of the three locations will be non-NULL |
104 | // - Throws |
105 | /* static */ |
106 | void CordbValue::CreateGenericValue(CordbAppDomain * pAppdomain, |
107 | CordbType * pType, |
108 | TargetBuffer remoteValue, |
109 | MemoryRange localValue, |
110 | EnregisteredValueHomeHolder * ppRemoteRegAddr, |
111 | ICorDebugValue** ppValue) |
112 | { |
113 | LOG((LF_CORDB,LL_INFO100000,"CV::CreateValueByType CreateGenericValue\n" )); |
114 | RSSmartPtr<CordbGenericValue> pGenValue; |
115 | // A generic value |
116 | // By using a RSSmartPtr we ensure that in both success and failure cases, |
117 | // this object is cleaned up properly (deleted or not depending on ref counts). |
118 | // Specifically, the object has probably been placed on a neuter list so we |
119 | // can't delete it (but this is a detail we shouldn't rely on) |
120 | pGenValue.Assign(new CordbGenericValue(pAppdomain, |
121 | pType, |
122 | remoteValue, |
123 | ppRemoteRegAddr)); |
124 | |
125 | pGenValue->Init(localValue); // throws |
126 | |
127 | pGenValue->AddRef(); |
128 | *ppValue = (ICorDebugValue *)(ICorDebugGenericValue *)pGenValue; |
129 | } // CordbValue::CreateGenericValue |
130 | |
131 | // create a new instance of CordbVCObjectValue or CordbReferenceValue |
132 | // Arguments: |
133 | // input: pAppdomain - appdomain to which the value belongs |
134 | // pType - type of the value |
135 | // boxed - indicates whether the value is boxed |
136 | // remoteValue - remote address and size of the value |
137 | // localValue - local address and size of the value |
138 | // ppRemoteRegAddr - register address of the value |
139 | // output: ppValue - the newly created instance of an ICDValue |
140 | // Notes: |
141 | // - only one of the three locations will be non-NULL |
142 | // - Throws error codes from reading process memory |
143 | /* static */ |
144 | void CordbValue::CreateVCObjOrRefValue(CordbAppDomain * pAppdomain, |
145 | CordbType * pType, |
146 | bool boxed, |
147 | TargetBuffer remoteValue, |
148 | MemoryRange localValue, |
149 | EnregisteredValueHomeHolder * ppRemoteRegAddr, |
150 | ICorDebugValue** ppValue) |
151 | |
152 | { |
153 | HRESULT hr = S_OK; |
154 | LOG((LF_CORDB,LL_INFO1000000,"CV::CreateValueByType Creating ReferenceValue\n" )); |
155 | |
156 | // We either have a boxed or unboxed value type, or we have a value that's not a value type. |
157 | // For an unboxed value type, we'll create an instance of CordbVCObjectValue. Otherwise, we'll |
158 | // create an instance of CordbReferenceValue. |
159 | |
160 | // do we have a value type? |
161 | bool isVCObject = pType->IsValueType(); // throws |
162 | |
163 | if (!boxed && isVCObject) |
164 | { |
165 | RSSmartPtr<CordbVCObjectValue> pVCValue(new CordbVCObjectValue(pAppdomain, |
166 | pType, |
167 | remoteValue, |
168 | ppRemoteRegAddr)); |
169 | |
170 | IfFailThrow(pVCValue->Init(localValue)); |
171 | |
172 | pVCValue->AddRef(); |
173 | *ppValue = (ICorDebugValue*)(ICorDebugObjectValue*)pVCValue; |
174 | } |
175 | else |
176 | { |
177 | // either the value is boxed or it's not a value type |
178 | RSSmartPtr<CordbReferenceValue> pRef; |
179 | hr = CordbReferenceValue::Build(pAppdomain, |
180 | pType, |
181 | remoteValue, |
182 | localValue, |
183 | VMPTR_OBJECTHANDLE::NullPtr(), |
184 | ppRemoteRegAddr, // Home |
185 | &pRef); |
186 | IfFailThrow(hr); |
187 | hr = pRef->QueryInterface(__uuidof(ICorDebugValue), (void**)ppValue); |
188 | _ASSERTE(SUCCEEDED(hr)); |
189 | } |
190 | } // CordbValue::CreateVCObjOrRefValue |
191 | |
192 | // |
193 | // Create the proper ICDValue instance based on the given element type. |
194 | // Arguments: |
195 | // input: pAppdomain - appdomain to which the value belongs |
196 | // pType - type of the value |
197 | // boxed - indicates whether the value is boxed |
198 | // remoteValue - remote address and size of the value |
199 | // localValue - local address and size of the value |
200 | // ppRemoteRegAddr - register address of the value |
201 | // output: ppValue - the newly created instance of an ICDValue |
202 | // Notes: |
203 | // - Only one of the three locations, remoteValue, localValue or ppRemoteRegAddr, will be non-NULL. |
204 | // - Throws. |
205 | /*static*/ void CordbValue::CreateValueByType(CordbAppDomain * pAppdomain, |
206 | CordbType * pType, |
207 | bool boxed, |
208 | TargetBuffer remoteValue, |
209 | MemoryRange localValue, |
210 | EnregisteredValueHomeHolder * ppRemoteRegAddr, |
211 | ICorDebugValue** ppValue) |
212 | { |
213 | INTERNAL_SYNC_API_ENTRY(pAppdomain->GetProcess()); // |
214 | |
215 | // We'd really hope that our callers give us a valid appdomain, but in case |
216 | // they don't, we'll fail gracefully. |
217 | if ((pAppdomain != NULL) && pAppdomain->IsNeutered()) |
218 | { |
219 | STRESS_LOG1(LF_CORDB, LL_EVERYTHING, "CVBT using neutered AP, %p\n" , pAppdomain); |
220 | ThrowHR(E_INVALIDARG); |
221 | } |
222 | |
223 | LOG((LF_CORDB,LL_INFO100000,"CV::CreateValueByType\n" )); |
224 | |
225 | *ppValue = NULL; |
226 | |
227 | switch(pType->m_elementType) |
228 | { |
229 | case ELEMENT_TYPE_BOOLEAN: |
230 | case ELEMENT_TYPE_CHAR: |
231 | case ELEMENT_TYPE_I1: |
232 | case ELEMENT_TYPE_U1: |
233 | case ELEMENT_TYPE_I2: |
234 | case ELEMENT_TYPE_U2: |
235 | case ELEMENT_TYPE_I4: |
236 | case ELEMENT_TYPE_U4: |
237 | case ELEMENT_TYPE_R4: |
238 | case ELEMENT_TYPE_I8: |
239 | case ELEMENT_TYPE_U8: |
240 | case ELEMENT_TYPE_R8: |
241 | case ELEMENT_TYPE_I: |
242 | case ELEMENT_TYPE_U: |
243 | { |
244 | CreateGenericValue(pAppdomain, pType, remoteValue, localValue, ppRemoteRegAddr, ppValue); // throws |
245 | break; |
246 | } |
247 | |
248 | case ELEMENT_TYPE_CLASS: |
249 | case ELEMENT_TYPE_OBJECT: |
250 | case ELEMENT_TYPE_STRING: |
251 | case ELEMENT_TYPE_PTR: |
252 | case ELEMENT_TYPE_BYREF: |
253 | case ELEMENT_TYPE_TYPEDBYREF: |
254 | case ELEMENT_TYPE_ARRAY: |
255 | case ELEMENT_TYPE_SZARRAY: |
256 | case ELEMENT_TYPE_FNPTR: |
257 | { |
258 | CreateVCObjOrRefValue(pAppdomain, pType, boxed, remoteValue, localValue, ppRemoteRegAddr, ppValue); // throws |
259 | break; |
260 | } |
261 | |
262 | default: |
263 | _ASSERTE(!"Bad value type!" ); |
264 | ThrowHR(E_FAIL); |
265 | } |
266 | } // CordbValue::CreateValueByType |
267 | |
268 | // Create the proper ICDValue instance based on the given remote heap object |
269 | // Arguments: |
270 | // pAppDomain - the app domain the remote object is in |
271 | // vmObj - the remote object to get an ICDValue for |
272 | ICorDebugValue* CordbValue::CreateHeapValue(CordbAppDomain* pAppDomain, VMPTR_Object vmObj) |
273 | { |
274 | IDacDbiInterface* pDac = pAppDomain->GetProcess()->GetDAC(); |
275 | |
276 | TargetBuffer objBuffer = pDac->GetObjectContents(vmObj); |
277 | VOID* pRemoteAddr = CORDB_ADDRESS_TO_PTR(objBuffer.pAddress); |
278 | // This creates a local reference that has a remote address in it. Ie &pRemoteAddr is an address |
279 | // in the host address space and pRemoteAddr is an address in the target. |
280 | MemoryRange localReferenceDescription(&pRemoteAddr, sizeof(pRemoteAddr)); |
281 | RSSmartPtr<CordbReferenceValue> pRefValue; |
282 | IfFailThrow(CordbReferenceValue::Build(pAppDomain, |
283 | NULL, |
284 | EMPTY_BUFFER, |
285 | localReferenceDescription, |
286 | VMPTR_OBJECTHANDLE::NullPtr(), |
287 | NULL, |
288 | &pRefValue)); |
289 | |
290 | // Dereference our temporary reference value to construct the heap value we want |
291 | ICorDebugValue* pExtValue; |
292 | IfFailThrow(pRefValue->Dereference(&pExtValue)); |
293 | return pExtValue; |
294 | } |
295 | |
296 | // Gets the size om bytes of a value from its type. If the value is complex, we assume it is represented as |
297 | // a reference, since this is called for values that have been found on the stack, as an element of an |
298 | // array (represented as CordbArrayValue) or field of an object (CordbObjectValue) or the result of a |
299 | // func eval. For unboxed value types, we get the size of the entire value (it is not represented as a |
300 | // reference). |
301 | // Examples: |
302 | // - int on the stack |
303 | // => sizeof(int) |
304 | // - int as a field in an object on the heap |
305 | // =>sizeof(int) |
306 | // - Boxed int on the heap |
307 | // => size of a pointer |
308 | // - Class Point { int x; int y}; // class will have a method table / object header which may increase size. |
309 | // => size of a pointer |
310 | // - Struct Point {int x; int y; }; // unboxed struct may not necessarily have the object header. |
311 | // => 2 * sizeof(int) |
312 | // - List<int> |
313 | // => size of a pointer |
314 | // Arguments: pType - the type of the value |
315 | // boxing - indicates whether the value is boxed or not |
316 | // Return Value: the size of the value |
317 | // Notes: Throws |
318 | // In general, this returns the unboxed size of the value, but if we have a type |
319 | // that represents a non-generic and it's not an unboxed value type, we know that |
320 | // it will be represented as a reference, so we return the size of a pointer instead. |
321 | /* static */ |
322 | ULONG32 CordbValue::GetSizeForType(CordbType * pType, BoxedValue boxing) |
323 | { |
324 | ULONG32 size = 0; |
325 | |
326 | switch(pType->m_elementType) |
327 | { |
328 | case ELEMENT_TYPE_BOOLEAN: |
329 | case ELEMENT_TYPE_CHAR: |
330 | case ELEMENT_TYPE_I1: |
331 | case ELEMENT_TYPE_U1: |
332 | case ELEMENT_TYPE_I2: |
333 | case ELEMENT_TYPE_U2: |
334 | case ELEMENT_TYPE_I4: |
335 | case ELEMENT_TYPE_U4: |
336 | case ELEMENT_TYPE_R4: |
337 | case ELEMENT_TYPE_I8: |
338 | case ELEMENT_TYPE_U8: |
339 | case ELEMENT_TYPE_R8: |
340 | case ELEMENT_TYPE_I: |
341 | case ELEMENT_TYPE_U: pType->GetUnboxedObjectSize(&size); break; |
342 | |
343 | case ELEMENT_TYPE_CLASS: |
344 | case ELEMENT_TYPE_OBJECT: |
345 | case ELEMENT_TYPE_STRING: |
346 | case ELEMENT_TYPE_PTR: |
347 | case ELEMENT_TYPE_BYREF: |
348 | case ELEMENT_TYPE_TYPEDBYREF: |
349 | case ELEMENT_TYPE_ARRAY: |
350 | case ELEMENT_TYPE_SZARRAY: |
351 | case ELEMENT_TYPE_FNPTR: { |
352 | bool isUnboxedVCObject = false; |
353 | |
354 | if (boxing == kUnboxed) |
355 | { |
356 | isUnboxedVCObject = pType->IsValueType(); // throws |
357 | } |
358 | if (!isUnboxedVCObject) |
359 | { |
360 | // if it's not an unboxed value type (we're in the case |
361 | // for compound types), then it's a reference |
362 | // and we just want to return the size of a pointer |
363 | size = sizeof(void *); |
364 | } |
365 | else |
366 | { |
367 | pType->GetUnboxedObjectSize(&size); |
368 | } |
369 | } break; |
370 | |
371 | default: |
372 | _ASSERTE(!"Bad value type!" ); |
373 | } |
374 | return size; |
375 | } // CordbValue::GetSizeForType |
376 | |
377 | |
378 | HRESULT CordbValue::CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint) |
379 | { |
380 | VALIDATE_POINTER_TO_OBJECT(ppBreakpoint, ICorDebugValueBreakpoint **); |
381 | |
382 | return E_NOTIMPL; |
383 | } // CordbValue::CreateBreakpoint |
384 | |
385 | // gets the exact type of a value |
386 | // Arguments: |
387 | // input: none (uses m_type field) |
388 | // output: ppType - an instance of ICDType representing the exact type of the value |
389 | // Return Value: |
390 | HRESULT CordbValue::GetExactType(ICorDebugType **ppType) |
391 | { |
392 | PUBLIC_REENTRANT_API_ENTRY(this); |
393 | VALIDATE_POINTER_TO_OBJECT(ppType, ICorDebugType **); |
394 | FAIL_IF_NEUTERED(this); |
395 | |
396 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
397 | |
398 | *ppType = static_cast<ICorDebugType*> (m_type); |
399 | |
400 | if (*ppType != NULL) |
401 | (*ppType)->AddRef(); |
402 | |
403 | return S_OK; |
404 | } // CordbValue::GetExactType |
405 | |
406 | // CreateHandle for a heap object. |
407 | // @todo: How to prevent this being called by non-heap object? |
408 | // Arguments: |
409 | // input: handleType - type of the handle to be created |
410 | // output: ppHandle - on success, the newly created handle |
411 | // Return Value: S_OK on success or E_INVALIDARG, E_OUTOFMEMORY, or CORDB_E_HELPER_MAY_DEADLOCK |
412 | HRESULT CordbValue::InternalCreateHandle(CorDebugHandleType handleType, |
413 | ICorDebugHandleValue ** ppHandle) |
414 | { |
415 | INTERNAL_SYNC_API_ENTRY(GetProcess()); |
416 | LOG((LF_CORDB,LL_INFO1000,"CV::CreateHandle\n" )); |
417 | |
418 | DebuggerIPCEvent event; |
419 | CordbProcess *process; |
420 | BOOL fStrong = FALSE; |
421 | |
422 | // @dbgtodo- , as part of inspection, convert this path to throwing. |
423 | if (ppHandle == NULL) |
424 | { |
425 | return E_INVALIDARG; |
426 | } |
427 | |
428 | *ppHandle = NULL; |
429 | |
430 | if (handleType == HANDLE_STRONG) |
431 | { |
432 | fStrong = TRUE; |
433 | } |
434 | else |
435 | { |
436 | _ASSERTE(handleType == HANDLE_WEAK_TRACK_RESURRECTION); |
437 | } |
438 | |
439 | |
440 | // Create the ICorDebugHandleValue object |
441 | RSInitHolder<CordbHandleValue> pHandle(new (nothrow) CordbHandleValue(m_appdomain, m_type, handleType) ); |
442 | |
443 | if (pHandle == NULL) |
444 | { |
445 | return E_OUTOFMEMORY; |
446 | } |
447 | |
448 | // Send the event to create the handle. |
449 | process = m_appdomain->GetProcess(); |
450 | _ASSERTE(process != NULL); |
451 | |
452 | process->InitIPCEvent(&event, |
453 | DB_IPCE_CREATE_HANDLE, |
454 | true, |
455 | m_appdomain->GetADToken()); |
456 | |
457 | CORDB_ADDRESS addr = GetValueHome() != NULL ? GetValueHome()->GetAddress() : NULL; |
458 | event.CreateHandle.objectToken = CORDB_ADDRESS_TO_PTR(addr); |
459 | event.CreateHandle.fStrong = fStrong; |
460 | |
461 | // Note: two-way event here... |
462 | HRESULT hr = process->SendIPCEvent(&event, sizeof(DebuggerIPCEvent)); |
463 | hr = WORST_HR(hr, event.hr); |
464 | |
465 | if (SUCCEEDED(hr)) |
466 | { |
467 | _ASSERTE(event.type == DB_IPCE_CREATE_HANDLE_RESULT); |
468 | |
469 | // Initialize the handle value object. |
470 | hr = pHandle->Init(event.CreateHandleResult.vmObjectHandle); |
471 | } |
472 | |
473 | if (!SUCCEEDED(hr)) |
474 | { |
475 | // Free the handle from the left-side. |
476 | pHandle->Dispose(); |
477 | |
478 | // The RSInitHolder will neuter and delete it. |
479 | return hr; |
480 | } |
481 | |
482 | // Pass out the new handle value object. |
483 | pHandle.TransferOwnershipExternal(ppHandle); |
484 | |
485 | return S_OK; |
486 | } // CordbValue::InternalCreateHandle |
487 | |
488 | /* ------------------------------------------------------------------------- * |
489 | * Generic Value class |
490 | * ------------------------------------------------------------------------- */ |
491 | |
492 | // |
493 | // CordbGenericValue constructor that builds a generic value from |
494 | // a remote address or register. |
495 | // Arguments: |
496 | // input: pAppdomain - the app domain to which the value belongs |
497 | // pType - the type of the value |
498 | // remoteValue - buffer (and size) of the remote location where |
499 | // the value resides. This may be NULL if the value |
500 | // is enregistered. |
501 | // ppRemoteRegAddr - information describing the register in which the |
502 | // value resides. This may be NULL--only one of |
503 | // ppRemoteRegAddr and remoteValue will be non-NULL, |
504 | // depending on whether the value is in a register or |
505 | // memory. |
506 | CordbGenericValue::CordbGenericValue(CordbAppDomain * pAppdomain, |
507 | CordbType * pType, |
508 | TargetBuffer remoteValue, |
509 | EnregisteredValueHomeHolder * ppRemoteRegAddr) |
510 | : CordbValue(pAppdomain, pType, remoteValue.pAddress, false), |
511 | m_pValueHome(NULL) |
512 | { |
513 | _ASSERTE(pType->m_elementType != ELEMENT_TYPE_END); |
514 | _ASSERTE(pType->m_elementType != ELEMENT_TYPE_VOID); |
515 | _ASSERTE(pType->m_elementType < ELEMENT_TYPE_MAX); |
516 | |
517 | // We can fill in the size now for generic values. |
518 | ULONG32 size; |
519 | HRESULT hr; |
520 | hr = pType->GetUnboxedObjectSize(&size); |
521 | _ASSERTE (!FAILED(hr)); |
522 | m_size = size; |
523 | |
524 | // now instantiate the value home |
525 | NewHolder<ValueHome> pHome(NULL); |
526 | if (remoteValue.IsEmpty()) |
527 | { |
528 | pHome = (new RegisterValueHome(pAppdomain->GetProcess(), ppRemoteRegAddr)); |
529 | } |
530 | else |
531 | { |
532 | pHome = (new RemoteValueHome(pAppdomain->GetProcess(), remoteValue)); |
533 | } |
534 | m_pValueHome = pHome.GetValue(); // throws |
535 | pHome.SuppressRelease(); |
536 | } // CordbGenericValue::CordbGenericValue |
537 | |
538 | // |
539 | // CordbGenericValue constructor that builds an empty generic value |
540 | // from just an element type. Used for literal values for func evals |
541 | // only. |
542 | // Arguments: |
543 | // input: pType - the type of the value |
544 | CordbGenericValue::CordbGenericValue(CordbType * pType) |
545 | : CordbValue(NULL, pType, NULL, true), |
546 | m_pValueHome(NULL) |
547 | { |
548 | // The only purpose of a literal value is to hold a RS literal value. |
549 | ULONG32 size; |
550 | HRESULT hr; |
551 | hr = pType->GetUnboxedObjectSize(&size); |
552 | _ASSERTE (!FAILED(hr)); |
553 | m_size = size; |
554 | |
555 | memset(m_pCopyOfData, 0, m_size); |
556 | |
557 | // there is no value home for a literal so we leave it as NULL |
558 | } // CordbGenericValue::CordbGenericValue |
559 | |
560 | // destructor |
561 | CordbGenericValue::~CordbGenericValue() |
562 | { |
563 | if (m_pValueHome != NULL) |
564 | { |
565 | delete m_pValueHome; |
566 | m_pValueHome = NULL; |
567 | } |
568 | } // CordbGenericValue::~CordbGenericValue |
569 | |
570 | HRESULT CordbGenericValue::QueryInterface(REFIID id, void **pInterface) |
571 | { |
572 | if (id == IID_ICorDebugValue) |
573 | { |
574 | *pInterface = static_cast<ICorDebugValue*>(static_cast<ICorDebugGenericValue*>(this)); |
575 | } |
576 | else if (id == IID_ICorDebugValue2) |
577 | { |
578 | *pInterface = static_cast<ICorDebugValue2*>(this); |
579 | } |
580 | else if (id == IID_ICorDebugValue3) |
581 | { |
582 | *pInterface = static_cast<ICorDebugValue3*>(this); |
583 | } |
584 | else if (id == IID_ICorDebugGenericValue) |
585 | { |
586 | *pInterface = static_cast<ICorDebugGenericValue*>(this); |
587 | } |
588 | else if (id == IID_IUnknown) |
589 | { |
590 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugGenericValue*>(this)); |
591 | } |
592 | else |
593 | { |
594 | *pInterface = NULL; |
595 | return E_NOINTERFACE; |
596 | } |
597 | |
598 | ExternalAddRef(); |
599 | return S_OK; |
600 | } // CordbGenericValue::QueryInterface |
601 | |
602 | // |
603 | // initialize a generic value by copying the necessary data, either |
604 | // from the remote process or from another value in this process. |
605 | // Argument: |
606 | // input: localValue - RS location of value to be copied. This could be NULL or it |
607 | // could be a field from the cached copy of a CordbVCObjectValue or CordbObjectValue |
608 | // instance or an element from the cached copy of a CordbArrayValue instance |
609 | // Note: Throws error codes from reading process memory |
610 | void CordbGenericValue::Init(MemoryRange localValue) |
611 | { |
612 | INTERNAL_SYNC_API_ENTRY(this->GetProcess()); |
613 | |
614 | if(!m_isLiteral) |
615 | { |
616 | // If neither localValue.StartAddress nor m_remoteValue.pAddress are set, then all that means |
617 | // is that we've got a pre-initialized 64-bit value. |
618 | if (localValue.StartAddress() != NULL) |
619 | { |
620 | // Copy the data out of the local address space. |
621 | localCopy(m_pCopyOfData, localValue); |
622 | } |
623 | else |
624 | { |
625 | m_pValueHome->GetValue(MemoryRange(m_pCopyOfData, m_size)); // throws |
626 | } |
627 | } |
628 | } // CordbGenericValue::Init |
629 | |
630 | // gets the value (i.e., number, boolean or pointer value) for this instance of CordbGenericValue |
631 | // Arguments: |
632 | // output: pTo - the starting address of a buffer in which the value will be written. This buffer must |
633 | // be guaranteed by the caller to be large enough to hold the value. There is no way for |
634 | // us to check here if it is. This must be non-NULL. |
635 | // Return Value: S_OK on success or E_INVALIDARG if the pTo is NULL |
636 | HRESULT CordbGenericValue::GetValue(void *pTo) |
637 | { |
638 | PUBLIC_REENTRANT_API_ENTRY(this); |
639 | FAIL_IF_NEUTERED(this); |
640 | VALIDATE_POINTER_TO_OBJECT_ARRAY(pTo, BYTE, m_size, false, true); |
641 | |
642 | _ASSERTE(m_pCopyOfData != NULL); |
643 | // Copy out the value |
644 | memcpy(pTo, m_pCopyOfData, m_size); |
645 | |
646 | return S_OK; |
647 | } // CordbGenericValue::GetValue |
648 | |
649 | // Sets the value of this instance of CordbGenericValue |
650 | // Arguments: |
651 | // input: pFrom - pointer to a buffer holding the new value. We assume this is the same size as the |
652 | // original value; we have no way to check. This must be non-NULL. |
653 | // Return Value: S_OK on success or E_INVALIDARG if the pFrom is NULL |
654 | HRESULT CordbGenericValue::SetValue(void *pFrom) |
655 | { |
656 | HRESULT hr = S_OK; |
657 | PUBLIC_REENTRANT_API_ENTRY(this); |
658 | FAIL_IF_NEUTERED(this); |
659 | VALIDATE_POINTER_TO_OBJECT_ARRAY(pFrom, BYTE, m_size, true, false); |
660 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
661 | |
662 | // We only need to send to the left side to update values that are |
663 | // object references. For generic values, we can simply do a write |
664 | // memory. |
665 | |
666 | EX_TRY |
667 | { |
668 | if(!m_isLiteral) |
669 | { |
670 | m_pValueHome->SetValue(MemoryRange(pFrom, m_size), m_type); // throws |
671 | } |
672 | } |
673 | EX_CATCH_HRESULT(hr); |
674 | IfFailRet(hr); |
675 | |
676 | // That worked, so update the copy of the value we have in |
677 | // m_copyOfData. |
678 | memcpy(m_pCopyOfData, pFrom, m_size); |
679 | |
680 | return hr; |
681 | } // CordbGenericValue::SetValue |
682 | |
683 | // copies the value from this instance of CordbGenericValue iff the value represents a literal |
684 | // Arguments: |
685 | // output: pBuffer - pointer to the beginning of a caller-allocated buffer.This buffer must |
686 | // be guaranteed by the caller to be large enough to hol |
687 | // d the value. There is no way for |
688 | // us to check here if it is. This must be non-NULL. |
689 | // Return Value: true iff this is a literal value and pBuffer is a valid writeable address |
690 | bool CordbGenericValue::CopyLiteralData(BYTE *pBuffer) |
691 | { |
692 | INTERNAL_SYNC_API_ENTRY(this->GetProcess()); |
693 | _ASSERTE(pBuffer != NULL); |
694 | |
695 | // If this is a RS fabrication, copy the literal data into the |
696 | // given buffer and return true. |
697 | if (m_isLiteral) |
698 | { |
699 | _ASSERTE(m_size <= 8); |
700 | memcpy(pBuffer, m_pCopyOfData, m_size); |
701 | return true; |
702 | } |
703 | else |
704 | return false; |
705 | } // CordbGenericValue::CopyLiteralData |
706 | |
707 | /* ------------------------------------------------------------------------- * |
708 | * Reference Value class |
709 | * ------------------------------------------------------------------------- */ |
710 | |
711 | // constructor |
712 | // Arguments: |
713 | // input: pAppdomain - appdomain to which the value belongs |
714 | // pType - the type of the referent (the object pointed to) |
715 | // localValue - the RS address and size of the buffer from which the reference |
716 | // will be copied. This will be NULL if either remoteValue, |
717 | // ppRemoteRegAddr or vmObjectHandle is non-NULL. Otherwise, it will |
718 | // point into the local cached copy of another instance of ICDValue |
719 | // remoteValue - the LS address and size of the buffer from which the reference |
720 | // will be copied. This will be NULL if either localValue, |
721 | // ppRemoteRegAddr, or vmObjectHandle is non-NULL. |
722 | // ppRemoteRegAddr - information about the register location of the buffer from which |
723 | // the reference will be copied. This will be NULL if either localValue, |
724 | // remoteValue, or vmObjectHandle is non-NULL. |
725 | // vmObjectHandle - a LS object handle holding the reference. This will be NULL if either |
726 | // localValue, remoteValue, or ppRemoteRegAddr is non-NULL. |
727 | // Note: this may throw OOM |
728 | CordbReferenceValue::CordbReferenceValue(CordbAppDomain * pAppdomain, |
729 | CordbType * pType, |
730 | MemoryRange localValue, |
731 | TargetBuffer remoteValue, |
732 | EnregisteredValueHomeHolder * ppRemoteRegAddr, |
733 | VMPTR_OBJECTHANDLE vmObjectHandle) |
734 | : CordbValue(pAppdomain, pType, remoteValue.pAddress, false, |
735 | // We'd like to change this to be a ContinueList so it gets neutered earlier, |
736 | // but it may be a breaking change |
737 | pAppdomain->GetSweepableExitNeuterList()), |
738 | |
739 | m_realTypeOfTypedByref(NULL) |
740 | { |
741 | memset(&m_info, 0, sizeof(m_info)); |
742 | |
743 | LOG((LF_CORDB,LL_EVERYTHING,"CRV::CRV: this:0x%x\n" ,this)); |
744 | m_size = sizeof(void *); |
745 | |
746 | // now instantiate the value home |
747 | NewHolder<ValueHome> pHome(NULL); |
748 | |
749 | if (!vmObjectHandle.IsNull()) |
750 | { |
751 | pHome = (new HandleValueHome(pAppdomain->GetProcess(), vmObjectHandle)); |
752 | m_valueHome.SetObjHandleFlag(false); |
753 | } |
754 | |
755 | else if (remoteValue.IsEmpty()) |
756 | { |
757 | pHome = (new RegisterValueHome(pAppdomain->GetProcess(), ppRemoteRegAddr)); |
758 | m_valueHome.SetObjHandleFlag(true); |
759 | |
760 | } |
761 | else |
762 | { |
763 | pHome = (new RefRemoteValueHome(pAppdomain->GetProcess(), remoteValue)); |
764 | } |
765 | m_valueHome.m_pHome = pHome.GetValue(); // throws |
766 | pHome.SuppressRelease(); |
767 | } // CordbReferenceValue::CordbReferenceValue |
768 | |
769 | // CordbReferenceValue constructor that builds an empty reference value |
770 | // from just an element type. Used for literal values for func evals |
771 | // only. |
772 | // Arguments: |
773 | // input: pType - the type of the value |
774 | CordbReferenceValue::CordbReferenceValue(CordbType * pType) |
775 | : CordbValue(NULL, pType, NULL, true, pType->GetAppDomain()->GetSweepableExitNeuterList()) |
776 | { |
777 | memset(&m_info, 0, sizeof(m_info)); |
778 | |
779 | // The only purpose of a literal value is to hold a RS literal value. |
780 | m_size = sizeof(void*); |
781 | |
782 | // there is no value home for a literal |
783 | m_valueHome.m_pHome = NULL; |
784 | } // CordbReferenceValue::CordbReferenceValue |
785 | |
786 | // copies the value from this instance of CordbReferenceValue iff the value represents a literal |
787 | // Arguments: |
788 | // output: pBuffer - pointer to the beginning of a caller-allocated buffer.This buffer must |
789 | // be guaranteed by the caller to be large enough to hold the value. |
790 | // There is no way for us to check here if it is. This must be non-NULL. |
791 | // Return Value: true iff this is a literal value and pBuffer is a valid writeable address |
792 | bool CordbReferenceValue::CopyLiteralData(BYTE *pBuffer) |
793 | { |
794 | _ASSERTE(pBuffer != NULL); |
795 | |
796 | // If this is a RS fabrication, then its a null reference. |
797 | if (m_isLiteral) |
798 | { |
799 | void *n = NULL; |
800 | memcpy(pBuffer, &n, sizeof(n)); |
801 | return true; |
802 | } |
803 | else |
804 | return false; |
805 | } // CordbReferenceValue::CopyLiteralData |
806 | |
807 | // destructor |
808 | CordbReferenceValue::~CordbReferenceValue() |
809 | { |
810 | DTOR_ENTRY(this); |
811 | |
812 | LOG((LF_CORDB,LL_EVERYTHING,"CRV::~CRV: this:0x%x\n" ,this)); |
813 | |
814 | _ASSERTE(IsNeutered()); |
815 | } // CordbReferenceValue::~CordbReferenceValue |
816 | |
817 | void CordbReferenceValue::Neuter() |
818 | { |
819 | if (m_valueHome.m_pHome != NULL) |
820 | { |
821 | m_valueHome.m_pHome->Clear(); |
822 | delete m_valueHome.m_pHome; |
823 | m_valueHome.m_pHome = NULL; |
824 | } |
825 | |
826 | m_realTypeOfTypedByref = NULL; |
827 | CordbValue::Neuter(); |
828 | } // CordbReferenceValue::Neuter |
829 | |
830 | |
831 | HRESULT CordbReferenceValue::QueryInterface(REFIID id, void **pInterface) |
832 | { |
833 | if (id == IID_ICorDebugValue) |
834 | { |
835 | *pInterface = static_cast<ICorDebugValue*>(static_cast<ICorDebugReferenceValue*>(this)); |
836 | } |
837 | else if (id == IID_ICorDebugValue2) |
838 | { |
839 | *pInterface = static_cast<ICorDebugValue2*>(this); |
840 | } |
841 | else if (id == IID_ICorDebugValue3) |
842 | { |
843 | *pInterface = static_cast<ICorDebugValue3*>(this); |
844 | } |
845 | else if (id == IID_ICorDebugReferenceValue) |
846 | { |
847 | *pInterface = static_cast<ICorDebugReferenceValue*>(this); |
848 | } |
849 | else if (id == IID_IUnknown) |
850 | { |
851 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugReferenceValue*>(this)); |
852 | } |
853 | else |
854 | { |
855 | *pInterface = NULL; |
856 | return E_NOINTERFACE; |
857 | } |
858 | |
859 | ExternalAddRef(); |
860 | return S_OK; |
861 | } // CordbReferenceValue::QueryInterface |
862 | |
863 | // gets the type of the referent of the object ref |
864 | // Arguments: |
865 | // output: pType - the type of the value. The caller must guarantee that pType is non-null. |
866 | // Return Value: S_OK on success, E_INVALIDARG on failure |
867 | HRESULT CordbReferenceValue::GetType(CorElementType *pType) |
868 | { |
869 | LIMITED_METHOD_CONTRACT; |
870 | |
871 | FAIL_IF_NEUTERED(this); |
872 | VALIDATE_POINTER_TO_OBJECT(pType, CorElementType *); |
873 | |
874 | if( m_type == NULL ) |
875 | { |
876 | // We may not have a CordbType if we were created from a GC handle to NULL |
877 | _ASSERTE( m_info.objTypeData.elementType == ELEMENT_TYPE_CLASS ); |
878 | _ASSERTE(!m_valueHome.ObjHandleIsNull()); |
879 | _ASSERTE( m_info.objRef == NULL ); |
880 | *pType = m_info.objTypeData.elementType; |
881 | } |
882 | else |
883 | { |
884 | // The element type stored in both places should match |
885 | _ASSERTE( m_info.objTypeData.elementType == m_type->m_elementType ); |
886 | *pType = m_type->m_elementType; |
887 | } |
888 | |
889 | return S_OK; |
890 | } // CordbReferenceValue::GetType |
891 | |
892 | // gets the remote (LS) address of the reference. This may return NULL if the |
893 | // reference is a literal or resides in a register. |
894 | // Arguments: |
895 | // output: pAddress - the LS location of the reference. The caller must guarantee pAddress is non-null, |
896 | // but the contents may be null after the call if the reference is enregistered or is |
897 | // the value of a field or element of some other Cordb*Value instance. |
898 | // Return Value: S_OK on success or E_INVALIDARG if pAddress is null |
899 | HRESULT CordbReferenceValue::GetAddress(CORDB_ADDRESS *pAddress) |
900 | { |
901 | PUBLIC_REENTRANT_API_ENTRY(this); |
902 | VALIDATE_POINTER_TO_OBJECT(pAddress, CORDB_ADDRESS *); |
903 | |
904 | *pAddress = m_valueHome.m_pHome ? m_valueHome.m_pHome->GetAddress() : NULL; |
905 | return (S_OK); |
906 | } |
907 | |
908 | // Determines whether the reference is null |
909 | // Arguments: |
910 | // output - pfIsNull - pointer to a BOOL that will be set to true iff this represents a |
911 | // null reference |
912 | // Return Value: S_OK on success or E_INVALIDARG if pfIsNull is null |
913 | HRESULT CordbReferenceValue::IsNull(BOOL * pfIsNull) |
914 | { |
915 | PUBLIC_REENTRANT_API_ENTRY(this); |
916 | FAIL_IF_NEUTERED(this); |
917 | VALIDATE_POINTER_TO_OBJECT(pfIsNull, BOOL *); |
918 | |
919 | if (m_isLiteral || (m_info.objRef == NULL)) |
920 | *pfIsNull = TRUE; |
921 | else |
922 | *pfIsNull = FALSE; |
923 | |
924 | return S_OK; |
925 | } |
926 | |
927 | // gets the value (object address) of this CordbReferenceValue |
928 | // Arguments: |
929 | // output: pTo - reference value |
930 | // Return Value: S_OK on success or E_INVALIDARG if pAddress is null |
931 | HRESULT CordbReferenceValue::GetValue(CORDB_ADDRESS *pAddress) |
932 | { |
933 | PUBLIC_REENTRANT_API_ENTRY(this); |
934 | VALIDATE_POINTER_TO_OBJECT(pAddress, CORDB_ADDRESS *); |
935 | FAIL_IF_NEUTERED(this); |
936 | |
937 | // Copy out the value, which is simply the value the object reference. |
938 | if (m_isLiteral) |
939 | *pAddress = NULL; |
940 | else |
941 | *pAddress = PTR_TO_CORDB_ADDRESS(m_info.objRef); |
942 | |
943 | return S_OK; |
944 | } |
945 | |
946 | // sets the value of the reference |
947 | // Arguments: |
948 | // input: address - the new reference--this must be a LS address |
949 | // Return Value: S_OK on success or E_INVALIDARG or write process memory errors |
950 | // Note: We make no effort to ensure that the new reference is of the same type as the old one. |
951 | // We simply assume it is. As long as this assumption is correct, we only need to update information about |
952 | // the referent if it's a string (its length can change). |
953 | |
954 | // @dbgtodo Microsoft inspection: consider whether it's worthwhile to verify that the type of the new referent is |
955 | // 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 |
956 | // this, since we need to know the type of the new referent. |
957 | HRESULT CordbReferenceValue::SetValue(CORDB_ADDRESS address) |
958 | { |
959 | PUBLIC_API_ENTRY(this); |
960 | FAIL_IF_NEUTERED(this); |
961 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
962 | HRESULT hr = S_OK; |
963 | |
964 | // If this is a heap object, ideally we'd prevent violations of AppDomain isolation |
965 | // here. However, we have no reliable way of determining what AppDomain the address is in. |
966 | |
967 | // Can't change literal refs. |
968 | if (m_isLiteral) |
969 | { |
970 | return E_INVALIDARG; |
971 | } |
972 | |
973 | // Either we know the type, or it's a handle to a null value |
974 | _ASSERTE((m_type != NULL) || |
975 | (!m_valueHome.ObjHandleIsNull() && (m_info.objRef == NULL))); |
976 | |
977 | EX_TRY |
978 | { |
979 | m_valueHome.m_pHome->SetValue(MemoryRange(&address, sizeof(void *)), m_type); // throws |
980 | } |
981 | EX_CATCH_HRESULT(hr); |
982 | |
983 | if (SUCCEEDED(hr)) |
984 | { |
985 | // That worked, so update the copy of the value we have in |
986 | // our local cache. |
987 | m_info.objRef = CORDB_ADDRESS_TO_PTR(address); |
988 | |
989 | |
990 | if (m_info.objTypeData.elementType == ELEMENT_TYPE_STRING) |
991 | { |
992 | // update information about the string |
993 | InitRef(MemoryRange(&m_info.objRef, sizeof (void *))); |
994 | } |
995 | |
996 | // All other data in m_info is no longer valid, and we may have invalidated other |
997 | // ICDRVs at this address. We have to invalidate all cached debuggee data. |
998 | m_appdomain->GetProcess()->m_continueCounter++; |
999 | } |
1000 | |
1001 | return hr; |
1002 | } // CordbReferenceValue::SetValue |
1003 | |
1004 | HRESULT CordbReferenceValue::DereferenceStrong(ICorDebugValue **ppValue) |
1005 | { |
1006 | return E_NOTIMPL; |
1007 | } |
1008 | |
1009 | // Get a new ICDValue instance to represent the referent of this object ref. |
1010 | // Arguments: |
1011 | // output: ppValue - the new ICDValue instance |
1012 | // Return Value: S_OK on success or E_INVALIDARG |
1013 | HRESULT CordbReferenceValue::Dereference(ICorDebugValue **ppValue) |
1014 | { |
1015 | PUBLIC_REENTRANT_API_ENTRY(this); |
1016 | FAIL_IF_NEUTERED(this); |
1017 | |
1018 | // Can't dereference literal refs. |
1019 | if (m_isLiteral) |
1020 | return E_INVALIDARG; |
1021 | |
1022 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
1023 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1024 | |
1025 | HRESULT hr = S_OK; |
1026 | |
1027 | if (m_continueCounterLastSync != m_appdomain->GetProcess()->m_continueCounter) |
1028 | { |
1029 | IfFailRet(InitRef(MemoryRange(NULL, 0))); |
1030 | } |
1031 | |
1032 | EX_TRY |
1033 | { |
1034 | // We may know ahead of time (depending on the reference type) if |
1035 | // the reference is bad. |
1036 | if ((m_info.objRefBad) || (m_info.objRef == NULL)) |
1037 | { |
1038 | ThrowHR(CORDBG_E_BAD_REFERENCE_VALUE); |
1039 | } |
1040 | |
1041 | hr = DereferenceCommon(m_appdomain, m_type, m_realTypeOfTypedByref, &m_info, ppValue); |
1042 | } |
1043 | EX_CATCH_HRESULT(hr); |
1044 | return hr; |
1045 | |
1046 | } |
1047 | |
1048 | //----------------------------------------------------------------------------- |
1049 | // Common helper to dereferefence. |
1050 | // Parameters: |
1051 | // pAppDomain, pType, pInfo - necessary paramters to create the value |
1052 | // pRealTypeOfTypedByref - type for a potential TypedByRef. Can be NULL if we know |
1053 | // that we're not a typed-byref (this is true if we're definitely an object handle) |
1054 | // ppValue - outparameter for newly created value. This will get an Ext AddRef. |
1055 | //----------------------------------------------------------------------------- |
1056 | HRESULT CordbReferenceValue::DereferenceCommon( |
1057 | CordbAppDomain * pAppDomain, |
1058 | CordbType * pType, |
1059 | CordbType * pRealTypeOfTypedByref, |
1060 | DebuggerIPCE_ObjectData * pInfo, |
1061 | ICorDebugValue **ppValue |
1062 | ) |
1063 | { |
1064 | INTERNAL_SYNC_API_ENTRY(pAppDomain->GetProcess()); |
1065 | |
1066 | // pCachedObject may be NULL if we're not caching. |
1067 | _ASSERTE(pType != NULL); |
1068 | _ASSERTE(pAppDomain != NULL); |
1069 | _ASSERTE(pInfo != NULL); |
1070 | _ASSERTE(ppValue != NULL); |
1071 | |
1072 | HRESULT hr = S_OK; |
1073 | *ppValue = NULL; // just to be safe. |
1074 | |
1075 | switch(pType->m_elementType) |
1076 | { |
1077 | case ELEMENT_TYPE_CLASS: |
1078 | case ELEMENT_TYPE_OBJECT: |
1079 | case ELEMENT_TYPE_STRING: |
1080 | { |
1081 | LOG((LF_CORDB, LL_INFO1000, "DereferenceInternal: type class/object/string\n" )); |
1082 | // An object value (possibly a string value, too.) If the class of this object is a value class, |
1083 | // then we have a reference to a boxed object. So we create a box instead of an object value. |
1084 | bool isBoxedVCObject = false; |
1085 | |
1086 | if ((pType->m_pClass != NULL) && (pType->m_elementType != ELEMENT_TYPE_STRING)) |
1087 | { |
1088 | EX_TRY |
1089 | { |
1090 | isBoxedVCObject = pType->m_pClass->IsValueClass(); |
1091 | } |
1092 | EX_CATCH_HRESULT(hr); |
1093 | if (FAILED(hr)) |
1094 | { |
1095 | return hr; |
1096 | } |
1097 | } |
1098 | |
1099 | if (isBoxedVCObject) |
1100 | { |
1101 | TargetBuffer remoteValue(PTR_TO_CORDB_ADDRESS(pInfo->objRef), (ULONG)pInfo->objSize); |
1102 | EX_TRY |
1103 | { |
1104 | RSSmartPtr<CordbBoxValue> pBoxValue(new CordbBoxValue( |
1105 | pAppDomain, |
1106 | pType, |
1107 | remoteValue, |
1108 | (ULONG32)pInfo->objSize, |
1109 | pInfo->objOffsetToVars)); |
1110 | pBoxValue->ExternalAddRef(); |
1111 | *ppValue = (ICorDebugValue*)(ICorDebugBoxValue*)pBoxValue; |
1112 | } |
1113 | EX_CATCH_HRESULT(hr); |
1114 | } |
1115 | else |
1116 | { |
1117 | RSSmartPtr<CordbObjectValue> pObj; |
1118 | TargetBuffer remoteValue(PTR_TO_CORDB_ADDRESS(pInfo->objRef), (ULONG)pInfo->objSize); |
1119 | // Note: we call Init() by default when we create (or refresh) a reference value, so we |
1120 | // never have to do it again. |
1121 | EX_TRY |
1122 | { |
1123 | pObj.Assign(new CordbObjectValue(pAppDomain, pType, remoteValue, pInfo)); |
1124 | IfFailThrow(pObj->Init()); |
1125 | |
1126 | pObj->ExternalAddRef(); |
1127 | *ppValue = static_cast<ICorDebugValue*>( static_cast<ICorDebugObjectValue*>(pObj) ); |
1128 | } |
1129 | EX_CATCH_HRESULT(hr); |
1130 | } // boxed? |
1131 | |
1132 | break; |
1133 | } |
1134 | |
1135 | case ELEMENT_TYPE_ARRAY: |
1136 | case ELEMENT_TYPE_SZARRAY: |
1137 | { |
1138 | LOG((LF_CORDB, LL_INFO1000, "DereferenceInternal: type array/szarray\n" )); |
1139 | TargetBuffer remoteValue(PTR_TO_CORDB_ADDRESS(pInfo->objRef), (ULONG)pInfo->objSize); // sizeof(void *)? |
1140 | EX_TRY |
1141 | { |
1142 | RSSmartPtr<CordbArrayValue> pArrayValue(new CordbArrayValue( |
1143 | pAppDomain, |
1144 | pType, |
1145 | pInfo, |
1146 | remoteValue)); |
1147 | |
1148 | IfFailThrow(pArrayValue->Init()); |
1149 | |
1150 | pArrayValue->ExternalAddRef(); |
1151 | *ppValue = (ICorDebugValue*)(ICorDebugArrayValue*)pArrayValue; |
1152 | } |
1153 | EX_CATCH_HRESULT(hr); |
1154 | |
1155 | break; |
1156 | } |
1157 | |
1158 | case ELEMENT_TYPE_BYREF: |
1159 | case ELEMENT_TYPE_PTR: |
1160 | { |
1161 | //_ASSERTE(pInfo->objToken.IsNull()); // can't get this type w/ an object handle |
1162 | |
1163 | LOG((LF_CORDB, LL_INFO1000, "DereferenceInternal: type byref/ptr\n" )); |
1164 | CordbType *ptrType; |
1165 | pType->DestUnaryType(&ptrType); |
1166 | |
1167 | CorElementType et = ptrType->m_elementType; |
1168 | |
1169 | if (et == ELEMENT_TYPE_VOID) |
1170 | { |
1171 | *ppValue = NULL; |
1172 | return CORDBG_S_VALUE_POINTS_TO_VOID; |
1173 | } |
1174 | |
1175 | TargetBuffer remoteValue(pInfo->objRef, GetSizeForType(ptrType, kUnboxed)); |
1176 | // Create a value for what this reference points to. Note: |
1177 | // this could be almost any type of value. |
1178 | EX_TRY |
1179 | { |
1180 | CordbValue::CreateValueByType( |
1181 | pAppDomain, |
1182 | ptrType, |
1183 | false, |
1184 | remoteValue, |
1185 | MemoryRange(NULL, 0), // local value |
1186 | NULL, |
1187 | ppValue); // throws |
1188 | } |
1189 | EX_CATCH_HRESULT(hr); |
1190 | |
1191 | break; |
1192 | } |
1193 | |
1194 | case ELEMENT_TYPE_TYPEDBYREF: |
1195 | { |
1196 | //_ASSERTE(pInfo->objToken.IsNull()); // can't get this type w/ an object handle |
1197 | _ASSERTE(pRealTypeOfTypedByref != NULL); |
1198 | |
1199 | LOG((LF_CORDB, LL_INFO1000, "DereferenceInternal: type typedbyref\n" )); |
1200 | |
1201 | TargetBuffer remoteValue(pInfo->objRef, sizeof(void *)); |
1202 | // Create the value for what this reference points |
1203 | // to. |
1204 | EX_TRY |
1205 | { |
1206 | CordbValue::CreateValueByType( |
1207 | pAppDomain, |
1208 | pRealTypeOfTypedByref, |
1209 | false, |
1210 | remoteValue, |
1211 | MemoryRange(NULL, 0), // local value |
1212 | NULL, |
1213 | ppValue); // throws |
1214 | } |
1215 | EX_CATCH_HRESULT(hr); |
1216 | |
1217 | break; |
1218 | } |
1219 | |
1220 | case ELEMENT_TYPE_FNPTR: |
1221 | // Function pointers cannot be dereferenced; only the pointer value itself |
1222 | // may be inspected--not what it points to. |
1223 | *ppValue = NULL; |
1224 | return CORDBG_E_VALUE_POINTS_TO_FUNCTION; |
1225 | |
1226 | default: |
1227 | LOG((LF_CORDB, LL_INFO1000, "DereferenceInternal: Fail!\n" )); |
1228 | _ASSERTE(!"Bad reference type!" ); |
1229 | hr = E_FAIL; |
1230 | break; |
1231 | } |
1232 | |
1233 | return hr; |
1234 | } |
1235 | |
1236 | // static helper to build a CordbReferenceValue from a general variable home. |
1237 | // We can find the CordbType from the object instance. |
1238 | HRESULT CordbReferenceValue::Build(CordbAppDomain * appdomain, |
1239 | CordbType * type, |
1240 | TargetBuffer remoteValue, |
1241 | MemoryRange localValue, |
1242 | VMPTR_OBJECTHANDLE vmObjectHandle, |
1243 | EnregisteredValueHomeHolder * ppRemoteRegAddr, |
1244 | CordbReferenceValue** ppValue) |
1245 | { |
1246 | HRESULT hr = S_OK; |
1247 | |
1248 | // We can find the AD from an object handle (but not a normal object), so the AppDomain may |
1249 | // be NULL if if it's an OH. |
1250 | //_ASSERTE((appdomain != NULL) || objectRefsInHandles); |
1251 | |
1252 | // A reference, possibly to an object or value class |
1253 | // Weak by default |
1254 | EX_TRY |
1255 | { |
1256 | RSSmartPtr<CordbReferenceValue> pRefValue(new CordbReferenceValue(appdomain, |
1257 | type, |
1258 | localValue, |
1259 | remoteValue, |
1260 | ppRemoteRegAddr, |
1261 | vmObjectHandle)); |
1262 | IfFailThrow(pRefValue->InitRef(localValue)); |
1263 | |
1264 | pRefValue->InternalAddRef(); |
1265 | *ppValue = pRefValue; |
1266 | } |
1267 | EX_CATCH_HRESULT(hr) |
1268 | return hr; |
1269 | } |
1270 | |
1271 | //----------------------------------------------------------------------------- |
1272 | // Static helper to build a CordbReferenceValue from a GCHandle |
1273 | // The LS can actually determine an AppDomain from an OBJECTHandles, however, the RS |
1274 | // should already have this infromation too, so we pass it in. |
1275 | // We also supply the AppDomain here because it provides the CordbValue with |
1276 | // process affinity. |
1277 | // Note that the GC handle may point to a NULL reference, in which case we should still create |
1278 | // an appropriate ICorDebugReferenceValue for which IsNull returns TRUE. |
1279 | //----------------------------------------------------------------------------- |
1280 | HRESULT CordbReferenceValue::BuildFromGCHandle( |
1281 | CordbAppDomain *pAppDomain, |
1282 | VMPTR_OBJECTHANDLE gcHandle, |
1283 | ICorDebugReferenceValue ** pOutRef |
1284 | ) |
1285 | { |
1286 | _ASSERTE(pAppDomain != NULL); |
1287 | _ASSERTE(pOutRef != NULL); |
1288 | |
1289 | CordbProcess * pProc; |
1290 | pProc = pAppDomain->GetProcess(); |
1291 | INTERNAL_SYNC_API_ENTRY(pProc); |
1292 | |
1293 | HRESULT hr = S_OK; |
1294 | |
1295 | *pOutRef = NULL; |
1296 | |
1297 | // Make sure we even have a GC handle. |
1298 | // Also, We may have a handle, but its contents may be null. |
1299 | if (gcHandle.IsNull()) |
1300 | { |
1301 | // We've seen this assert fire in the wild, but have never gotten a repro. |
1302 | // so we'll include a runtime check to avoid the AV. |
1303 | _ASSERTE(false || !"We got a bad reference value." ); |
1304 | return CORDBG_E_BAD_REFERENCE_VALUE; |
1305 | } |
1306 | |
1307 | // Now that we've got an AppDomain, we can go ahead and create the reference value normally. |
1308 | |
1309 | RSSmartPtr<CordbReferenceValue> pRefValue; |
1310 | TargetBuffer remoteValue; |
1311 | EX_TRY |
1312 | { |
1313 | remoteValue.Init(pProc->GetDAC()->GetHandleAddressFromVmHandle(gcHandle), sizeof(void *)); |
1314 | } |
1315 | EX_CATCH_HRESULT(hr); |
1316 | IfFailRet(hr); |
1317 | |
1318 | hr = CordbReferenceValue::Build( |
1319 | pAppDomain, |
1320 | NULL, // unknown type |
1321 | remoteValue, // CORDB_ADDRESS remoteAddress, |
1322 | MemoryRange(NULL, 0), |
1323 | gcHandle, // objectRefsInHandles, |
1324 | NULL, // EnregisteredValueHome * pRemoteRegAddr, |
1325 | &pRefValue); |
1326 | |
1327 | if (SUCCEEDED(hr)) |
1328 | { |
1329 | pRefValue->QueryInterface(__uuidof(ICorDebugReferenceValue), (void**)pOutRef); |
1330 | } |
1331 | |
1332 | return hr; |
1333 | } |
1334 | |
1335 | // Helper function for SanityCheckPointer. Make an attempt to read memory at the address which is the value |
1336 | // of the reference. |
1337 | // Arguments: none |
1338 | // Notes: |
1339 | // - Throws |
1340 | // - m_info.objRefBad must be set to true before calling this function. If we throw, we'll |
1341 | // never end up setting m_info.objRefBad, but throwing indicates that the reference is |
1342 | // indeed bad. Only if we exit normally will we end up setting m_info.objRefBad to false. |
1343 | void CordbReferenceValue::TryDereferencingTarget() |
1344 | { |
1345 | _ASSERTE(!!m_info.objRefBad == true); |
1346 | // First get the referent type |
1347 | CordbType * pReferentType; |
1348 | m_type->DestUnaryType(&pReferentType); |
1349 | |
1350 | // Next get the size |
1351 | ULONG32 dataSize, sizeToRead; |
1352 | IfFailThrow(pReferentType->GetUnboxedObjectSize(&dataSize)); |
1353 | if (dataSize <= 0) |
1354 | sizeToRead = 1; // Read at least one byte. |
1355 | else if (dataSize >= 8) |
1356 | sizeToRead = 8; // Read at most eight bytes--this is just a perf improvement. Even if we read |
1357 | // all the bytes, we are only able to determine that we can read those bytes, |
1358 | // we can't really tell if the data we are reading is actually the data we |
1359 | // want. |
1360 | else sizeToRead = dataSize; |
1361 | |
1362 | // Now see if we can read from the address where the object is supposed to be |
1363 | BYTE dummy[8]; |
1364 | |
1365 | // Get a target buffer with the remote address and size of the object--since we don't know if the |
1366 | // address if valid, this could throw or return a size that's complete garbage |
1367 | TargetBuffer object(m_info.objRef, sizeToRead); |
1368 | |
1369 | // now read target memory. This may throw ... |
1370 | GetProcess()->SafeReadBuffer(object, dummy); |
1371 | |
1372 | } // CordbReferenceValue::TryDereferencingTarget |
1373 | |
1374 | // Do a sanity check on the pointer which is the value of the object reference. We can't efficiently ensure that |
1375 | // the pointer is really good, so we settle for a quick check just to make sure the memory at the address is |
1376 | // readable. We're actually just checking that we can dereference the pointer. |
1377 | // Arguments: |
1378 | // input: type - the type of the pointer to which the object reference points. |
1379 | // output: none, but fills in m_info.objRefBad |
1380 | // Note: Throws |
1381 | void CordbReferenceValue::SanityCheckPointer (CorElementType type) |
1382 | { |
1383 | m_info.objRefBad = TRUE; |
1384 | if (type != ELEMENT_TYPE_FNPTR) |
1385 | { |
1386 | // We should never dereference a function pointer, so all references |
1387 | // are considered "bad." |
1388 | if (m_info.objRef != NULL) |
1389 | { |
1390 | if (type == ELEMENT_TYPE_PTR) |
1391 | { |
1392 | // The only way to tell if the reference in PTR is bad or |
1393 | // not is to try to deref the thing. |
1394 | TryDereferencingTarget(); |
1395 | } |
1396 | } // !m_info.m_basicData.m_vmObject.IsNull() |
1397 | // else Null refs are considered "bad". |
1398 | } // type != ELEMENT_TYPE_FNPTR |
1399 | |
1400 | // we made it without throwing, so we'll assume (perhaps wrongly) that the ref is good |
1401 | m_info.objRefBad = FALSE; |
1402 | |
1403 | } // CordbReferenceValue::SanityCheckPointer |
1404 | |
1405 | // get information about the reference when it's not an object address but another kind of pointer type: |
1406 | // ELEMENT_TYPE_BYREF, ELEMENT_TYPE_PTR or ELEMENT_TYPE_FNPTR |
1407 | // Arguments: |
1408 | // input: type - type of the referent |
1409 | // localValue - starting address and length of a local buffer containing the object ref |
1410 | // Notes: |
1411 | // - fills in the m_info field of "this" |
1412 | // - Throws (errors from reading process memory) |
1413 | void CordbReferenceValue::GetPointerData(CorElementType type, MemoryRange localValue) |
1414 | { |
1415 | HRESULT hr = S_OK; |
1416 | // Fill in the type since we will not be getting it from the DAC |
1417 | m_info.objTypeData.elementType = type; |
1418 | |
1419 | // First get the objRef |
1420 | if (localValue.StartAddress() != NULL) |
1421 | { |
1422 | // localValue represents a buffer containing a copy of the objectRef that exists locally. It could be a |
1423 | // component of a container type residing within a local cached copy belonging to some other |
1424 | // Cordb*Value instance representing the container type. In this case it will be a field, array |
1425 | // element, or referent of a different object reference for that other Cordb*Value instance. It |
1426 | // could also be a pointer to the value of a local register display of the frame from which this object |
1427 | // ref comes. |
1428 | |
1429 | // For example, if we have a value class (represented by a CordbVCObject instance) with a field |
1430 | // that is an object pointer, localValue will contain a pointer to that field in the local |
1431 | // cache of the CordbVCObjectValue instance (CordbVCObjectValue::m_pObjectCopy). |
1432 | |
1433 | // Note, though, that pLocalValue holds the address of a target object. We will cache |
1434 | // the contents of pLocalValue (the object ref) here for efficiency of read access, but if we |
1435 | // want to set the reference later (e.g., we want the object ref to point to NULL instead of an |
1436 | // object), we'll have to set the object ref in the target, not our local copy. |
1437 | // Host memory Target memory |
1438 | // --------------- | |
1439 | // CordbVCObjectValue::m_copyOfObject ----> | | |
1440 | // | ... | | |
1441 | // | | |
1442 | // |---------------| | Object |
1443 | // localAddress ---> | object addr |-------------> -------------- |
1444 | // |---------------| | ---> | | |
1445 | // | ... | | | | |
1446 | // --------------- | | -------------- |
1447 | // | |
1448 | // CordbReferenceValue::m_info.objRef ---> --------------- | | |
1449 | // | object addr |--------- |
1450 | // --------------- | |
1451 | |
1452 | _ASSERTE(localValue.Size() == sizeof(void *)); |
1453 | localCopy(&(m_info.objRef), localValue); |
1454 | } |
1455 | else |
1456 | { |
1457 | // we have a non-local location, so we'll get the value of the ref from its home |
1458 | |
1459 | // do some preinitialization in case we get an exception |
1460 | EX_TRY |
1461 | { |
1462 | m_valueHome.m_pHome->GetValue(MemoryRange(&(m_info.objRef), sizeof(void*))); // throws |
1463 | } |
1464 | EX_CATCH_HRESULT(hr); |
1465 | if (FAILED(hr)) |
1466 | { |
1467 | m_info.objRef = NULL; |
1468 | m_info.objRefBad = TRUE; |
1469 | ThrowHR(hr); |
1470 | } |
1471 | } |
1472 | |
1473 | EX_TRY |
1474 | { |
1475 | // If we made it this far, we need to sanity check the pointer--we'll just see if we can |
1476 | // read at that address |
1477 | SanityCheckPointer(type); |
1478 | } |
1479 | EX_CATCH_HRESULT(hr); // we don't need to do anything here, m_info.objRefBad will have been set to true |
1480 | |
1481 | } // CordbReferenceValue::GetPointerData |
1482 | |
1483 | // Helper function for CordbReferenceValue::GetObjectData: Sets default values for the fields in pObjectData |
1484 | // before processing begins. Not all will necessarily be initialized during processing. |
1485 | // Arguments: |
1486 | // input: objectType - type of the referent of the objRef being examined |
1487 | // output: pObjectData - information about the reference to be initialized |
1488 | void PreInitObjectData(DebuggerIPCE_ObjectData * pObjectData, void * objAddress, CorElementType objectType) |
1489 | { |
1490 | _ASSERTE(pObjectData != NULL); |
1491 | |
1492 | memset(pObjectData, 0, sizeof(DebuggerIPCE_ObjectData)); |
1493 | pObjectData->objRef = objAddress; |
1494 | pObjectData->objTypeData.elementType = objectType; |
1495 | |
1496 | } // PreInitObjectData |
1497 | |
1498 | // get basic object specific data when a reference points to an object, plus extra data if the object is an |
1499 | // array or string |
1500 | // Arguments: |
1501 | // input: pProcess - process to which the object belongs |
1502 | // objectAddress - pointer to the TypedByRef object (this is the value of the object reference |
1503 | // or handle. |
1504 | // type - the type of the object referenced |
1505 | // vmAppDomain - appdomain to which the object belongs |
1506 | // output: pInfo - filled with information about the object to which the TypedByRef refers. |
1507 | // Note: Throws |
1508 | /* static */ |
1509 | void CordbReferenceValue::GetObjectData(CordbProcess * pProcess, |
1510 | void * objectAddress, |
1511 | CorElementType type, |
1512 | VMPTR_AppDomain vmAppdomain, |
1513 | DebuggerIPCE_ObjectData * pInfo) |
1514 | { |
1515 | IDacDbiInterface *pInterface = pProcess->GetDAC(); |
1516 | CORDB_ADDRESS objTargetAddr = PTR_TO_CORDB_ADDRESS(objectAddress); |
1517 | |
1518 | // make sure we don't end up with old garbage values in case the reference is bad |
1519 | PreInitObjectData(pInfo, objectAddress, type); |
1520 | |
1521 | pInterface->GetBasicObjectInfo(objTargetAddr, type, vmAppdomain, pInfo); |
1522 | |
1523 | if (!pInfo->objRefBad) |
1524 | { |
1525 | // for certain referent types, we need a bit more information: |
1526 | if (pInfo->objTypeData.elementType == ELEMENT_TYPE_STRING) |
1527 | { |
1528 | pInterface->GetStringData(objTargetAddr, pInfo); |
1529 | } |
1530 | else if ((pInfo->objTypeData.elementType == ELEMENT_TYPE_ARRAY) || |
1531 | (pInfo->objTypeData.elementType == ELEMENT_TYPE_SZARRAY)) |
1532 | { |
1533 | pInterface->GetArrayData(objTargetAddr, pInfo); |
1534 | } |
1535 | } |
1536 | |
1537 | } // CordbReferenceValue::GetObjectData |
1538 | |
1539 | // get information about a TypedByRef object when the reference is the address of a TypedByRef structure. |
1540 | // Arguments: |
1541 | // input: pProcess - process to which the object belongs |
1542 | // pTypedByRef - pointer to the TypedByRef object (this is the value of the object reference or |
1543 | // handle. |
1544 | // type - the type of the object referenced |
1545 | // vmAppDomain - appdomain to which the object belongs |
1546 | // output: pInfo - filled with information about the object to which the TypedByRef refers. |
1547 | // Note: Throws |
1548 | /* static */ |
1549 | void CordbReferenceValue::GetTypedByRefData(CordbProcess * pProcess, |
1550 | CORDB_ADDRESS pTypedByRef, |
1551 | CorElementType type, |
1552 | VMPTR_AppDomain vmAppDomain, |
1553 | DebuggerIPCE_ObjectData * pInfo) |
1554 | { |
1555 | |
1556 | // make sure we don't end up with old garbage values since we don't set all the values for TypedByRef objects |
1557 | PreInitObjectData(pInfo, CORDB_ADDRESS_TO_PTR(pTypedByRef), type); |
1558 | |
1559 | // Though pTypedByRef is the value of the object ref represented by an instance of CordbReferenceValue, |
1560 | // it is not the address of an object, as we would ordinarily expect. Instead, in the special case of |
1561 | // TypedByref objects, it is actually the address of the TypedByRef struct which contains the |
1562 | // type and the object address. |
1563 | |
1564 | pProcess->GetDAC()->GetTypedByRefInfo(pTypedByRef, vmAppDomain, pInfo); |
1565 | } // CordbReferenceValue::GetTypedByRefData |
1566 | |
1567 | // get the address of the object referenced |
1568 | // Arguments: none |
1569 | // Return Value: the address of the object referenced (i.e., the value of the object ref) |
1570 | // Note: Throws |
1571 | void * CordbReferenceValue::GetObjectAddress(MemoryRange localValue) |
1572 | { |
1573 | void * objectAddress; |
1574 | if (localValue.StartAddress() != NULL) |
1575 | { |
1576 | // the object ref comes from a local cached copy |
1577 | _ASSERTE(localValue.Size() == sizeof(void *)); |
1578 | memcpy(&objectAddress, localValue.StartAddress(), localValue.Size()); |
1579 | } |
1580 | else |
1581 | { |
1582 | _ASSERTE(m_valueHome.m_pHome != NULL); |
1583 | m_valueHome.m_pHome->GetValue(MemoryRange(&objectAddress, sizeof(void *))); // throws |
1584 | } |
1585 | return objectAddress; |
1586 | } // CordbReferenceValue::GetObjectAddress |
1587 | |
1588 | // update type information after initializing -- when we initialize, we may get more exact type information |
1589 | // than we previously had |
1590 | // Arguments: none--uses and updates data members |
1591 | // Note: Throws |
1592 | void CordbReferenceValue::UpdateTypeInfo() |
1593 | { |
1594 | // If the object type that we got back is different than the one we sent, then it means that we |
1595 | // originally had a CLASS and now have something more specific, like a SDARRAY, MDARRAY, or STRING or |
1596 | // a constructed type. |
1597 | // Update our signature accordingly, which is okay since we always have a copy of our sig. This |
1598 | // ensures that the reference's signature accurately reflects what the Runtime knows it's pointing |
1599 | // to. |
1600 | // |
1601 | // GENERICS: do this for all types: for example, an array might have been discovered to be a more |
1602 | // specific kind of array (String[] where an Object[] was expected). |
1603 | CordbType *newtype; |
1604 | |
1605 | IfFailThrow(CordbType::TypeDataToType(m_appdomain, &m_info.objTypeData, &newtype)); |
1606 | |
1607 | _ASSERTE(newtype->m_elementType != ELEMENT_TYPE_VALUETYPE); |
1608 | m_type.Assign(newtype); // implicit Release + AddRef |
1609 | |
1610 | // For typed-byref's the act of dereferencing the object also reveals to us |
1611 | // what the "real" type of the object is... |
1612 | if (m_info.objTypeData.elementType == ELEMENT_TYPE_TYPEDBYREF) |
1613 | { |
1614 | IfFailThrow(CordbType::TypeDataToType(m_appdomain, |
1615 | &m_info.typedByrefInfo.typedByrefType, |
1616 | &m_realTypeOfTypedByref)); |
1617 | } |
1618 | } // CordbReferenceValue::UpdateTypeInfo |
1619 | |
1620 | // Initialize this CordbReferenceValue. This may involve inspecting the LS to get information about the |
1621 | // referent. |
1622 | // Arguments: |
1623 | // input: localValue - buffer address and size of the RS location of the reference. (This may be NULL |
1624 | // if the reference didn't come from a local cached copy. See |
1625 | // code:CordbReferenceValue::GetPointerData for further explanation of local locations.) |
1626 | // Return Value: S_OK on success or E_INVALIDARG or write process memory errors on failure |
1627 | |
1628 | HRESULT CordbReferenceValue::InitRef(MemoryRange localValue) |
1629 | { |
1630 | INTERNAL_SYNC_API_ENTRY(this->GetProcess()); |
1631 | |
1632 | HRESULT hr = S_OK; |
1633 | CordbProcess * pProcess = GetProcess(); |
1634 | |
1635 | // Simple init needed for literal refs. Literals may have a null process / appdomain ptr. |
1636 | if (m_isLiteral) |
1637 | { |
1638 | _ASSERTE(m_type != NULL); |
1639 | m_info.objTypeData.elementType = m_type->m_elementType; |
1640 | return hr; |
1641 | } |
1642 | |
1643 | _ASSERTE((pProcess->GetShim() == NULL) || pProcess->GetSynchronized()); |
1644 | |
1645 | // If the helper thread is dead, then pretend this is a bad reference. |
1646 | if (GetProcess()->m_helperThreadDead) |
1647 | { |
1648 | m_info.objRef = NULL; |
1649 | m_info.objRefBad = TRUE; |
1650 | return hr; |
1651 | } |
1652 | |
1653 | m_continueCounterLastSync = pProcess->m_continueCounter; |
1654 | |
1655 | // If no type provided, then it's b/c we're a class and we'll get the type when we get Created. |
1656 | CorElementType type = (m_type != NULL) ? (m_type->m_elementType) : ELEMENT_TYPE_CLASS; |
1657 | _ASSERTE (type != ELEMENT_TYPE_GENERICINST); |
1658 | _ASSERTE (type != ELEMENT_TYPE_VAR); |
1659 | _ASSERTE (type != ELEMENT_TYPE_MVAR); |
1660 | |
1661 | EX_TRY |
1662 | { |
1663 | if ((type == ELEMENT_TYPE_BYREF) || |
1664 | (type == ELEMENT_TYPE_PTR) || |
1665 | (type == ELEMENT_TYPE_FNPTR)) |
1666 | { |
1667 | // we know the size is just the size of a pointer, so we can just read process memory to get the |
1668 | // information we need |
1669 | GetPointerData(type, localValue); |
1670 | } |
1671 | else // we have to get more information about the object from the DAC |
1672 | { |
1673 | if (type == ELEMENT_TYPE_TYPEDBYREF) |
1674 | { |
1675 | _ASSERTE(m_valueHome.m_pHome != NULL); |
1676 | GetTypedByRefData(pProcess, |
1677 | m_valueHome.m_pHome->GetAddress(), |
1678 | type, |
1679 | m_appdomain->GetADToken(), |
1680 | &m_info); |
1681 | } |
1682 | else |
1683 | { |
1684 | GetObjectData(pProcess, GetObjectAddress(localValue), type, m_appdomain->GetADToken(), &m_info); |
1685 | } |
1686 | |
1687 | // if we got (what we believe is probably) a good reference, we should update the type info |
1688 | if (!m_info.objRefBad) |
1689 | { |
1690 | // we may have gotten back a more specific type than we had previously |
1691 | UpdateTypeInfo(); |
1692 | } |
1693 | } |
1694 | } |
1695 | EX_CATCH_HRESULT(hr); |
1696 | return hr; |
1697 | } // CordbReferenceValue::InitRef |
1698 | |
1699 | /* ------------------------------------------------------------------------- * |
1700 | * Object Value class |
1701 | * ------------------------------------------------------------------------- */ |
1702 | |
1703 | |
1704 | // validate a CordbObjectValue to ensure it hasn't been neutered |
1705 | #define COV_VALIDATE_OBJECT() do { \ |
1706 | BOOL bValid; \ |
1707 | HRESULT hr; \ |
1708 | if (FAILED(hr = IsValid(&bValid))) \ |
1709 | return hr; \ |
1710 | \ |
1711 | if (!bValid) \ |
1712 | { \ |
1713 | return CORDBG_E_INVALID_OBJECT; \ |
1714 | } \ |
1715 | }while(0) |
1716 | |
1717 | // constructor |
1718 | // Arguments: |
1719 | // input: pAppDomain - the appdomain to which the object belongs |
1720 | // pType - the type of the object |
1721 | // remoteValue - the LS address and size of the object |
1722 | // pObjectData - other information about the object, most importantly, the offset to the |
1723 | // fields of the object |
1724 | CordbObjectValue::CordbObjectValue(CordbAppDomain * pAppdomain, |
1725 | CordbType * pType, |
1726 | TargetBuffer remoteValue, |
1727 | DebuggerIPCE_ObjectData *pObjectData ) |
1728 | : CordbValue(pAppdomain, pType, remoteValue.pAddress, |
1729 | false, pAppdomain->GetProcess()->GetContinueNeuterList()), |
1730 | m_info(*pObjectData), |
1731 | m_pObjectCopy(NULL), m_objectLocalVars(NULL), m_stringBuffer(NULL), |
1732 | m_valueHome(pAppdomain->GetProcess(), remoteValue), |
1733 | m_fIsExceptionObject(FALSE), m_fIsRcw(FALSE) |
1734 | { |
1735 | _ASSERTE(pAppdomain != NULL); |
1736 | |
1737 | m_size = m_info.objSize; |
1738 | |
1739 | HRESULT hr = S_FALSE; |
1740 | |
1741 | ALLOW_DATATARGET_MISSING_MEMORY |
1742 | ( |
1743 | hr = IsExceptionObject(); |
1744 | ); |
1745 | |
1746 | if (hr == S_OK) |
1747 | m_fIsExceptionObject = TRUE; |
1748 | |
1749 | hr = S_FALSE; |
1750 | ALLOW_DATATARGET_MISSING_MEMORY |
1751 | ( |
1752 | hr = IsRcw(); |
1753 | ); |
1754 | |
1755 | if (hr == S_OK) |
1756 | m_fIsRcw = TRUE; |
1757 | } // CordbObjectValue::CordbObjectValue |
1758 | |
1759 | // destructor |
1760 | CordbObjectValue::~CordbObjectValue() |
1761 | { |
1762 | DTOR_ENTRY(this); |
1763 | |
1764 | _ASSERTE(IsNeutered()); |
1765 | } // CordbObjectValue::~CordbObjectValue |
1766 | |
1767 | void CordbObjectValue::Neuter() |
1768 | { |
1769 | // Destroy the copy of the object. |
1770 | if (m_pObjectCopy != NULL) |
1771 | { |
1772 | delete [] m_pObjectCopy; |
1773 | m_pObjectCopy = NULL; |
1774 | } |
1775 | |
1776 | CordbValue::Neuter(); |
1777 | } // CordbObjectValue::Neuter |
1778 | |
1779 | HRESULT CordbObjectValue::QueryInterface(REFIID id, void **pInterface) |
1780 | { |
1781 | if (id == IID_ICorDebugValue) |
1782 | { |
1783 | *pInterface = static_cast<ICorDebugValue*>(static_cast<ICorDebugObjectValue*>(this)); |
1784 | } |
1785 | else if (id == IID_ICorDebugValue2) |
1786 | { |
1787 | *pInterface = static_cast<ICorDebugValue2*>(this); |
1788 | } |
1789 | else if (id == IID_ICorDebugValue3) |
1790 | { |
1791 | *pInterface = static_cast<ICorDebugValue3*>(this); |
1792 | } |
1793 | else if (id == IID_ICorDebugObjectValue) |
1794 | { |
1795 | *pInterface = static_cast<ICorDebugObjectValue*>(this); |
1796 | } |
1797 | else if (id == IID_ICorDebugObjectValue2) |
1798 | { |
1799 | *pInterface = static_cast<ICorDebugObjectValue2*>(this); |
1800 | } |
1801 | else if (id == IID_ICorDebugGenericValue) |
1802 | { |
1803 | *pInterface = static_cast<ICorDebugGenericValue*>(this); |
1804 | } |
1805 | else if (id == IID_ICorDebugHeapValue) |
1806 | { |
1807 | *pInterface = static_cast<ICorDebugHeapValue*>(this); |
1808 | } |
1809 | else if (id == IID_ICorDebugHeapValue2) |
1810 | { |
1811 | *pInterface = static_cast<ICorDebugHeapValue2*>(this); |
1812 | } |
1813 | else if (id == IID_ICorDebugHeapValue3) |
1814 | { |
1815 | *pInterface = static_cast<ICorDebugHeapValue3*>(this); |
1816 | } |
1817 | else if ((id == IID_ICorDebugStringValue) && |
1818 | (m_info.objTypeData.elementType == ELEMENT_TYPE_STRING)) |
1819 | { |
1820 | *pInterface = static_cast<ICorDebugStringValue*>(this); |
1821 | } |
1822 | else if (id == IID_ICorDebugExceptionObjectValue && m_fIsExceptionObject) |
1823 | { |
1824 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugExceptionObjectValue*>(this)); |
1825 | } |
1826 | else if (id == IID_ICorDebugComObjectValue && m_fIsRcw) |
1827 | { |
1828 | *pInterface = static_cast<ICorDebugComObjectValue*>(this); |
1829 | } |
1830 | else if (id == IID_IUnknown) |
1831 | { |
1832 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugObjectValue*>(this)); |
1833 | } |
1834 | else |
1835 | { |
1836 | *pInterface = NULL; |
1837 | return E_NOINTERFACE; |
1838 | } |
1839 | |
1840 | ExternalAddRef(); |
1841 | return S_OK; |
1842 | } // CordbObjectValue::QueryInterface |
1843 | |
1844 | // gets the type of the object |
1845 | // Arguments: |
1846 | // output: pType - the type of the value. The caller must guarantee that pType is non-null. |
1847 | // Return Value: S_OK on success, E_INVALIDARG on failure |
1848 | HRESULT CordbObjectValue::GetType(CorElementType *pType) |
1849 | { |
1850 | PUBLIC_REENTRANT_API_ENTRY(this); |
1851 | FAIL_IF_NEUTERED(this); |
1852 | return (CordbValue::GetType(pType)); |
1853 | } // CordbObjectValue::GetType |
1854 | |
1855 | // gets the size of the object |
1856 | // Arguments: |
1857 | // output: pSize - the size of the value. The caller must guarantee that pSize is non-null. |
1858 | // Return Value: S_OK on success, E_INVALIDARG on failure |
1859 | HRESULT CordbObjectValue::GetSize(ULONG32 *pSize) |
1860 | { |
1861 | PUBLIC_REENTRANT_API_ENTRY(this); |
1862 | FAIL_IF_NEUTERED(this); |
1863 | return (CordbValue::GetSize(pSize)); |
1864 | } // CordbObjectValue::GetSize |
1865 | |
1866 | // gets the size of the object |
1867 | // Arguments: |
1868 | // output: pSize - the size of the value. The caller must guarantee that pSize is non-null. |
1869 | // Return Value: S_OK on success, E_INVALIDARG on failure |
1870 | HRESULT CordbObjectValue::GetSize64(ULONG64 *pSize) |
1871 | { |
1872 | PUBLIC_REENTRANT_API_ENTRY(this); |
1873 | FAIL_IF_NEUTERED(this); |
1874 | return (CordbValue::GetSize64(pSize)); |
1875 | } // CordbObjectValue::GetSize64 |
1876 | |
1877 | |
1878 | // gets the remote (LS) address of the object. This may return NULL if the |
1879 | // object is a literal or resides in a register. |
1880 | // Arguments: |
1881 | // output: pAddress - the LS address (the contents should not be null since objects |
1882 | // aren't enregistered nor are they fields or elements of other |
1883 | // types). The caller must ensure that pAddress is not null. |
1884 | // Return Value: S_OK on success or E_INVALIDARG if pAddress is null |
1885 | HRESULT CordbObjectValue::GetAddress(CORDB_ADDRESS *pAddress) |
1886 | { |
1887 | PUBLIC_REENTRANT_API_ENTRY(this); |
1888 | FAIL_IF_NEUTERED(this); |
1889 | COV_VALIDATE_OBJECT(); |
1890 | VALIDATE_POINTER_TO_OBJECT(pAddress, CORDB_ADDRESS *); |
1891 | |
1892 | *pAddress = m_valueHome.GetAddress(); |
1893 | return (S_OK); |
1894 | } // CordbObjectValue::GetAddress |
1895 | |
1896 | HRESULT CordbObjectValue::CreateBreakpoint(ICorDebugValueBreakpoint ** ppBreakpoint) |
1897 | { |
1898 | PUBLIC_REENTRANT_API_ENTRY(this); |
1899 | FAIL_IF_NEUTERED(this); |
1900 | COV_VALIDATE_OBJECT(); |
1901 | |
1902 | return (CordbValue::CreateBreakpoint(ppBreakpoint)); |
1903 | } |
1904 | |
1905 | // determine if "this" is still valid (i.e., not neutered) |
1906 | // Arguments: |
1907 | // output: pfIsValid - true iff "this" is still not neutered |
1908 | // Return Value: S_OK or E_INVALIDARG |
1909 | HRESULT CordbObjectValue::IsValid(BOOL * pfIsValid) |
1910 | { |
1911 | PUBLIC_REENTRANT_API_ENTRY(this); |
1912 | VALIDATE_POINTER_TO_OBJECT(pfIsValid, BOOL *); |
1913 | FAIL_IF_NEUTERED(this); |
1914 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1915 | |
1916 | // We're neutered on continue, so we're valid up until the time we're neutered |
1917 | (*pfIsValid) = TRUE; |
1918 | return S_OK; |
1919 | } |
1920 | |
1921 | HRESULT CordbObjectValue::CreateRelocBreakpoint( |
1922 | ICorDebugValueBreakpoint **ppBreakpoint) |
1923 | { |
1924 | PUBLIC_REENTRANT_API_ENTRY(this); |
1925 | FAIL_IF_NEUTERED(this); |
1926 | VALIDATE_POINTER_TO_OBJECT(ppBreakpoint, ICorDebugValueBreakpoint **); |
1927 | |
1928 | COV_VALIDATE_OBJECT(); |
1929 | |
1930 | return E_NOTIMPL; |
1931 | } |
1932 | |
1933 | /* |
1934 | * Creates a handle of the given type for this heap value. |
1935 | * |
1936 | * Not Implemented In-Proc. |
1937 | */ |
1938 | HRESULT CordbObjectValue::CreateHandle( |
1939 | CorDebugHandleType handleType, |
1940 | ICorDebugHandleValue ** ppHandle) |
1941 | { |
1942 | PUBLIC_API_ENTRY(this); |
1943 | FAIL_IF_NEUTERED(this); |
1944 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1945 | |
1946 | return CordbValue::InternalCreateHandle(handleType, ppHandle); |
1947 | } // CreateHandle |
1948 | |
1949 | // Get class information for this object |
1950 | // Arguments: |
1951 | // output: ppClass - ICDClass instance for this object |
1952 | // Return Value: S_OK if success, CORDBG_E_CLASS_NOT_LOADED, E_INVALIDARG, OOM on failure |
1953 | HRESULT CordbObjectValue::GetClass(ICorDebugClass **ppClass) |
1954 | { |
1955 | PUBLIC_REENTRANT_API_ENTRY(this); |
1956 | VALIDATE_POINTER_TO_OBJECT(ppClass, ICorDebugClass **); |
1957 | FAIL_IF_NEUTERED(this); |
1958 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1959 | |
1960 | HRESULT hr = S_OK; |
1961 | if (m_type->m_pClass == NULL) |
1962 | { |
1963 | if (FAILED(hr = m_type->Init(FALSE))) |
1964 | return hr; |
1965 | } |
1966 | |
1967 | _ASSERTE(m_type->m_pClass); |
1968 | *ppClass = (ICorDebugClass*) m_type->m_pClass; |
1969 | |
1970 | if (*ppClass != NULL) |
1971 | (*ppClass)->AddRef(); |
1972 | |
1973 | return hr; |
1974 | } // CordbObjectValue::GetClass |
1975 | |
1976 | |
1977 | |
1978 | |
1979 | |
1980 | //----------------------------------------------------------------------------- |
1981 | // |
1982 | // Public API to get instance field of the given type in the object and returns an ICDValue for it. |
1983 | // |
1984 | // Arguments: |
1985 | // pType - The type containing the field token. |
1986 | // fieldDef - The field's metadata def. |
1987 | // ppValue - OUT: the ICDValue for the field. |
1988 | // |
1989 | // Returns: |
1990 | // S_OK on success. E_INVALIDARG, CORDBG_E_ENC_HANGING_FIELD, CORDBG_E_FIELD_NOT_INSTANCE or OOM on |
1991 | // failure |
1992 | // |
1993 | // Notes: |
1994 | // This is for instance fields only. |
1995 | // Lookup on code:CordbType::GetStaticFieldValue to get static fields. |
1996 | // This is generics aware. |
1997 | HRESULT CordbObjectValue::GetFieldValueForType(ICorDebugType * pType, |
1998 | mdFieldDef fieldDef, |
1999 | ICorDebugValue ** ppValue) |
2000 | { |
2001 | PUBLIC_REENTRANT_API_ENTRY(this); |
2002 | VALIDATE_POINTER_TO_OBJECT(pType, ICorDebugType *); |
2003 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
2004 | |
2005 | FAIL_IF_NEUTERED(this); |
2006 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2007 | |
2008 | COV_VALIDATE_OBJECT(); |
2009 | |
2010 | CordbType * pCordbType = NULL; |
2011 | HRESULT hr = S_OK; |
2012 | |
2013 | EX_TRY |
2014 | { |
2015 | BOOL fSyncBlockField = FALSE; |
2016 | SIZE_T fldOffset; |
2017 | |
2018 | // |
2019 | // <TODO>@todo: need to ensure that pType is really on the class |
2020 | // hierarchy of m_class!!!</TODO> |
2021 | // |
2022 | if (pType == NULL) |
2023 | { |
2024 | pCordbType = m_type; |
2025 | } |
2026 | else |
2027 | { |
2028 | pCordbType = static_cast<CordbType *>(pType); |
2029 | } |
2030 | |
2031 | // Validate the token. |
2032 | if (pCordbType->m_pClass == NULL) |
2033 | { |
2034 | ThrowHR(E_INVALIDARG); |
2035 | } |
2036 | IMetaDataImport * pImport = pCordbType->m_pClass->GetModule()->GetMetaDataImporter(); |
2037 | |
2038 | if (!pImport->IsValidToken(fieldDef)) |
2039 | { |
2040 | ThrowHR(E_INVALIDARG); |
2041 | } |
2042 | |
2043 | FieldData * pFieldData; |
2044 | |
2045 | #ifdef _DEBUG |
2046 | pFieldData = NULL; |
2047 | #endif |
2048 | |
2049 | hr = pCordbType->GetFieldInfo(fieldDef, &pFieldData); |
2050 | |
2051 | // If we couldn't get field info because the field was added with EnC |
2052 | if (hr == CORDBG_E_ENC_HANGING_FIELD) |
2053 | { |
2054 | // The instance field hangs off the syncblock, get its address |
2055 | hr = pCordbType->m_pClass->GetEnCHangingField(fieldDef, &pFieldData, this); |
2056 | |
2057 | if (SUCCEEDED(hr)) |
2058 | { |
2059 | fSyncBlockField = TRUE; |
2060 | } |
2061 | } |
2062 | |
2063 | if (SUCCEEDED(hr)) |
2064 | { |
2065 | _ASSERTE(pFieldData != NULL); |
2066 | |
2067 | if (pFieldData->m_fFldIsStatic) |
2068 | { |
2069 | ThrowHR(CORDBG_E_FIELD_NOT_INSTANCE); |
2070 | } |
2071 | |
2072 | // Compute the remote address, too, so that SetValue will work. |
2073 | // Note that if pFieldData is a syncBlock field, fldOffset will have been cooked |
2074 | // to produce the correct result here. |
2075 | _ASSERTE(pFieldData->OkToGetOrSetInstanceOffset()); |
2076 | fldOffset = pFieldData->GetInstanceOffset(); |
2077 | |
2078 | CordbModule * pModule = pCordbType->m_pClass->GetModule(); |
2079 | |
2080 | SigParser sigParser; |
2081 | IfFailThrow(pFieldData->GetFieldSignature(pModule, &sigParser)); |
2082 | |
2083 | CordbType * pFieldType; |
2084 | IfFailThrow(CordbType::SigToType(pModule, &sigParser, &(pCordbType->m_inst), &pFieldType)); |
2085 | |
2086 | ULONG32 size = GetSizeForType(pFieldType, kUnboxed); |
2087 | |
2088 | void * localAddr = NULL; |
2089 | if (!fSyncBlockField) |
2090 | { |
2091 | // verify that the field starts and ends before the end of m_pObjectCopy |
2092 | _ASSERTE(m_info.objOffsetToVars + fldOffset < m_size); |
2093 | _ASSERTE(m_info.objOffsetToVars + fldOffset + size <= m_size); |
2094 | localAddr = m_objectLocalVars + fldOffset; |
2095 | } |
2096 | |
2097 | // pass the computed local field address, but don't claim we have a local addr if the fldOffset |
2098 | // has been cooked to point us to a sync block field. |
2099 | m_valueHome.CreateInternalValue(pFieldType, |
2100 | m_info.objOffsetToVars + fldOffset, |
2101 | localAddr, |
2102 | size, |
2103 | ppValue); // throws |
2104 | } |
2105 | |
2106 | // If we can't get it b/c it's a constant, then say so. |
2107 | hr = CordbClass::PostProcessUnavailableHRESULT(hr, pImport, fieldDef); |
2108 | } |
2109 | EX_CATCH_HRESULT(hr); |
2110 | return hr; |
2111 | } // CordbObjectValue::GetFieldValueForType |
2112 | |
2113 | // Public implementation of ICorDebugObjectValue::GetFieldValue |
2114 | // Arguments: |
2115 | // input: pClass - class information for this object |
2116 | // fieldDef - the field token for the requested field |
2117 | // output: ppValue - instance of ICDValue created to represent the field |
2118 | // Return Value: S_OK on success, E_INVALIDARG, CORDBG_E_ENC_HANGING_FIELD, CORDBG_E_FIELD_NOT_INSTANCE |
2119 | // or OOM on failure |
2120 | HRESULT CordbObjectValue::GetFieldValue(ICorDebugClass *pClass, |
2121 | mdFieldDef fieldDef, |
2122 | ICorDebugValue **ppValue) |
2123 | { |
2124 | PUBLIC_REENTRANT_API_ENTRY(this); |
2125 | FAIL_IF_NEUTERED(this); |
2126 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2127 | VALIDATE_POINTER_TO_OBJECT(pClass, ICorDebugClass *); |
2128 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
2129 | |
2130 | COV_VALIDATE_OBJECT(); |
2131 | |
2132 | HRESULT hr; |
2133 | _ASSERTE(m_type); |
2134 | |
2135 | if (m_type->m_elementType != ELEMENT_TYPE_CLASS && |
2136 | m_type->m_elementType != ELEMENT_TYPE_VALUETYPE) |
2137 | { |
2138 | return E_INVALIDARG; |
2139 | } |
2140 | |
2141 | // mdFieldDef may specify a field within a base class. mdFieldDef tokens are unique throughout a module. |
2142 | // So we still need a metadata scope to resolve the mdFieldDef. We can infer the scope from pClass. |
2143 | // Beware that this Type may be derived from a type in another module, and so the incoming |
2144 | // fieldDef has to be resolved in the metadata scope of pClass. |
2145 | |
2146 | RSExtSmartPtr<CordbType> relevantType; |
2147 | |
2148 | // This object has an ICorDebugType which has the type-parameters for generics. |
2149 | // ICorDebugClass provided by the caller does not have type-parameters. So we resolve that |
2150 | // by using the provided ICDClass with the type parameters from this object's ICDType. |
2151 | if (FAILED (hr= m_type->GetParentType((CordbClass *) pClass, &relevantType))) |
2152 | { |
2153 | return hr; |
2154 | } |
2155 | // Upon exit relevantType will either be the appropriate type for the |
2156 | // class we're looking for. |
2157 | |
2158 | hr = GetFieldValueForType(relevantType, fieldDef, ppValue); |
2159 | // GetParentType adds one reference to relevantType., Holder dtor releases |
2160 | return hr; |
2161 | |
2162 | } // CordbObjectValue::GetFieldValue |
2163 | |
2164 | HRESULT CordbObjectValue::GetVirtualMethod(mdMemberRef memberRef, |
2165 | ICorDebugFunction **ppFunction) |
2166 | { |
2167 | VALIDATE_POINTER_TO_OBJECT(ppFunction, ICorDebugFunction **); |
2168 | FAIL_IF_NEUTERED(this); |
2169 | COV_VALIDATE_OBJECT(); |
2170 | |
2171 | return E_NOTIMPL; |
2172 | } // CordbObjectValue::GetVirtualMethod |
2173 | |
2174 | HRESULT CordbObjectValue::GetVirtualMethodAndType(mdMemberRef memberRef, |
2175 | ICorDebugFunction **ppFunction, |
2176 | ICorDebugType **ppType) |
2177 | { |
2178 | FAIL_IF_NEUTERED(this); |
2179 | VALIDATE_POINTER_TO_OBJECT(ppFunction, ICorDebugFunction **); |
2180 | VALIDATE_POINTER_TO_OBJECT(ppFunction, ICorDebugType **); |
2181 | |
2182 | COV_VALIDATE_OBJECT(); |
2183 | |
2184 | return E_NOTIMPL; |
2185 | } // CordbObjectValue::GetVirtualMethodAndType |
2186 | |
2187 | HRESULT CordbObjectValue::GetContext(ICorDebugContext **ppContext) |
2188 | { |
2189 | FAIL_IF_NEUTERED(this); |
2190 | VALIDATE_POINTER_TO_OBJECT(ppContext, ICorDebugContext **); |
2191 | |
2192 | COV_VALIDATE_OBJECT(); |
2193 | |
2194 | return E_NOTIMPL; |
2195 | } // CordbObjectValue::GetContext |
2196 | |
2197 | // determines whether this represents a value class-- always returns false |
2198 | // Arguments: |
2199 | // output: pfIsValueClass - always false; CordbVCObjectValue is used to represent |
2200 | // value classes, so by definition, a CordbObjectValue instance |
2201 | // does not represent a value class |
2202 | // Return Value: S_OK |
2203 | // |
2204 | HRESULT CordbObjectValue::IsValueClass(BOOL * pfIsValueClass) |
2205 | { |
2206 | FAIL_IF_NEUTERED(this); |
2207 | COV_VALIDATE_OBJECT(); |
2208 | |
2209 | if (pfIsValueClass) // don't assign to a null pointer! |
2210 | *pfIsValueClass = FALSE; |
2211 | |
2212 | return S_OK; |
2213 | } // CordbObjectValue::IsValueClass |
2214 | |
2215 | HRESULT CordbObjectValue::GetManagedCopy(IUnknown **ppObject) |
2216 | { |
2217 | // GetManagedCopy() is deprecated. In the case where the version of |
2218 | // the debugger doesn't match the version of the debuggee, the two processes |
2219 | // might have dangerously different notions of the layout of an object. |
2220 | |
2221 | // This function is deprecated |
2222 | return E_NOTIMPL; |
2223 | } // CordbObjectValue::GetManagedCopy |
2224 | |
2225 | HRESULT CordbObjectValue::SetFromManagedCopy(IUnknown *pObject) |
2226 | { |
2227 | // Deprecated for the same reason as GetManagedCopy() |
2228 | return E_NOTIMPL; |
2229 | } // CordbObjectValue::SetFromManagedCopy |
2230 | |
2231 | // gets a copy of the value |
2232 | // Arguments: |
2233 | // output: pTo - buffer to hold the object copy. The caller must guarantee that this |
2234 | // is non-null and the buffer is large enough to hold the object |
2235 | // Return Value: S_OK or CORDBG_E_INVALID_OBJECT, CORDBG_E_OBJECT_NEUTERED, or E_INVALIDARG on failure |
2236 | // |
2237 | HRESULT CordbObjectValue::GetValue(void *pTo) |
2238 | { |
2239 | FAIL_IF_NEUTERED(this); |
2240 | COV_VALIDATE_OBJECT(); |
2241 | |
2242 | VALIDATE_POINTER_TO_OBJECT_ARRAY(pTo, BYTE, m_size, false, true); |
2243 | |
2244 | // Copy out the value, which is the whole object. |
2245 | memcpy(pTo, m_pObjectCopy, m_size); |
2246 | |
2247 | return S_OK; |
2248 | } // CordbObjectValue::GetValue |
2249 | |
2250 | HRESULT CordbObjectValue::SetValue(void *pFrom) |
2251 | { |
2252 | // You're not allowed to set a whole object at once. |
2253 | return E_INVALIDARG; |
2254 | } // CordbObjectValue::SetValue |
2255 | |
2256 | // If this instance of CordbObjectValue is actually a string, get its length |
2257 | // Arguments: |
2258 | // output: pcchString - the count of characters in the string |
2259 | // Return Value: S_OK or CORDBG_E_INVALID_OBJECT, CORDBG_E_OBJECT_NEUTERED, or E_INVALIDARG on failure |
2260 | // Note: if the object is not really a string, the value in pcchString will be garbage on exit |
2261 | HRESULT CordbObjectValue::GetLength(ULONG32 *pcchString) |
2262 | { |
2263 | PUBLIC_REENTRANT_API_ENTRY(this); |
2264 | VALIDATE_POINTER_TO_OBJECT(pcchString, SIZE_T *); |
2265 | FAIL_IF_NEUTERED(this); |
2266 | |
2267 | _ASSERTE(m_info.objTypeData.elementType == ELEMENT_TYPE_STRING); |
2268 | |
2269 | COV_VALIDATE_OBJECT(); |
2270 | |
2271 | *pcchString = (ULONG32)m_info.stringInfo.length; |
2272 | return S_OK; |
2273 | } // CordbObjectValue::GetLength |
2274 | |
2275 | // If this instance of CordbObjectValue represents a string, extract the string and its length. |
2276 | // If cchString is less than the length of the string, we'll return only the first cchString characters |
2277 | // but pcchString will still hold the full length. If cchString is more than the string length, we'll |
2278 | // return only string length characters. |
2279 | // Arguments: |
2280 | // input: cchString - the maximum number of characters to return, including NULL terminator |
2281 | // output: pcchString - the actual length of the string, excluding NULL terminator (this may be greater than cchString) |
2282 | // szString - a buffer holding the string. The memory for this must be allocated and |
2283 | // managed by the caller and must have space for at least cchString characters |
2284 | // Return Value: S_OK or CORDBG_E_INVALID_OBJECT, CORDBG_E_OBJECT_NEUTERED, or E_INVALIDARG on failure |
2285 | HRESULT CordbObjectValue::GetString(ULONG32 cchString, |
2286 | ULONG32 *pcchString, |
2287 | __out_ecount_opt(cchString) WCHAR szString[]) |
2288 | { |
2289 | PUBLIC_REENTRANT_API_ENTRY(this); |
2290 | FAIL_IF_NEUTERED(this); |
2291 | VALIDATE_POINTER_TO_OBJECT_ARRAY(szString, WCHAR, cchString, true, true); |
2292 | VALIDATE_POINTER_TO_OBJECT(pcchString, SIZE_T *); |
2293 | |
2294 | _ASSERTE(m_info.objTypeData.elementType == ELEMENT_TYPE_STRING); |
2295 | |
2296 | COV_VALIDATE_OBJECT(); |
2297 | |
2298 | if ((szString == NULL) || (cchString == 0)) |
2299 | return E_INVALIDARG; |
2300 | |
2301 | // Add 1 to include null terminator |
2302 | SIZE_T len = m_info.stringInfo.length + 1; |
2303 | |
2304 | // adjust length to the size of the buffer |
2305 | if (cchString < len) |
2306 | len = cchString; |
2307 | |
2308 | memcpy(szString, m_stringBuffer, len * 2); |
2309 | *pcchString = (ULONG32)m_info.stringInfo.length; |
2310 | |
2311 | return S_OK; |
2312 | } // CordbObjectValue::GetString |
2313 | |
2314 | // Initialize an instance of CordbObjectValue, filling in the m_pObjectCopy field and, if appropriate, |
2315 | // string information. |
2316 | // Arguments: none |
2317 | // ReturnValue: S_OK on success or E_OUTOFMEMORY or read process memory errors on failure |
2318 | HRESULT CordbObjectValue::Init() |
2319 | { |
2320 | INTERNAL_SYNC_API_ENTRY(this->GetProcess()); // |
2321 | LOG((LF_CORDB,LL_INFO1000,"Invoking COV::Init\n" )); |
2322 | |
2323 | HRESULT hr = S_OK; |
2324 | |
2325 | _ASSERTE (m_info.objTypeData.elementType != ELEMENT_TYPE_GENERICINST); |
2326 | _ASSERTE (m_info.objTypeData.elementType != ELEMENT_TYPE_VAR); |
2327 | _ASSERTE (m_info.objTypeData.elementType != ELEMENT_TYPE_MVAR); |
2328 | |
2329 | // Copy the entire object over to this process. |
2330 | m_pObjectCopy = new (nothrow) BYTE[m_size]; |
2331 | |
2332 | if (m_pObjectCopy == NULL) |
2333 | return E_OUTOFMEMORY; |
2334 | |
2335 | EX_TRY |
2336 | { |
2337 | m_valueHome.GetValue(MemoryRange(m_pObjectCopy, m_size)); // throws |
2338 | } |
2339 | EX_CATCH_HRESULT(hr); |
2340 | IfFailRet(hr); |
2341 | |
2342 | // Compute offsets in bytes to the locals and to a string if this is a |
2343 | // string object. |
2344 | m_objectLocalVars = m_pObjectCopy + m_info.objOffsetToVars; |
2345 | |
2346 | if (m_info.objTypeData.elementType == ELEMENT_TYPE_STRING) |
2347 | m_stringBuffer = m_pObjectCopy + m_info.stringInfo.offsetToStringBase; |
2348 | |
2349 | return hr; |
2350 | } // CordbObjectValue::Init |
2351 | |
2352 | // CordbObjectValue::GetThreadOwningMonitorLock |
2353 | // If a managed thread owns the monitor lock on this object then *ppThread |
2354 | // will point to that thread and S_OK will be returned. The thread object is valid |
2355 | // until the thread exits. *pAcquisitionCount will indicate the number of times |
2356 | // this thread would need to release the lock before it returns to being |
2357 | // unowned. |
2358 | // If no managed thread owns the monitor lock on this object then *ppThread |
2359 | // and pAcquisitionCount will be unchanged and S_FALSE returned. |
2360 | // If ppThread or pAcquisitionCount is not a valid pointer the result is |
2361 | // undefined. |
2362 | // If any error occurs such that it cannot be determined which, if any, thread |
2363 | // owns the monitor lock on this object then a failing HRESULT will be returned |
2364 | HRESULT CordbObjectValue::GetThreadOwningMonitorLock(ICorDebugThread **ppThread, DWORD *pAcquisitionCount) |
2365 | { |
2366 | PUBLIC_API_ENTRY(this); |
2367 | FAIL_IF_NEUTERED(this); |
2368 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2369 | |
2370 | return CordbHeapValue3Impl::GetThreadOwningMonitorLock(GetProcess(), |
2371 | GetValueHome()->GetAddress(), |
2372 | ppThread, |
2373 | pAcquisitionCount); |
2374 | } |
2375 | |
2376 | // CordbObjectValue::GetMonitorEventWaitList |
2377 | // Provides an ordered list of threads which are queued on the event associated |
2378 | // with a monitor lock. The first thread in the list is the first thread which |
2379 | // will be released by the next call to Monitor.Pulse, the next thread in the list |
2380 | // will be released on the following call, and so on. |
2381 | // If this list is non-empty S_OK will be returned, if it is empty S_FALSE |
2382 | // will be returned (the enumeration is still valid, just empty). |
2383 | // In either case the enumeration interface is only usable for the duration |
2384 | // of the current synchronized state, however the threads interfaces dispensed |
2385 | // from it are valid until the thread exits. |
2386 | // If ppThread is not a valid pointer the result is undefined. |
2387 | // If any error occurs such that it cannot be determined which, if any, threads |
2388 | // are waiting for the monitor then a failing HRESULT will be returned |
2389 | HRESULT CordbObjectValue::GetMonitorEventWaitList(ICorDebugThreadEnum **ppThreadEnum) |
2390 | { |
2391 | PUBLIC_API_ENTRY(this); |
2392 | FAIL_IF_NEUTERED(this); |
2393 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2394 | |
2395 | return CordbHeapValue3Impl::GetMonitorEventWaitList(GetProcess(), |
2396 | GetValueHome()->GetAddress(), |
2397 | ppThreadEnum); |
2398 | } |
2399 | |
2400 | HRESULT CordbObjectValue::EnumerateExceptionCallStack(ICorDebugExceptionObjectCallStackEnum** ppCallStackEnum) |
2401 | { |
2402 | if (!ppCallStackEnum) |
2403 | return E_INVALIDARG; |
2404 | |
2405 | *ppCallStackEnum = NULL; |
2406 | |
2407 | HRESULT hr = S_OK; |
2408 | CorDebugExceptionObjectStackFrame* pStackFrames = NULL; |
2409 | |
2410 | PUBLIC_API_BEGIN(this); |
2411 | |
2412 | CORDB_ADDRESS objAddr = m_valueHome.GetAddress(); |
2413 | |
2414 | IDacDbiInterface* pDAC = GetProcess()->GetDAC(); |
2415 | VMPTR_Object vmObj = pDAC->GetObject(objAddr); |
2416 | |
2417 | DacDbiArrayList<DacExceptionCallStackData> dacStackFrames; |
2418 | |
2419 | pDAC->GetStackFramesFromException(vmObj, dacStackFrames); |
2420 | int stackFramesLength = dacStackFrames.Count(); |
2421 | |
2422 | if (stackFramesLength > 0) |
2423 | { |
2424 | pStackFrames = new CorDebugExceptionObjectStackFrame[stackFramesLength]; |
2425 | for (int index = 0; index < stackFramesLength; ++index) |
2426 | { |
2427 | DacExceptionCallStackData& currentDacFrame = dacStackFrames[index]; |
2428 | CorDebugExceptionObjectStackFrame& currentStackFrame = pStackFrames[index]; |
2429 | |
2430 | CordbAppDomain* pAppDomain = GetProcess()->LookupOrCreateAppDomain(currentDacFrame.vmAppDomain); |
2431 | CordbModule* pModule = pAppDomain->LookupOrCreateModule(currentDacFrame.vmDomainFile); |
2432 | |
2433 | hr = pModule->QueryInterface(IID_ICorDebugModule, reinterpret_cast<void**>(¤tStackFrame.pModule)); |
2434 | _ASSERTE(SUCCEEDED(hr)); |
2435 | |
2436 | currentStackFrame.ip = currentDacFrame.ip; |
2437 | currentStackFrame.methodDef = currentDacFrame.methodDef; |
2438 | currentStackFrame.isLastForeignExceptionFrame = currentDacFrame.isLastForeignExceptionFrame; |
2439 | } |
2440 | } |
2441 | |
2442 | CordbExceptionObjectCallStackEnumerator* callStackEnum = new CordbExceptionObjectCallStackEnumerator(GetProcess(), pStackFrames, stackFramesLength); |
2443 | GetProcess()->GetContinueNeuterList()->Add(GetProcess(), callStackEnum); |
2444 | |
2445 | hr = callStackEnum->QueryInterface(IID_ICorDebugExceptionObjectCallStackEnum, reinterpret_cast<void**>(ppCallStackEnum)); |
2446 | _ASSERTE(SUCCEEDED(hr)); |
2447 | |
2448 | PUBLIC_API_END(hr); |
2449 | |
2450 | if (pStackFrames) |
2451 | delete[] pStackFrames; |
2452 | |
2453 | return hr; |
2454 | } |
2455 | |
2456 | HRESULT CordbObjectValue::IsExceptionObject() |
2457 | { |
2458 | HRESULT hr = S_OK; |
2459 | |
2460 | if (m_info.objTypeData.elementType != ELEMENT_TYPE_CLASS) |
2461 | { |
2462 | hr = S_FALSE; |
2463 | } |
2464 | else |
2465 | { |
2466 | CORDB_ADDRESS objAddr = m_valueHome.GetAddress(); |
2467 | |
2468 | if (objAddr == NULL) |
2469 | { |
2470 | // object is a literal |
2471 | hr = S_FALSE; |
2472 | } |
2473 | else |
2474 | { |
2475 | IDacDbiInterface* pDAC = GetProcess()->GetDAC(); |
2476 | |
2477 | VMPTR_Object vmObj = pDAC->GetObject(objAddr); |
2478 | BOOL fIsException = pDAC->IsExceptionObject(vmObj); |
2479 | |
2480 | if (!fIsException) |
2481 | hr = S_FALSE; |
2482 | } |
2483 | } |
2484 | |
2485 | return hr; |
2486 | } |
2487 | |
2488 | HRESULT CordbObjectValue::IsRcw() |
2489 | { |
2490 | HRESULT hr = S_OK; |
2491 | |
2492 | if (m_info.objTypeData.elementType != ELEMENT_TYPE_CLASS) |
2493 | { |
2494 | hr = S_FALSE; |
2495 | } |
2496 | else |
2497 | { |
2498 | CORDB_ADDRESS objAddr = m_valueHome.GetAddress(); |
2499 | |
2500 | if (objAddr == NULL) |
2501 | { |
2502 | // object is a literal |
2503 | hr = S_FALSE; |
2504 | } |
2505 | else |
2506 | { |
2507 | IDacDbiInterface* pDAC = GetProcess()->GetDAC(); |
2508 | |
2509 | VMPTR_Object vmObj = pDAC->GetObject(objAddr); |
2510 | BOOL fIsRcw = pDAC->IsRcw(vmObj); |
2511 | |
2512 | if (!fIsRcw) |
2513 | hr = S_FALSE; |
2514 | } |
2515 | } |
2516 | |
2517 | return hr; |
2518 | } |
2519 | |
2520 | HRESULT CordbObjectValue::GetCachedInterfaceTypes( |
2521 | BOOL bIInspectableOnly, |
2522 | ICorDebugTypeEnum * * ppInterfacesEnum) |
2523 | { |
2524 | #if !defined(FEATURE_COMINTEROP) |
2525 | |
2526 | return E_NOTIMPL; |
2527 | |
2528 | #else |
2529 | |
2530 | HRESULT hr = S_OK; |
2531 | |
2532 | PUBLIC_API_ENTRY(this); |
2533 | FAIL_IF_NEUTERED(this); |
2534 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2535 | VALIDATE_POINTER_TO_OBJECT(ppInterfacesEnum, ICorDebugTypeEnum **); |
2536 | |
2537 | _ASSERTE(m_fIsRcw); |
2538 | |
2539 | EX_TRY |
2540 | { |
2541 | *ppInterfacesEnum = NULL; |
2542 | |
2543 | NewArrayHolder<CordbType*> pItfs(NULL); |
2544 | |
2545 | // retrieve interface types |
2546 | DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> dacInterfaces; |
2547 | |
2548 | IDacDbiInterface* pDAC = GetProcess()->GetDAC(); |
2549 | |
2550 | CORDB_ADDRESS objAddr = m_valueHome.GetAddress(); |
2551 | VMPTR_Object vmObj = pDAC->GetObject(objAddr); |
2552 | |
2553 | // retrieve type info from LS |
2554 | pDAC->GetRcwCachedInterfaceTypes(vmObj, m_appdomain->GetADToken(), |
2555 | bIInspectableOnly, &dacInterfaces); |
2556 | |
2557 | // synthesize CordbType instances |
2558 | int cItfs = dacInterfaces.Count(); |
2559 | if (cItfs > 0) |
2560 | { |
2561 | pItfs = new CordbType*[cItfs]; |
2562 | for (int n = 0; n < cItfs; ++n) |
2563 | { |
2564 | hr = CordbType::TypeDataToType(m_appdomain, |
2565 | &(dacInterfaces[n]), |
2566 | &pItfs[n]); |
2567 | } |
2568 | } |
2569 | |
2570 | // build a type enumerator |
2571 | CordbTypeEnum* pTypeEnum = CordbTypeEnum::Build(m_appdomain, GetProcess()->GetContinueNeuterList(), cItfs, pItfs); |
2572 | if ( pTypeEnum == NULL ) |
2573 | { |
2574 | IfFailThrow(E_OUTOFMEMORY); |
2575 | } |
2576 | |
2577 | (*ppInterfacesEnum) = static_cast<ICorDebugTypeEnum*> (pTypeEnum); |
2578 | pTypeEnum->ExternalAddRef(); |
2579 | |
2580 | } |
2581 | EX_CATCH_HRESULT(hr); |
2582 | |
2583 | return hr; |
2584 | |
2585 | #endif |
2586 | } |
2587 | |
2588 | HRESULT CordbObjectValue::GetCachedInterfacePointers( |
2589 | BOOL bIInspectableOnly, |
2590 | ULONG32 celt, |
2591 | ULONG32 *pcEltFetched, |
2592 | CORDB_ADDRESS * ptrs) |
2593 | { |
2594 | #if !defined(FEATURE_COMINTEROP) |
2595 | |
2596 | return E_NOTIMPL; |
2597 | |
2598 | #else |
2599 | |
2600 | PUBLIC_API_ENTRY(this); |
2601 | FAIL_IF_NEUTERED(this); |
2602 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2603 | _ASSERTE(m_fIsRcw); |
2604 | |
2605 | if (pcEltFetched == NULL && (ptrs == NULL || celt == 0)) |
2606 | return E_INVALIDARG; |
2607 | |
2608 | HRESULT hr = S_OK; |
2609 | ULONG32 cItfs = 0; |
2610 | |
2611 | // retrieve interface types |
2612 | |
2613 | CORDB_ADDRESS objAddr = m_valueHome.GetAddress(); |
2614 | |
2615 | DacDbiArrayList<CORDB_ADDRESS> dacItfPtrs; |
2616 | EX_TRY |
2617 | { |
2618 | IDacDbiInterface* pDAC = GetProcess()->GetDAC(); |
2619 | VMPTR_Object vmObj = pDAC->GetObject(objAddr); |
2620 | |
2621 | // retrieve type info from LS |
2622 | pDAC->GetRcwCachedInterfacePointers(vmObj, bIInspectableOnly, &dacItfPtrs); |
2623 | } |
2624 | EX_CATCH_HRESULT(hr); |
2625 | IfFailRet(hr); |
2626 | |
2627 | // synthesize CordbType instances |
2628 | cItfs = (ULONG32)dacItfPtrs.Count(); |
2629 | |
2630 | if (pcEltFetched != NULL && ptrs == NULL) |
2631 | { |
2632 | *pcEltFetched = cItfs; |
2633 | return S_OK; |
2634 | } |
2635 | |
2636 | if (pcEltFetched != NULL) |
2637 | { |
2638 | *pcEltFetched = (cItfs <= celt ? cItfs : celt); |
2639 | } |
2640 | |
2641 | if (ptrs != NULL && *pcEltFetched > 0) |
2642 | { |
2643 | for (ULONG32 i = 0; i < *pcEltFetched; ++i) |
2644 | ptrs[i] = dacItfPtrs[i]; |
2645 | } |
2646 | |
2647 | return (*pcEltFetched == celt ? S_OK : S_FALSE); |
2648 | |
2649 | #endif |
2650 | } |
2651 | |
2652 | |
2653 | /* ------------------------------------------------------------------------- * |
2654 | * Value Class Object |
2655 | * ------------------------------------------------------------------------- */ |
2656 | |
2657 | // constructor |
2658 | // Arguments: |
2659 | // input: pAppdomain - app domain to which the value belongs |
2660 | // pType - type information for the value |
2661 | // remoteValue - buffer describing the target location of the value |
2662 | // ppRemoteRegAddr - describes the register information if the value resides in a register |
2663 | // Note: May throw E_OUTOFMEMORY |
2664 | CordbVCObjectValue::CordbVCObjectValue(CordbAppDomain * pAppdomain, |
2665 | CordbType * pType, |
2666 | TargetBuffer remoteValue, |
2667 | EnregisteredValueHomeHolder * ppRemoteRegAddr) |
2668 | |
2669 | // We'd like to neuter this on Continue (not just exit), but it may be a breaking change, |
2670 | // especially for ValueTypes that don't have any GC refs in them. |
2671 | : CordbValue(pAppdomain, |
2672 | pType, |
2673 | remoteValue.pAddress, |
2674 | false, |
2675 | pAppdomain->GetSweepableExitNeuterList()), |
2676 | m_pObjectCopy(NULL), |
2677 | m_pValueHome(NULL) |
2678 | { |
2679 | // instantiate the value home |
2680 | NewHolder<ValueHome> pHome(NULL); |
2681 | |
2682 | if (remoteValue.IsEmpty()) |
2683 | { |
2684 | pHome = (new RegisterValueHome(pAppdomain->GetProcess(), ppRemoteRegAddr)); |
2685 | } |
2686 | else |
2687 | { |
2688 | pHome = (new VCRemoteValueHome(pAppdomain->GetProcess(), remoteValue)); |
2689 | } |
2690 | m_pValueHome = pHome.GetValue(); // throws |
2691 | pHome.SuppressRelease(); |
2692 | } // CordbVCObjectValue::CordbVCObjectValue |
2693 | |
2694 | // destructor |
2695 | CordbVCObjectValue::~CordbVCObjectValue() |
2696 | { |
2697 | DTOR_ENTRY(this); |
2698 | |
2699 | _ASSERTE(IsNeutered()); |
2700 | |
2701 | // Destroy the copy of the object. |
2702 | if (m_pObjectCopy != NULL) |
2703 | { |
2704 | delete [] m_pObjectCopy; |
2705 | m_pObjectCopy = NULL; |
2706 | } |
2707 | |
2708 | // destroy the value home |
2709 | if (m_pValueHome != NULL) |
2710 | { |
2711 | delete m_pValueHome; |
2712 | m_pValueHome = NULL; |
2713 | } |
2714 | } // CordbVCObjectValue::~CordbVCObjectValue |
2715 | |
2716 | HRESULT CordbVCObjectValue::QueryInterface(REFIID id, void **pInterface) |
2717 | { |
2718 | if (id == IID_ICorDebugValue) |
2719 | { |
2720 | *pInterface = static_cast<ICorDebugValue*>(static_cast<ICorDebugObjectValue*>(this)); |
2721 | } |
2722 | else if (id == IID_ICorDebugValue2) |
2723 | { |
2724 | *pInterface = static_cast<ICorDebugValue2*>(this); |
2725 | } |
2726 | else if (id == IID_ICorDebugValue3) |
2727 | { |
2728 | *pInterface = static_cast<ICorDebugValue3*>(this); |
2729 | } |
2730 | else if (id == IID_ICorDebugObjectValue) |
2731 | { |
2732 | *pInterface = static_cast<ICorDebugObjectValue*>(this); |
2733 | } |
2734 | else if (id == IID_ICorDebugObjectValue2) |
2735 | |
2736 | { |
2737 | *pInterface = static_cast<ICorDebugObjectValue2*>(this); |
2738 | } |
2739 | else if (id == IID_ICorDebugGenericValue) |
2740 | { |
2741 | *pInterface = static_cast<ICorDebugGenericValue*>(this); |
2742 | } |
2743 | else if (id == IID_IUnknown) |
2744 | { |
2745 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugObjectValue*>(this)); |
2746 | } |
2747 | else |
2748 | { |
2749 | *pInterface = NULL; |
2750 | return E_NOINTERFACE; |
2751 | } |
2752 | |
2753 | ExternalAddRef(); |
2754 | return S_OK; |
2755 | } // CordbVCObjectValue::QueryInterface |
2756 | |
2757 | // returns the basic type of the ICDValue |
2758 | // Arguments: |
2759 | // output: pType - the type of the ICDValue (always E_T_VALUETYPE) |
2760 | // ReturnValue: S_OK on success or E_INVALIDARG if pType is NULL |
2761 | HRESULT CordbVCObjectValue::GetType(CorElementType *pType) |
2762 | { |
2763 | PUBLIC_REENTRANT_API_ENTRY(this); |
2764 | VALIDATE_POINTER_TO_OBJECT(pType, CorElementType *); |
2765 | |
2766 | *pType = ELEMENT_TYPE_VALUETYPE; |
2767 | return S_OK; |
2768 | } // CordbVCObjectValue::GetType |
2769 | |
2770 | // public API to get the CordbClass field |
2771 | // Arguments: |
2772 | // output: ppClass - holds a pointer to the ICDClass instance belonging to this |
2773 | // Return Value: S_OK on success, CORDBG_E_OBJECT_NEUTERED or synchronization errors on failure |
2774 | HRESULT CordbVCObjectValue::GetClass(ICorDebugClass **ppClass) |
2775 | { |
2776 | PUBLIC_REENTRANT_API_ENTRY(this); |
2777 | FAIL_IF_NEUTERED(this); |
2778 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2779 | *ppClass = (ICorDebugClass*) GetClass(); |
2780 | |
2781 | if (*ppClass != NULL) |
2782 | (*ppClass)->AddRef(); |
2783 | |
2784 | return S_OK; |
2785 | } // CordbVCObjectValue::GetClass |
2786 | |
2787 | // internal method to get the CordbClass field |
2788 | // Arguments: none |
2789 | // ReturnValue: the instance of CordbClass belonging to this VC object |
2790 | CordbClass *CordbVCObjectValue::GetClass() |
2791 | { |
2792 | CordbClass *tycon; |
2793 | Instantiation inst; |
2794 | m_type->DestConstructedType(&tycon, &inst); |
2795 | return tycon; |
2796 | } // CordbVCObjectValue::GetClass |
2797 | |
2798 | //----------------------------------------------------------------------------- |
2799 | // |
2800 | // Finds the given field of the given type in the object and returns an ICDValue for it. |
2801 | // |
2802 | // Arguments: |
2803 | // pType - The type of the field |
2804 | // fieldDef - The field's metadata def. |
2805 | // ppValue - OUT: the ICDValue for the field. |
2806 | // |
2807 | // Returns: |
2808 | // S_OK on success, CORDBG_E_OBJECT_NEUTERED, E_INVALIDARG, CORDBG_E_ENC_HANGING_FIELD, or various other |
2809 | // failure codes |
2810 | HRESULT CordbVCObjectValue::GetFieldValueForType(ICorDebugType * pType, |
2811 | mdFieldDef fieldDef, |
2812 | ICorDebugValue ** ppValue) |
2813 | { |
2814 | PUBLIC_REENTRANT_API_ENTRY(this); |
2815 | FAIL_IF_NEUTERED(this); |
2816 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2817 | |
2818 | HRESULT hr = S_OK; |
2819 | EX_TRY |
2820 | { |
2821 | // Validate the token. |
2822 | if ((m_type->m_pClass == NULL) || !m_type->m_pClass->GetModule()->GetMetaDataImporter()->IsValidToken(fieldDef)) |
2823 | { |
2824 | ThrowHR(E_INVALIDARG); |
2825 | } |
2826 | |
2827 | |
2828 | CordbType * pCordbType; |
2829 | |
2830 | // |
2831 | // <TODO>@todo: need to ensure that pClass is really on the class |
2832 | // hierarchy of m_class!!!</TODO> |
2833 | // |
2834 | if (pType == NULL) |
2835 | { |
2836 | pCordbType = m_type; |
2837 | } |
2838 | else |
2839 | { |
2840 | pCordbType = static_cast<CordbType *> (pType); |
2841 | } |
2842 | |
2843 | FieldData * pFieldData; |
2844 | |
2845 | #ifdef _DEBUG |
2846 | pFieldData = NULL; |
2847 | #endif |
2848 | |
2849 | hr = pCordbType->GetFieldInfo(fieldDef, &pFieldData); |
2850 | _ASSERTE(hr != CORDBG_E_ENC_HANGING_FIELD); |
2851 | |
2852 | // If we get back CORDBG_E_ENC_HANGING_FIELD we'll just fail - |
2853 | // value classes should not be able to add fields once they're loaded, |
2854 | // since the new fields _can't_ be contiguous with the old fields, |
2855 | // and having all the fields contiguous is kinda the point of a V.C. |
2856 | IfFailThrow(hr); |
2857 | |
2858 | _ASSERTE(pFieldData != NULL); |
2859 | |
2860 | CordbModule * pModule = pCordbType->m_pClass->GetModule(); |
2861 | |
2862 | SigParser sigParser; |
2863 | IfFailThrow(pFieldData->GetFieldSignature(pModule, &sigParser)); |
2864 | |
2865 | // <TODO> |
2866 | // How can I assert that I have exactly one field? |
2867 | // </TODO> |
2868 | CordbType * pFieldType; |
2869 | |
2870 | IfFailThrow(CordbType::SigToType(pModule, &sigParser, &(pCordbType->m_inst), &pFieldType)); |
2871 | |
2872 | _ASSERTE(pFieldData->OkToGetOrSetInstanceOffset()); |
2873 | // Compute the address of the field contents in our local object cache |
2874 | SIZE_T fieldOffset = pFieldData->GetInstanceOffset(); |
2875 | ULONG32 size = GetSizeForType(pFieldType, kUnboxed); |
2876 | |
2877 | // verify that the field starts before the end of m_pObjectCopy |
2878 | _ASSERTE(fieldOffset < m_size); |
2879 | _ASSERTE(fieldOffset + size <= m_size); |
2880 | |
2881 | m_pValueHome->CreateInternalValue(pFieldType, |
2882 | fieldOffset, |
2883 | m_pObjectCopy + fieldOffset, |
2884 | size, |
2885 | ppValue); // throws |
2886 | |
2887 | } |
2888 | EX_CATCH_HRESULT(hr); |
2889 | return hr; |
2890 | } // CordbVCObjectValue::GetFieldValueForType |
2891 | |
2892 | // gets an ICDValue to represent a field of the VC object |
2893 | // Arguments: |
2894 | // input: pClass - the class information for this object (needed to get the parent class information) |
2895 | // fieldDef - field token for the desired field |
2896 | // output: ppValue - on success, the ICDValue representing the desired field |
2897 | // Return Value: S_OK on success, CORDBG_E_OBJECT_NEUTERED, CORDBG_E_CLASS_NOT_LOADED, E_INVALIDARG, OOM, |
2898 | // CORDBG_E_ENC_HANGING_FIELD, or various other failure codes |
2899 | HRESULT CordbVCObjectValue::GetFieldValue(ICorDebugClass *pClass, |
2900 | mdFieldDef fieldDef, |
2901 | ICorDebugValue **ppValue) |
2902 | { |
2903 | PUBLIC_REENTRANT_API_ENTRY(this); |
2904 | FAIL_IF_NEUTERED(this); |
2905 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2906 | VALIDATE_POINTER_TO_OBJECT(pClass, ICorDebugClass *); |
2907 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
2908 | |
2909 | HRESULT hr; |
2910 | _ASSERTE(m_type); |
2911 | |
2912 | if (m_type->m_elementType != ELEMENT_TYPE_CLASS && |
2913 | m_type->m_elementType != ELEMENT_TYPE_VALUETYPE) |
2914 | { |
2915 | return E_INVALIDARG; |
2916 | } |
2917 | |
2918 | RSExtSmartPtr<CordbType> relevantType; |
2919 | |
2920 | if (FAILED (hr= m_type->GetParentType((CordbClass *) pClass, &relevantType))) |
2921 | { |
2922 | return hr; |
2923 | } |
2924 | // Upon exit relevantType will either be the appropriate type for the |
2925 | // class we're looking for. |
2926 | |
2927 | hr = GetFieldValueForType(relevantType, fieldDef, ppValue); |
2928 | // GetParentType ands one reference to relevantType, holder dtor releases that. |
2929 | return hr; |
2930 | |
2931 | } // CordbVCObjectValue::GetFieldValue |
2932 | |
2933 | // get a copy of the VC object |
2934 | // Arguments: |
2935 | // output: pTo - a caller-allocated buffer to hold the copy |
2936 | // Return Value: S_OK on success, CORDBG_E_OBJECT_NEUTERED on failure |
2937 | // Note: The caller must ensure the buffer is large enough to hold the value (by a previous call to GetSize) |
2938 | // and is responsible for allocation and deallocation. |
2939 | HRESULT CordbVCObjectValue::GetValue(void *pTo) |
2940 | { |
2941 | VALIDATE_POINTER_TO_OBJECT_ARRAY(pTo, BYTE, m_size, false, true); |
2942 | FAIL_IF_NEUTERED(this); |
2943 | |
2944 | // Copy out the value, which is the whole object. |
2945 | memcpy(pTo, m_pObjectCopy, m_size); |
2946 | |
2947 | return S_OK; |
2948 | } // CordbVCObjectValue::GetValue |
2949 | |
2950 | // set the value of a VC object |
2951 | // Arguments: |
2952 | // input: pSrc - buffer containing the new value. Allocated and managed by the caller. |
2953 | // Return Value: S_OK on success, CORDBG_E_OBJECT_NEUTERED, synchronization errors, E_INVALIDARG, write |
2954 | // process memory errors, CORDBG_E_CLASS_NOT_LOADED or OOM on failure |
2955 | HRESULT CordbVCObjectValue::SetValue(void * pSrc) |
2956 | { |
2957 | PUBLIC_REENTRANT_API_ENTRY(this); |
2958 | FAIL_IF_NEUTERED(this); |
2959 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2960 | |
2961 | HRESULT hr = S_OK; |
2962 | |
2963 | VALIDATE_POINTER_TO_OBJECT_ARRAY(pSrc, BYTE, m_size, true, false); |
2964 | |
2965 | // Can't change literals... |
2966 | if (m_isLiteral) |
2967 | return E_INVALIDARG; |
2968 | |
2969 | if (m_type) |
2970 | { |
2971 | IfFailRet(m_type->Init(FALSE)); |
2972 | } |
2973 | |
2974 | EX_TRY |
2975 | { |
2976 | m_pValueHome->SetValue(MemoryRange(pSrc, m_size), m_type); // throws |
2977 | } |
2978 | EX_CATCH_HRESULT(hr); |
2979 | if (SUCCEEDED(hr)) |
2980 | { |
2981 | // That worked, so update the copy of the value we have over here. |
2982 | memcpy(m_pObjectCopy, pSrc, m_size); |
2983 | } |
2984 | |
2985 | return hr; |
2986 | } // CordbVCObjectValue::SetValue |
2987 | |
2988 | HRESULT CordbVCObjectValue::GetVirtualMethod(mdMemberRef memberRef, |
2989 | ICorDebugFunction **ppFunction) |
2990 | { |
2991 | return E_NOTIMPL; |
2992 | } |
2993 | |
2994 | HRESULT CordbVCObjectValue::GetVirtualMethodAndType(mdMemberRef memberRef, |
2995 | ICorDebugFunction **ppFunction, |
2996 | ICorDebugType **ppType) |
2997 | { |
2998 | return E_NOTIMPL; |
2999 | } |
3000 | |
3001 | HRESULT CordbVCObjectValue::GetContext(ICorDebugContext **ppContext) |
3002 | { |
3003 | return E_NOTIMPL; |
3004 | } |
3005 | |
3006 | // self-identifier--always returns true as long as pbIsValueClass is non-Null |
3007 | HRESULT CordbVCObjectValue::IsValueClass(BOOL *pbIsValueClass) |
3008 | { |
3009 | if (pbIsValueClass) |
3010 | *pbIsValueClass = TRUE; |
3011 | |
3012 | return S_OK; |
3013 | } // CordbVCObjectValue::IsValueClass |
3014 | |
3015 | HRESULT CordbVCObjectValue::GetManagedCopy(IUnknown **ppObject) |
3016 | { |
3017 | // This function is deprecated |
3018 | return E_NOTIMPL; |
3019 | } |
3020 | |
3021 | HRESULT CordbVCObjectValue::SetFromManagedCopy(IUnknown *pObject) |
3022 | { |
3023 | // This function is deprecated |
3024 | return E_NOTIMPL; |
3025 | } |
3026 | |
3027 | // |
3028 | // CordbVCObjectValue::Init |
3029 | // |
3030 | // Description |
3031 | // Initializes the Right-Side's representation of a Value Class object. |
3032 | // Parameters |
3033 | // input: localValue - buffer containing the value if this instance of CordbObjectValue |
3034 | // was a field or array element of an existing value, otherwise this |
3035 | // will have a start address equal to NULL |
3036 | // Returns |
3037 | // HRESULT |
3038 | // S_OK if the function completed normally |
3039 | // failing HR otherwise |
3040 | // Exceptions |
3041 | // None |
3042 | // |
3043 | HRESULT CordbVCObjectValue::Init(MemoryRange localValue) |
3044 | { |
3045 | HRESULT hr = S_OK; |
3046 | |
3047 | INTERNAL_SYNC_API_ENTRY(this->GetProcess()); // |
3048 | |
3049 | // Get the object size from the class |
3050 | ULONG32 size; |
3051 | IfFailRet( m_type->GetUnboxedObjectSize(&size) ); |
3052 | m_size = size; |
3053 | |
3054 | // Copy the entire object over to this process. |
3055 | m_pObjectCopy = new (nothrow) BYTE[m_size]; |
3056 | |
3057 | if (m_pObjectCopy == NULL) |
3058 | { |
3059 | return E_OUTOFMEMORY; |
3060 | } |
3061 | |
3062 | if (localValue.StartAddress() != NULL) |
3063 | { |
3064 | // The data is already in the local address space. Go ahead and copy it |
3065 | // from there. |
3066 | // localValue.StartAddress points to: |
3067 | // 1. A field from the local cached copy belonging to an instance of CordbVCObjectValue (different |
3068 | // instance from "this") or CordbObjectValue |
3069 | // 2. An element in the locally cached subrange of an array belonging to an instance of CordbArrayValue |
3070 | // 3. The address of a particular register in the register display of an instance of CordbNativeFrame |
3071 | // for an enregistered value type. In this case, it's possible that the size of the value is |
3072 | // smaller than the size of a full register. For that reason, we can't just use localValue.Size() |
3073 | // as the number of bytes to copy, because only enough space for the value has been allocated. |
3074 | _ASSERTE(localValue.Size() >= m_size); |
3075 | localCopy(m_pObjectCopy, MemoryRange(localValue.StartAddress(), m_size)); |
3076 | return S_OK; |
3077 | } |
3078 | |
3079 | EX_TRY |
3080 | { |
3081 | m_pValueHome->GetValue(MemoryRange(m_pObjectCopy, m_size)); // throws |
3082 | } |
3083 | EX_CATCH_HRESULT(hr); |
3084 | return hr; |
3085 | } // CordbVCObjectValue::Init |
3086 | |
3087 | /* ------------------------------------------------------------------------- * |
3088 | * Box Value class |
3089 | * ------------------------------------------------------------------------- */ |
3090 | |
3091 | // constructor |
3092 | // Arguments: |
3093 | // input: appdomain - app domain to which the value belongs |
3094 | // type - type information for the boxed value |
3095 | // remoteValue - buffer describing the remote location of the value |
3096 | // size - size of the value |
3097 | // offsetToVars - offset from the beginning of the value to the first field of the value |
3098 | CordbBoxValue::CordbBoxValue(CordbAppDomain *appdomain, |
3099 | CordbType *type, |
3100 | TargetBuffer remoteValue, |
3101 | ULONG32 size, |
3102 | SIZE_T offsetToVars) |
3103 | : CordbValue(appdomain, type, remoteValue.pAddress, false, appdomain->GetProcess()->GetContinueNeuterList()), |
3104 | m_offsetToVars(offsetToVars), |
3105 | m_valueHome(appdomain->GetProcess(), remoteValue) |
3106 | { |
3107 | m_size = size; |
3108 | } // CordbBoxValue::CordbBoxValue |
3109 | |
3110 | // destructor |
3111 | CordbBoxValue::~CordbBoxValue() |
3112 | { |
3113 | DTOR_ENTRY(this); |
3114 | _ASSERTE(IsNeutered()); |
3115 | } // CordbBoxValue::~CordbBoxValue |
3116 | |
3117 | HRESULT CordbBoxValue::QueryInterface(REFIID id, void **pInterface) |
3118 | { |
3119 | if (id == IID_ICorDebugValue) |
3120 | { |
3121 | *pInterface = static_cast<ICorDebugValue*>(static_cast<ICorDebugBoxValue*>(this)); |
3122 | } |
3123 | else if (id == IID_ICorDebugValue2) |
3124 | { |
3125 | *pInterface = static_cast<ICorDebugValue2*>(this); |
3126 | } |
3127 | else if (id == IID_ICorDebugValue3) |
3128 | { |
3129 | *pInterface = static_cast<ICorDebugValue3*>(this); |
3130 | } |
3131 | else if (id == IID_ICorDebugBoxValue) |
3132 | { |
3133 | *pInterface = static_cast<ICorDebugBoxValue*>(this); |
3134 | } |
3135 | else if (id == IID_ICorDebugGenericValue) |
3136 | { |
3137 | *pInterface = static_cast<ICorDebugGenericValue*>(this); |
3138 | } |
3139 | else if (id == IID_ICorDebugHeapValue) |
3140 | { |
3141 | *pInterface = static_cast<ICorDebugHeapValue*>(this); |
3142 | } |
3143 | else if (id == IID_ICorDebugHeapValue2) |
3144 | { |
3145 | *pInterface = static_cast<ICorDebugHeapValue2*>(this); |
3146 | } |
3147 | else if (id == IID_ICorDebugHeapValue3) |
3148 | { |
3149 | *pInterface = static_cast<ICorDebugHeapValue3*>(this); |
3150 | } |
3151 | else if (id == IID_IUnknown) |
3152 | { |
3153 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugBoxValue*>(this)); |
3154 | } |
3155 | else |
3156 | { |
3157 | *pInterface = NULL; |
3158 | return E_NOINTERFACE; |
3159 | } |
3160 | |
3161 | ExternalAddRef(); |
3162 | return S_OK; |
3163 | } // CordbBoxValue::QueryInterface |
3164 | |
3165 | // returns the basic type of the ICDValue |
3166 | // Arguments: |
3167 | // output: pType - the type of the ICDValue (always E_T_CLASS) |
3168 | // ReturnValue: S_OK on success or E_INVALIDARG if pType is NULL |
3169 | HRESULT CordbBoxValue::GetType(CorElementType *pType) |
3170 | { |
3171 | VALIDATE_POINTER_TO_OBJECT(pType, CorElementType *); |
3172 | |
3173 | *pType = ELEMENT_TYPE_CLASS; |
3174 | |
3175 | return (S_OK); |
3176 | } // CordbBoxValue::GetType |
3177 | |
3178 | HRESULT CordbBoxValue::IsValid(BOOL *pbValid) |
3179 | { |
3180 | VALIDATE_POINTER_TO_OBJECT(pbValid, BOOL *); |
3181 | |
3182 | // <TODO>@todo: implement tracking of objects across collections.</TODO> |
3183 | |
3184 | return E_NOTIMPL; |
3185 | } |
3186 | |
3187 | HRESULT CordbBoxValue::CreateRelocBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint) |
3188 | { |
3189 | VALIDATE_POINTER_TO_OBJECT(ppBreakpoint, ICorDebugValueBreakpoint **); |
3190 | |
3191 | return E_NOTIMPL; |
3192 | } |
3193 | |
3194 | // Creates a handle of the given type for this heap value. |
3195 | // Not Implemented In-Proc. |
3196 | // Create a handle for a heap object. |
3197 | // @todo: How to prevent this being called by non-heap object? |
3198 | // Arguments: |
3199 | // input: handleType - type of the handle to be created |
3200 | // output: ppHandle - on success, the newly created handle |
3201 | // Return Value: S_OK on success or E_INVALIDARG, E_OUTOFMEMORY, or CORDB_E_HELPER_MAY_DEADLOCK |
3202 | HRESULT CordbBoxValue::CreateHandle( |
3203 | CorDebugHandleType handleType, |
3204 | ICorDebugHandleValue ** ppHandle) |
3205 | { |
3206 | PUBLIC_API_ENTRY(this); |
3207 | FAIL_IF_NEUTERED(this); |
3208 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
3209 | |
3210 | return CordbValue::InternalCreateHandle(handleType, ppHandle); |
3211 | } // CordbBoxValue::CreateHandle |
3212 | |
3213 | HRESULT CordbBoxValue::GetValue(void *pTo) |
3214 | { |
3215 | // Can't get a whole copy of a box. |
3216 | return E_INVALIDARG; |
3217 | } |
3218 | |
3219 | HRESULT CordbBoxValue::SetValue(void *pFrom) |
3220 | { |
3221 | // You're not allowed to set a box value. |
3222 | return E_INVALIDARG; |
3223 | } |
3224 | |
3225 | // gets the unboxed value from this boxed value |
3226 | // Arguments: |
3227 | // output: ppObject - pointer to an instance of ICDValue representing the unboxed value, unless ppObject |
3228 | // is NULL |
3229 | // Return Value: S_OK on success or a variety of possible failures: OOM, E_FAIL, errors from |
3230 | // ReadProcessMemory. |
3231 | HRESULT CordbBoxValue::GetObject(ICorDebugObjectValue **ppObject) |
3232 | { |
3233 | PUBLIC_REENTRANT_API_ENTRY(this); |
3234 | VALIDATE_POINTER_TO_OBJECT(ppObject, ICorDebugObjectValue **); |
3235 | FAIL_IF_NEUTERED(this); |
3236 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
3237 | |
3238 | ULONG32 size; |
3239 | m_type->GetUnboxedObjectSize(&size); |
3240 | |
3241 | HRESULT hr = S_OK; |
3242 | EX_TRY |
3243 | { |
3244 | m_valueHome.CreateInternalValue(m_type, |
3245 | m_offsetToVars, |
3246 | NULL, |
3247 | size, |
3248 | reinterpret_cast<ICorDebugValue **>(ppObject)); // throws |
3249 | } |
3250 | EX_CATCH_HRESULT(hr); |
3251 | return hr; |
3252 | } // CordbBoxValue::GetObject |
3253 | |
3254 | // If a managed thread owns the monitor lock on this object then *ppThread |
3255 | // will point to that thread and S_OK will be returned. The thread object is valid |
3256 | // until the thread exits. *pAcquisitionCount will indicate the number of times |
3257 | // this thread would need to release the lock before it returns to being |
3258 | // unowned. |
3259 | // If no managed thread owns the monitor lock on this object then *ppThread |
3260 | // and pAcquisitionCount will be unchanged and S_FALSE returned. |
3261 | // If ppThread or pAcquisitionCount is not a valid pointer the result is |
3262 | // undefined. |
3263 | // If any error occurs such that it cannot be determined which, if any, thread |
3264 | // owns the monitor lock on this object then a failing HRESULT will be returned |
3265 | HRESULT CordbBoxValue::GetThreadOwningMonitorLock(ICorDebugThread **ppThread, DWORD *pAcquisitionCount) |
3266 | { |
3267 | PUBLIC_API_ENTRY(this); |
3268 | FAIL_IF_NEUTERED(this); |
3269 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
3270 | |
3271 | return CordbHeapValue3Impl::GetThreadOwningMonitorLock(GetProcess(), |
3272 | GetValueHome()->GetAddress(), |
3273 | ppThread, |
3274 | pAcquisitionCount); |
3275 | } |
3276 | |
3277 | // Provides an ordered list of threads which are queued on the event associated |
3278 | // with a monitor lock. The first thread in the list is the first thread which |
3279 | // will be released by the next call to Monitor.Pulse, the next thread in the list |
3280 | // will be released on the following call, and so on. |
3281 | // If this list is non-empty S_OK will be returned, if it is empty S_FALSE |
3282 | // will be returned (the enumeration is still valid, just empty). |
3283 | // In either case the enumeration interface is only usable for the duration |
3284 | // of the current synchronized state, however the threads interfaces dispensed |
3285 | // from it are valid until the thread exits. |
3286 | // If ppThread is not a valid pointer the result is undefined. |
3287 | // If any error occurs such that it cannot be determined which, if any, threads |
3288 | // are waiting for the monitor then a failing HRESULT will be returned |
3289 | HRESULT CordbBoxValue::GetMonitorEventWaitList(ICorDebugThreadEnum **ppThreadEnum) |
3290 | { |
3291 | PUBLIC_API_ENTRY(this); |
3292 | FAIL_IF_NEUTERED(this); |
3293 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
3294 | |
3295 | return CordbHeapValue3Impl::GetMonitorEventWaitList(GetProcess(), |
3296 | GetValueHome()->GetAddress(), |
3297 | ppThreadEnum); |
3298 | } |
3299 | |
3300 | |
3301 | /* ------------------------------------------------------------------------- * |
3302 | * Array Value class |
3303 | * ------------------------------------------------------------------------- */ |
3304 | |
3305 | // The size of the buffer we allocate to hold array elements. |
3306 | // Note that since we must be able to hold at least one element, we may |
3307 | // allocate larger than the cache size here. |
3308 | // Also, this cache doesn't include a small header used to store the rank vectors |
3309 | #ifdef _DEBUG |
3310 | // For debug, use a small size to cause more churn |
3311 | #define ARRAY_CACHE_SIZE (1000) |
3312 | #else |
3313 | // For release, guess 4 pages should be enough. Subtract some bytes to store |
3314 | // the header so that that doesn't push us onto another page. (We guess a reasonable |
3315 | // header size, but it's ok if it's larger). |
3316 | #define ARRAY_CACHE_SIZE (4 * 4096 - 24) |
3317 | #endif |
3318 | |
3319 | // constructor |
3320 | // Arguments: |
3321 | // input: |
3322 | // pAppDomain - app domain to which the value belongs |
3323 | // pType - type information for the value |
3324 | // pObjectInfo - array specific type information |
3325 | // remoteValue - buffer describing the remote location of the value |
3326 | CordbArrayValue::CordbArrayValue(CordbAppDomain * pAppdomain, |
3327 | CordbType * pType, |
3328 | DebuggerIPCE_ObjectData * pObjectInfo, |
3329 | TargetBuffer remoteValue) |
3330 | : CordbValue(pAppdomain, |
3331 | pType, |
3332 | remoteValue.pAddress, |
3333 | false, |
3334 | pAppdomain->GetProcess()->GetContinueNeuterList()), |
3335 | m_info(*pObjectInfo), |
3336 | m_pObjectCopy(NULL), |
3337 | m_valueHome(pAppdomain->GetProcess(), remoteValue) |
3338 | { |
3339 | m_size = m_info.objSize; |
3340 | pType->DestUnaryType(&m_elemtype); |
3341 | |
3342 | // Set range to illegal values to force a load on first access |
3343 | m_idxLower = m_idxUpper = (SIZE_T) -1; |
3344 | } // CordbArrayValue::CordbArrayValue |
3345 | |
3346 | // destructor |
3347 | CordbArrayValue::~CordbArrayValue() |
3348 | { |
3349 | DTOR_ENTRY(this); |
3350 | _ASSERTE(IsNeutered()); |
3351 | |
3352 | // Destroy the copy of the object. |
3353 | if (m_pObjectCopy != NULL) |
3354 | delete [] m_pObjectCopy; |
3355 | } // CordbArrayValue::~CordbArrayValue |
3356 | |
3357 | HRESULT CordbArrayValue::QueryInterface(REFIID id, void **pInterface) |
3358 | { |
3359 | if (id == IID_ICorDebugValue) |
3360 | { |
3361 | *pInterface = static_cast<ICorDebugValue*>(static_cast<ICorDebugArrayValue*>(this)); |
3362 | } |
3363 | else if (id == IID_ICorDebugValue2) |
3364 | { |
3365 | *pInterface = static_cast<ICorDebugValue2*>(this); |
3366 | } |
3367 | else if (id == IID_ICorDebugValue3) |
3368 | { |
3369 | *pInterface = static_cast<ICorDebugValue3*>(this); |
3370 | } |
3371 | else if (id == IID_ICorDebugArrayValue) |
3372 | { |
3373 | *pInterface = static_cast<ICorDebugArrayValue*>(this); |
3374 | } |
3375 | else if (id == IID_ICorDebugGenericValue) |
3376 | { |
3377 | *pInterface = static_cast<ICorDebugGenericValue*>(this); |
3378 | } |
3379 | else if (id == IID_ICorDebugHeapValue) |
3380 | { |
3381 | *pInterface = static_cast<ICorDebugHeapValue*>(this); |
3382 | } |
3383 | else if (id == IID_ICorDebugHeapValue2) |
3384 | { |
3385 | *pInterface = static_cast<ICorDebugHeapValue2*>(this); |
3386 | } |
3387 | else if (id == IID_ICorDebugHeapValue3) |
3388 | { |
3389 | *pInterface = static_cast<ICorDebugHeapValue3*>(this); |
3390 | } |
3391 | else if (id == IID_IUnknown) |
3392 | { |
3393 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugArrayValue*>(this)); |
3394 | } |
3395 | else |
3396 | { |
3397 | *pInterface = NULL; |
3398 | return E_NOINTERFACE; |
3399 | } |
3400 | |
3401 | ExternalAddRef(); |
3402 | return S_OK; |
3403 | } // CordbArrayValue::QueryInterface |
3404 | |
3405 | // gets the type of the array elements |
3406 | // Arguments: |
3407 | // output: pType - the element type unless pType is NULL |
3408 | // Return Value: S_OK on success or E_INVALIDARG if pType is null |
3409 | HRESULT CordbArrayValue::GetElementType(CorElementType *pType) |
3410 | { |
3411 | PUBLIC_REENTRANT_API_ENTRY(this); |
3412 | FAIL_IF_NEUTERED(this); |
3413 | VALIDATE_POINTER_TO_OBJECT(pType, CorElementType *); |
3414 | |
3415 | *pType = m_elemtype->m_elementType; |
3416 | return S_OK; |
3417 | } // CordbArrayValue::GetElementType |
3418 | |
3419 | |
3420 | // gets the rank of the array |
3421 | // Arguments: |
3422 | // output: pnRank - the rank of the array unless pnRank is null |
3423 | // Return Value: S_OK on success or E_INVALIDARG if pnRank is null |
3424 | HRESULT CordbArrayValue::GetRank(ULONG32 *pnRank) |
3425 | { |
3426 | PUBLIC_REENTRANT_API_ENTRY(this); |
3427 | FAIL_IF_NEUTERED(this); |
3428 | VALIDATE_POINTER_TO_OBJECT(pnRank, SIZE_T *); |
3429 | |
3430 | // Rank info is duplicated for sanity checking - double check it here. |
3431 | _ASSERTE(m_info.arrayInfo.rank == m_type->m_rank); |
3432 | *pnRank = m_type->m_rank; |
3433 | return S_OK; |
3434 | } // CordbArrayValue::GetRank |
3435 | |
3436 | // gets the number of elements in the array |
3437 | // Arguments: |
3438 | // output: pnCount - the number of dimensions for the array unless pnCount is null |
3439 | // Return Value: S_OK on success or E_INVALIDARG if pnCount is null |
3440 | HRESULT CordbArrayValue::GetCount(ULONG32 *pnCount) |
3441 | { |
3442 | PUBLIC_REENTRANT_API_ENTRY(this); |
3443 | FAIL_IF_NEUTERED(this); |
3444 | VALIDATE_POINTER_TO_OBJECT(pnCount, ULONG32 *); |
3445 | |
3446 | *pnCount = (ULONG32)m_info.arrayInfo.componentCount; |
3447 | return S_OK; |
3448 | } // CordbArrayValue::GetCount |
3449 | |
3450 | // get the size of each dimension of the array |
3451 | // Arguments: |
3452 | // input: cdim - the number of dimensions about which to get dimensions--this must be the same as the rank |
3453 | // output: dims - an array to hold the sizes of the dimensions of the array--this is allocated and |
3454 | // managed by the caller |
3455 | // Return Value: S_OK on success or E_INVALIDARG |
3456 | HRESULT CordbArrayValue::GetDimensions(ULONG32 cdim, ULONG32 dims[]) |
3457 | { |
3458 | PUBLIC_REENTRANT_API_ENTRY(this); |
3459 | FAIL_IF_NEUTERED(this); |
3460 | VALIDATE_POINTER_TO_OBJECT_ARRAY(dims, SIZE_T, cdim, true, true); |
3461 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
3462 | |
3463 | // Rank info is duplicated for sanity checking - double check it here. |
3464 | _ASSERTE(m_info.arrayInfo.rank == m_type->m_rank); |
3465 | if (cdim != m_type->m_rank) |
3466 | return E_INVALIDARG; |
3467 | |
3468 | // SDArrays don't have bounds info, so return the component count. |
3469 | if (cdim == 1) |
3470 | dims[0] = (ULONG32)m_info.arrayInfo.componentCount; |
3471 | else |
3472 | { |
3473 | _ASSERTE(m_info.arrayInfo.offsetToUpperBounds != 0); |
3474 | _ASSERTE(m_arrayUpperBase != NULL); |
3475 | |
3476 | // The upper bounds info in the array is the true size of each |
3477 | // dimension. |
3478 | for (unsigned int i = 0; i < cdim; i++) |
3479 | dims[i] = m_arrayUpperBase[i]; |
3480 | } |
3481 | |
3482 | return S_OK; |
3483 | } // CordbArrayValue::GetDimensions |
3484 | |
3485 | // |
3486 | // indicates whether the array has base indices |
3487 | // Arguments: |
3488 | // output: pbHasBaseIndices - true iff the array has more than one dimension and pbHasBaseIndices is not null |
3489 | // Return Value: S_OK on success or E_INVALIDARG if pbHasBaseIndices is null |
3490 | HRESULT CordbArrayValue::HasBaseIndicies(BOOL *pbHasBaseIndices) |
3491 | { |
3492 | PUBLIC_REENTRANT_API_ENTRY(this); |
3493 | FAIL_IF_NEUTERED(this); |
3494 | VALIDATE_POINTER_TO_OBJECT(pbHasBaseIndices, BOOL *); |
3495 | |
3496 | *pbHasBaseIndices = m_info.arrayInfo.offsetToLowerBounds != 0; |
3497 | return S_OK; |
3498 | } // CordbArrayValue::HasBaseIndicies |
3499 | |
3500 | // gets the base indices for a multidimensional array |
3501 | // Arguments: |
3502 | // input: cdim - the number of dimensions (this must be the same as the actual rank of the array) |
3503 | // indices - an array to hold the base indices for the array dimensions (allocated and managed |
3504 | // by the caller, it must have space for cdim elements) |
3505 | // Return Value: S_OK on success or E_INVALIDARG if cdim is not equal to the array rank or indices is null |
3506 | HRESULT CordbArrayValue::GetBaseIndicies(ULONG32 cdim, ULONG32 indices[]) |
3507 | { |
3508 | PUBLIC_REENTRANT_API_ENTRY(this); |
3509 | FAIL_IF_NEUTERED(this); |
3510 | VALIDATE_POINTER_TO_OBJECT_ARRAY(indices, SIZE_T, cdim, true, true); |
3511 | |
3512 | // Rank info is duplicated for sanity checking - double check it here. |
3513 | _ASSERTE(m_info.arrayInfo.rank == m_type->m_rank); |
3514 | if ((cdim != m_type->m_rank) || |
3515 | (m_info.arrayInfo.offsetToLowerBounds == 0)) |
3516 | return E_INVALIDARG; |
3517 | |
3518 | _ASSERTE(m_arrayLowerBase != NULL); |
3519 | |
3520 | for (unsigned int i = 0; i < cdim; i++) |
3521 | indices[i] = m_arrayLowerBase[i]; |
3522 | |
3523 | return S_OK; |
3524 | } // CordbArrayValue::GetBaseIndicies |
3525 | |
3526 | // Get an element at the position indicated by the values in indices (one index for each dimension) |
3527 | // Arguments: |
3528 | // input: cdim - the number of dimensions and thus the number of elements in indices. This must match |
3529 | // the actual rank of the array value. |
3530 | // indices - an array of indices to specify the position of the element. For example, to get a[2][1][0], |
3531 | // indices would contain 2, 1, and 0 in that order. |
3532 | // output: ppValue - an ICDValue representing the element, unless an error occurs |
3533 | // Return Value: S_OK on success or E_INVALIDARG if cdim != rank, indices is NULL or ppValue is NULL |
3534 | // or a variety of possible failures: OOM, E_FAIL, errors from |
3535 | // ReadProcessMemory. |
3536 | HRESULT CordbArrayValue::GetElement(ULONG32 cdim, |
3537 | ULONG32 indices[], |
3538 | ICorDebugValue **ppValue) |
3539 | { |
3540 | PUBLIC_REENTRANT_API_ENTRY(this); |
3541 | VALIDATE_POINTER_TO_OBJECT_ARRAY(indices, SIZE_T, cdim, true, true); |
3542 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
3543 | FAIL_IF_NEUTERED(this); |
3544 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
3545 | |
3546 | *ppValue = NULL; |
3547 | |
3548 | // Rank info is duplicated for sanity checking - double check it here. |
3549 | _ASSERTE(m_info.arrayInfo.rank == m_type->m_rank); |
3550 | if ((cdim != m_type->m_rank) || (indices == NULL)) |
3551 | return E_INVALIDARG; |
3552 | |
3553 | // If the array has lower bounds, adjust the indices. |
3554 | if (m_info.arrayInfo.offsetToLowerBounds != 0) |
3555 | { |
3556 | _ASSERTE(m_arrayLowerBase != NULL); |
3557 | |
3558 | for (unsigned int i = 0; i < cdim; i++) |
3559 | indices[i] -= m_arrayLowerBase[i]; |
3560 | } |
3561 | |
3562 | SIZE_T offset = 0; |
3563 | |
3564 | // SDArrays don't have upper bounds |
3565 | if (cdim == 1) |
3566 | { |
3567 | offset = indices[0]; |
3568 | |
3569 | // Bounds check |
3570 | if (offset >= m_info.arrayInfo.componentCount) |
3571 | return E_INVALIDARG; |
3572 | } |
3573 | else |
3574 | { |
3575 | _ASSERTE(m_info.arrayInfo.offsetToUpperBounds != 0); |
3576 | _ASSERTE(m_arrayUpperBase != NULL); |
3577 | |
3578 | // Calculate the offset in bytes for all dimensions. |
3579 | SIZE_T multiplier = 1; |
3580 | |
3581 | for (int i = cdim - 1; i >= 0; i--) |
3582 | { |
3583 | // Bounds check |
3584 | if (indices[i] >= m_arrayUpperBase[i]) |
3585 | return E_INVALIDARG; |
3586 | |
3587 | offset += indices[i] * multiplier; |
3588 | multiplier *= m_arrayUpperBase[i]; |
3589 | } |
3590 | |
3591 | _ASSERTE(offset < m_info.arrayInfo.componentCount); |
3592 | } |
3593 | |
3594 | return GetElementAtPosition((ULONG32)offset, ppValue); |
3595 | } // CordbArrayValue::GetElement |
3596 | |
3597 | // get an ICDValue to represent the element at a given position |
3598 | // Arguments: |
3599 | // input: nPosition - the offset from the beginning of the array to the element |
3600 | // output: ppValue - the ICDValue representing the array element on success |
3601 | // Return Value: S_OK on success, E_INVALIDARG or a variety of possible failures: OOM, E_FAIL, errors from |
3602 | // ReadProcessMemory. |
3603 | HRESULT CordbArrayValue::GetElementAtPosition(ULONG32 nPosition, |
3604 | ICorDebugValue **ppValue) |
3605 | { |
3606 | PUBLIC_REENTRANT_API_ENTRY(this); |
3607 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
3608 | FAIL_IF_NEUTERED(this); |
3609 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
3610 | |
3611 | if (nPosition >= m_info.arrayInfo.componentCount) |
3612 | { |
3613 | *ppValue = NULL; |
3614 | return E_INVALIDARG; |
3615 | } |
3616 | |
3617 | // Rank info is duplicated for sanity checking - double check it here. |
3618 | _ASSERTE(m_info.arrayInfo.rank == m_type->m_rank); |
3619 | |
3620 | // The header consists of two DWORDs for each dimension, representing the upper and lower bound for that dimension. A |
3621 | // vector of lower bounds comes first, followed by a vector of upper bounds. We want to copy a range of |
3622 | // elements into m_pObjectCopy following these vectors, so we need to compute the address where the |
3623 | // vectors end and the elements begin. |
3624 | const int = 2 * m_type->m_rank * sizeof(DWORD); |
3625 | HRESULT hr = S_OK; |
3626 | |
3627 | // Ensure that the proper subset is in the cache. m_idxLower and m_idxUpper are initialized to -1, so the |
3628 | // first time we hit this condition check, it will evaluate to true. We will set these inside the |
3629 | // consequent to the range starting at nPosition and ending at the last available cache position. Thus, |
3630 | // after the first time we hit this, we are asking if nPosition lies outside the range we've cached. |
3631 | if (nPosition < m_idxLower || nPosition >= m_idxUpper) |
3632 | { |
3633 | const SIZE_T cbElemSize = m_info.arrayInfo.elementSize; |
3634 | SIZE_T len = 1; |
3635 | |
3636 | if (cbElemSize != 0) |
3637 | { |
3638 | // the element size could be bigger than the cache, but we want len to be at least 1. |
3639 | len = max(ARRAY_CACHE_SIZE / cbElemSize, len); |
3640 | } |
3641 | else _ASSERTE(cbElemSize != 0); |
3642 | |
3643 | m_idxLower = nPosition; |
3644 | m_idxUpper = min(m_idxLower + len, m_info.arrayInfo.componentCount); |
3645 | _ASSERTE(m_idxLower < m_idxUpper); |
3646 | |
3647 | SIZE_T cbOffsetFrom = m_info.arrayInfo.offsetToArrayBase + m_idxLower * cbElemSize; |
3648 | |
3649 | SIZE_T cbSize = (m_idxUpper - m_idxLower) * cbElemSize; // we'll copy the largest range of ellements possible |
3650 | |
3651 | _ASSERTE(cbSize <= m_info.objSize); |
3652 | // Copy the proper subrange of the array over |
3653 | EX_TRY |
3654 | { |
3655 | m_valueHome.GetInternalValue(MemoryRange(m_pObjectCopy + cbHeader, cbSize), cbOffsetFrom); // throws |
3656 | } |
3657 | EX_CATCH_HRESULT(hr); |
3658 | IfFailRet(hr); |
3659 | } |
3660 | |
3661 | SIZE_T size = m_info.arrayInfo.elementSize; |
3662 | _ASSERTE(size <= m_info.objSize); |
3663 | |
3664 | SIZE_T offset = m_info.arrayInfo.offsetToArrayBase + (nPosition * size); |
3665 | void * localAddress = m_pObjectCopy + cbHeader + ((nPosition - m_idxLower) * size); |
3666 | |
3667 | EX_TRY |
3668 | { |
3669 | m_valueHome.CreateInternalValue(m_elemtype, |
3670 | offset, |
3671 | localAddress, |
3672 | (ULONG32)size, |
3673 | ppValue); // throws |
3674 | } |
3675 | EX_CATCH_HRESULT(hr); |
3676 | return hr; |
3677 | |
3678 | } // CordbArrayValue::GetElementAtPosition |
3679 | |
3680 | HRESULT CordbArrayValue::IsValid(BOOL *pbValid) |
3681 | { |
3682 | VALIDATE_POINTER_TO_OBJECT(pbValid, BOOL *); |
3683 | |
3684 | // <TODO>@todo: implement tracking of objects across collections.</TODO> |
3685 | |
3686 | return E_NOTIMPL; |
3687 | } |
3688 | |
3689 | HRESULT CordbArrayValue::CreateRelocBreakpoint( |
3690 | ICorDebugValueBreakpoint **ppBreakpoint) |
3691 | { |
3692 | VALIDATE_POINTER_TO_OBJECT(ppBreakpoint, ICorDebugValueBreakpoint **); |
3693 | |
3694 | return E_NOTIMPL; |
3695 | } |
3696 | |
3697 | // Creates a handle of the given type for this heap value. |
3698 | // Not Implemented In-Proc. |
3699 | // Arguments: |
3700 | // input: handleType - type of the handle to be created |
3701 | // output: ppHandle - on success, the newly created handle |
3702 | // Return Value: S_OK on success or E_INVALIDARG, E_OUTOFMEMORY, or CORDB_E_HELPER_MAY_DEADLOCK |
3703 | HRESULT CordbArrayValue::CreateHandle( |
3704 | CorDebugHandleType handleType, |
3705 | ICorDebugHandleValue ** ppHandle) |
3706 | { |
3707 | PUBLIC_API_ENTRY(this); |
3708 | FAIL_IF_NEUTERED(this); |
3709 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
3710 | |
3711 | return CordbValue::InternalCreateHandle(handleType, ppHandle); |
3712 | } // CordbArrayValue::CreateHandle |
3713 | |
3714 | // get a copy of the array |
3715 | // Arguments |
3716 | // output: pTo - pointer to a caller-allocated and managed buffer to hold the copy. The caller must guarantee |
3717 | // that this is large enough to hold the entire array |
3718 | // Return Value: S_OK on success, E_INVALIDARG or read process memory errors on failure |
3719 | HRESULT CordbArrayValue::GetValue(void *pTo) |
3720 | { |
3721 | VALIDATE_POINTER_TO_OBJECT_ARRAY(pTo, void *, 1, false, true); |
3722 | FAIL_IF_NEUTERED(this); |
3723 | |
3724 | HRESULT hr = S_OK; |
3725 | EX_TRY |
3726 | { |
3727 | // Copy out the value, which is the whole array. |
3728 | // There's no lazy-evaluation here, so this could be rather large |
3729 | m_valueHome.GetValue(MemoryRange(pTo, m_size)); // throws |
3730 | } |
3731 | EX_CATCH_HRESULT(hr); |
3732 | return hr; |
3733 | } // CordbArrayValue::GetValue |
3734 | |
3735 | HRESULT CordbArrayValue::SetValue(void *pFrom) |
3736 | { |
3737 | // You're not allowed to set a whole array at once. |
3738 | return E_INVALIDARG; |
3739 | } |
3740 | |
3741 | // initialize a new instance of CordbArrayValue |
3742 | // Arguments: none |
3743 | // Return Value: S_OK on success or E_OUTOFMEMORY or read process memory errors on failure |
3744 | // Note: we are only initializing information about the array (rank, sizes, dimensions, etc) here. We will not |
3745 | // attempt to read array contents until we receive a request to do so. |
3746 | HRESULT CordbArrayValue::Init() |
3747 | { |
3748 | INTERNAL_SYNC_API_ENTRY(this->GetProcess()); // |
3749 | HRESULT hr = S_OK; |
3750 | |
3751 | SIZE_T cbVector = m_info.arrayInfo.rank * sizeof(DWORD); |
3752 | _ASSERTE(cbVector <= m_info.objSize); |
3753 | |
3754 | int = 2 * (int)cbVector; |
3755 | |
3756 | // Find largest data size that will fit in cache |
3757 | SIZE_T cbData = m_info.arrayInfo.componentCount * m_info.arrayInfo.elementSize; |
3758 | if (cbData > ARRAY_CACHE_SIZE) |
3759 | { |
3760 | cbData = (ARRAY_CACHE_SIZE / m_info.arrayInfo.elementSize) |
3761 | * m_info.arrayInfo.elementSize; |
3762 | } |
3763 | |
3764 | if (cbData < m_info.arrayInfo.elementSize) |
3765 | { |
3766 | cbData = m_info.arrayInfo.elementSize; |
3767 | } |
3768 | |
3769 | // Allocate memory |
3770 | m_pObjectCopy = new (nothrow) BYTE[cbHeader + cbData]; |
3771 | if (m_pObjectCopy == NULL) |
3772 | return E_OUTOFMEMORY; |
3773 | |
3774 | |
3775 | m_arrayLowerBase = NULL; |
3776 | m_arrayUpperBase = NULL; |
3777 | |
3778 | // Copy base vectors into header. (Offsets are 0 if the vectors aren't used) |
3779 | if (m_info.arrayInfo.offsetToLowerBounds != 0) |
3780 | { |
3781 | m_arrayLowerBase = (DWORD*)(m_pObjectCopy); |
3782 | EX_TRY |
3783 | { |
3784 | m_valueHome.GetInternalValue(MemoryRange(m_arrayLowerBase, cbVector), |
3785 | m_info.arrayInfo.offsetToLowerBounds); // throws |
3786 | } |
3787 | EX_CATCH_HRESULT(hr); |
3788 | IfFailRet(hr); |
3789 | } |
3790 | |
3791 | |
3792 | if (m_info.arrayInfo.offsetToUpperBounds != 0) |
3793 | { |
3794 | m_arrayUpperBase = (DWORD*)(m_pObjectCopy + cbVector); |
3795 | EX_TRY |
3796 | { |
3797 | m_valueHome.GetInternalValue(MemoryRange(m_arrayUpperBase, cbVector), |
3798 | m_info.arrayInfo.offsetToUpperBounds); // throws |
3799 | } |
3800 | EX_CATCH_HRESULT(hr); |
3801 | IfFailRet(hr); |
3802 | } |
3803 | |
3804 | // That's all for now. We'll do lazy-evaluation for the array contents. |
3805 | |
3806 | return hr; |
3807 | } // CordbArrayValue::Init |
3808 | |
3809 | // CordbArrayValue::GetThreadOwningMonitorLock |
3810 | // If a managed thread owns the monitor lock on this object then *ppThread |
3811 | // will point to that thread and S_OK will be returned. The thread object is valid |
3812 | // until the thread exits. *pAcquisitionCount will indicate the number of times |
3813 | // this thread would need to release the lock before it returns to being |
3814 | // unowned. |
3815 | // If no managed thread owns the monitor lock on this object then *ppThread |
3816 | // and pAcquisitionCount will be unchanged and S_FALSE returned. |
3817 | // If ppThread or pAcquisitionCount is not a valid pointer the result is |
3818 | // undefined. |
3819 | // If any error occurs such that it cannot be determined which, if any, thread |
3820 | // owns the monitor lock on this object then a failing HRESULT will be returned |
3821 | HRESULT CordbArrayValue::GetThreadOwningMonitorLock(ICorDebugThread **ppThread, DWORD *pAcquisitionCount) |
3822 | { |
3823 | PUBLIC_API_ENTRY(this); |
3824 | FAIL_IF_NEUTERED(this); |
3825 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
3826 | |
3827 | return CordbHeapValue3Impl::GetThreadOwningMonitorLock(GetProcess(), |
3828 | GetValueHome()->GetAddress(), ppThread, pAcquisitionCount); |
3829 | } |
3830 | |
3831 | // CordbArrayValue::GetMonitorEventWaitList |
3832 | // Provides an ordered list of threads which are queued on the event associated |
3833 | // with a monitor lock. The first thread in the list is the first thread which |
3834 | // will be released by the next call to Monitor.Pulse, the next thread in the list |
3835 | // will be released on the following call, and so on. |
3836 | // If this list is non-empty S_OK will be returned, if it is empty S_FALSE |
3837 | // will be returned (the enumeration is still valid, just empty). |
3838 | // In either case the enumeration interface is only usable for the duration |
3839 | // of the current synchronized state, however the threads interfaces dispensed |
3840 | // from it are valid until the thread exits. |
3841 | // If ppThread is not a valid pointer the result is undefined. |
3842 | // If any error occurs such that it cannot be determined which, if any, threads |
3843 | // are waiting for the monitor then a failing HRESULT will be returned |
3844 | HRESULT CordbArrayValue::GetMonitorEventWaitList(ICorDebugThreadEnum **ppThreadEnum) |
3845 | { |
3846 | PUBLIC_API_ENTRY(this); |
3847 | FAIL_IF_NEUTERED(this); |
3848 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
3849 | |
3850 | return CordbHeapValue3Impl::GetMonitorEventWaitList(GetProcess(), |
3851 | GetValueHome()->GetAddress(), |
3852 | ppThreadEnum); |
3853 | } |
3854 | |
3855 | /* ------------------------------------------------------------------------- * |
3856 | * Handle Value |
3857 | * ------------------------------------------------------------------------- */ |
3858 | // constructor |
3859 | // Arguments: |
3860 | // input: |
3861 | // pAppDomain - app domain to which the value belongs |
3862 | // pType - type information for the value |
3863 | // handleType - indicates whether we are constructing a strong or weak handle |
3864 | CordbHandleValue::CordbHandleValue( |
3865 | CordbAppDomain * pAppdomain, |
3866 | CordbType * pType, // The type of object that we create handle on |
3867 | CorDebugHandleType handleType) // strong or weak handle |
3868 | : CordbValue(pAppdomain, pType, NULL, false, |
3869 | pAppdomain->GetSweepableExitNeuterList() |
3870 | ) |
3871 | { |
3872 | m_vmHandle = VMPTR_OBJECTHANDLE::NullPtr(); |
3873 | m_fCanBeValid = TRUE; |
3874 | |
3875 | m_handleType = handleType; |
3876 | m_size = sizeof(void*); |
3877 | } // CordbHandleValue::CordbHandleValue |
3878 | |
3879 | //----------------------------------------------------------------------------- |
3880 | // Assign internal handle to the given value, and update pertinent counters |
3881 | // |
3882 | // Arguments: |
3883 | // handle - non-null CLR ObjectHandle that this CordbHandleValue will represent |
3884 | // |
3885 | // Notes: |
3886 | // Call code:CordbHandleValue::ClearHandle to clear the handle value. |
3887 | void CordbHandleValue::AssignHandle(VMPTR_OBJECTHANDLE handle) |
3888 | { |
3889 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
3890 | _ASSERTE(m_vmHandle.IsNull()); |
3891 | |
3892 | // Use code:CordbHandleValue::ClearHandle to clear the handle value. |
3893 | _ASSERTE(!handle.IsNull()); |
3894 | |
3895 | m_vmHandle = handle; |
3896 | GetProcess()->IncrementOutstandingHandles(); |
3897 | } |
3898 | |
3899 | //----------------------------------------------------------------------------- |
3900 | // Clear the handle value |
3901 | // |
3902 | // Assumptions: |
3903 | // Caller only clears if not already cleared. |
3904 | // |
3905 | // Notes: |
3906 | // This is the inverse of code:CordbHandleValue::AssignHandle |
3907 | void CordbHandleValue::ClearHandle() |
3908 | { |
3909 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
3910 | _ASSERTE(!m_vmHandle.IsNull()); |
3911 | |
3912 | m_vmHandle = VMPTR_OBJECTHANDLE::NullPtr(); |
3913 | GetProcess()->DecrementOutstandingHandles(); |
3914 | } |
3915 | |
3916 | // initialize a new instance of CordbHandleValue |
3917 | // Arguments: |
3918 | // input: pHandle - non-null CLR ObjectHandle that this CordbHandleValue will represent |
3919 | // Return Value: S_OK on success or CORDBG_E_TARGET_INCONSISTENT, E_INVALIDARG, read process memory errors. |
3920 | HRESULT CordbHandleValue::Init(VMPTR_OBJECTHANDLE pHandle) |
3921 | { |
3922 | INTERNAL_SYNC_API_ENTRY(GetProcess()); |
3923 | HRESULT hr = S_OK; |
3924 | |
3925 | { |
3926 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
3927 | // If it is a strong handle, m_pHandle will not be NULL unless Dispose method is called. |
3928 | // If it is a weak handle, m_pHandle can be NULL when Dispose is called. |
3929 | AssignHandle(pHandle); |
3930 | } |
3931 | |
3932 | // This will init m_info. |
3933 | IfFailRet(RefreshHandleValue()); |
3934 | |
3935 | // objRefBad is currently overloaded to mean that 1) the object ref is invalid, or 2) the object ref is NULL. |
3936 | // NULL is clearly not a bad object reference, but in either case we have no more type data to work with, |
3937 | // so don't attempt to assign more specific type information to the reference. |
3938 | if (!m_info.objRefBad) |
3939 | { |
3940 | // We need to get the type info from the left side. |
3941 | CordbType *newtype; |
3942 | |
3943 | IfFailRet(CordbType::TypeDataToType(m_appdomain, &m_info.objTypeData, &newtype)); |
3944 | |
3945 | m_type.Assign(newtype); |
3946 | } |
3947 | |
3948 | return hr; |
3949 | } // CordbHandleValue::Init |
3950 | |
3951 | // destructor |
3952 | CordbHandleValue::~CordbHandleValue() |
3953 | { |
3954 | DTOR_ENTRY(this); |
3955 | |
3956 | _ASSERTE(IsNeutered()); |
3957 | } // CordbHandleValue::~CordbHandleValue |
3958 | |
3959 | // Free left-side resources, mainly the GC handle keeping the object alive. |
3960 | void CordbHandleValue::NeuterLeftSideResources() |
3961 | { |
3962 | Dispose(); |
3963 | |
3964 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
3965 | Neuter(); |
3966 | } // CordbHandleValue::NeuterLeftSideResources |
3967 | |
3968 | // Neuter |
3969 | // Notes: |
3970 | // CordbHandleValue may hold Left-Side resources via the GC handle. |
3971 | // By the time we neuter it, those resources must have been freed, |
3972 | // either explicitly by calling code:CordbHandleValue::Dispose, or |
3973 | // implicitly by the left-side process exiting. |
3974 | void CordbHandleValue::Neuter() |
3975 | { |
3976 | // CordbHandleValue is on the AppDomainExit neuter list. |
3977 | |
3978 | // We should have cleaned up our Left-side resource by now (m_vmHandle |
3979 | // should be null). If AppDomain / Process has already exited, then the LS |
3980 | // already cleaned them up for us, and so we don't worry about them. |
3981 | bool fAppDomainIsAlive = (m_appdomain != NULL && !m_appdomain->IsNeutered()); |
3982 | if (fAppDomainIsAlive) |
3983 | { |
3984 | BOOL fTargetIsDead = !GetProcess()->IsSafeToSendEvents() || GetProcess()->m_exiting; |
3985 | if (!fTargetIsDead) |
3986 | { |
3987 | _ASSERTE(m_vmHandle.IsNull()); |
3988 | } |
3989 | } |
3990 | |
3991 | CordbValue::Neuter(); |
3992 | } // CordbHandleValue::Neuter |
3993 | |
3994 | // Helper: Refresh the handle value object. |
3995 | // Gets information about the object to which the handle points. |
3996 | // Arguments: none |
3997 | // Return Value: S_OK on success, CORDBG_E_HANDLE_HAS_BEEN_DISPOSED, CORDBG_E_BAD_REFERENCE_VALUE, |
3998 | // errors from read process memory. |
3999 | HRESULT CordbHandleValue::RefreshHandleValue() |
4000 | { |
4001 | INTERNAL_SYNC_API_ENTRY(this->GetProcess()); // |
4002 | _ASSERTE(m_appdomain != NULL); |
4003 | _ASSERTE(!m_appdomain->IsNeutered()); |
4004 | |
4005 | // If Dispose has been called, don't bother to refresh handle value. |
4006 | if (m_vmHandle.IsNull()) |
4007 | { |
4008 | return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED; |
4009 | } |
4010 | |
4011 | // If weak handle and the object was dead, no point to refresh the handle value |
4012 | if (m_fCanBeValid == FALSE) |
4013 | { |
4014 | return CORDBG_E_BAD_REFERENCE_VALUE; |
4015 | } |
4016 | |
4017 | HRESULT hr = S_OK; |
4018 | CorElementType type = m_type->m_elementType; |
4019 | |
4020 | _ASSERTE((m_pProcess != NULL)); |
4021 | |
4022 | _ASSERTE (type != ELEMENT_TYPE_GENERICINST); |
4023 | _ASSERTE (type != ELEMENT_TYPE_VAR); |
4024 | _ASSERTE (type != ELEMENT_TYPE_MVAR); |
4025 | |
4026 | CordbProcess * pProcess = GetProcess(); |
4027 | void * objectAddress = NULL; |
4028 | CORDB_ADDRESS objectHandle = 0; |
4029 | |
4030 | EX_TRY |
4031 | { |
4032 | objectHandle = pProcess->GetDAC()->GetHandleAddressFromVmHandle(m_vmHandle); |
4033 | if (type != ELEMENT_TYPE_TYPEDBYREF) |
4034 | { |
4035 | pProcess->SafeReadBuffer(TargetBuffer(objectHandle, sizeof(void *)), (BYTE *)&objectAddress); |
4036 | } |
4037 | } |
4038 | EX_CATCH_HRESULT(hr); |
4039 | IfFailRet(hr); |
4040 | EX_TRY |
4041 | { |
4042 | if (type == ELEMENT_TYPE_TYPEDBYREF) |
4043 | { |
4044 | CordbReferenceValue::GetTypedByRefData(pProcess, |
4045 | objectHandle, |
4046 | type, |
4047 | m_appdomain->GetADToken(), |
4048 | &m_info); |
4049 | } |
4050 | else |
4051 | { |
4052 | CordbReferenceValue::GetObjectData(pProcess, |
4053 | objectAddress, |
4054 | type, |
4055 | m_appdomain->GetADToken(), |
4056 | &m_info); |
4057 | } |
4058 | } |
4059 | EX_CATCH_HRESULT(hr); |
4060 | IfFailRet(hr); |
4061 | |
4062 | // If reference is already gone bad or reference is NULL, |
4063 | // don't bother to refetch in the future. |
4064 | // |
4065 | if ((m_info.objRefBad) || (m_info.objRef == NULL)) |
4066 | { |
4067 | m_fCanBeValid = FALSE; |
4068 | } |
4069 | |
4070 | return hr; |
4071 | } |
4072 | // CordbHandleValue::RefreshHandleValue |
4073 | |
4074 | HRESULT CordbHandleValue::QueryInterface(REFIID id, void **pInterface) |
4075 | { |
4076 | VALIDATE_POINTER_TO_OBJECT(pInterface, void **); |
4077 | |
4078 | if (id == IID_ICorDebugValue) |
4079 | { |
4080 | *pInterface = static_cast<ICorDebugValue*>(this); |
4081 | } |
4082 | else if (id == IID_ICorDebugValue2) |
4083 | { |
4084 | *pInterface = static_cast<ICorDebugValue2*>(this); |
4085 | } |
4086 | else if (id == IID_ICorDebugValue3) |
4087 | { |
4088 | *pInterface = static_cast<ICorDebugValue3*>(this); |
4089 | } |
4090 | else if (id == IID_ICorDebugReferenceValue) |
4091 | { |
4092 | *pInterface = static_cast<ICorDebugReferenceValue*>(this); |
4093 | } |
4094 | else if (id == IID_ICorDebugHandleValue) |
4095 | { |
4096 | *pInterface = static_cast<ICorDebugHandleValue*>(this); |
4097 | } |
4098 | else if (id == IID_IUnknown) |
4099 | { |
4100 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugHandleValue*>(this)); |
4101 | } |
4102 | else |
4103 | { |
4104 | *pInterface = NULL; |
4105 | return E_NOINTERFACE; |
4106 | } |
4107 | |
4108 | ExternalAddRef(); |
4109 | return S_OK; |
4110 | } // CordbHandleValue::QueryInterface |
4111 | |
4112 | |
4113 | // return handle type. Currently we have strong and weak. |
4114 | // Arguments: |
4115 | // output: pType - the handle type unless pType is null |
4116 | // Return Value: S_OK on success or E_INVALIDARG or CORDBG_E_HANDLE_HAS_BEEN_DISPOSED on failure |
4117 | HRESULT CordbHandleValue::GetHandleType(CorDebugHandleType *pType) |
4118 | { |
4119 | PUBLIC_REENTRANT_API_ENTRY(this); |
4120 | VALIDATE_POINTER_TO_OBJECT(pType, CorDebugHandleType *); |
4121 | FAIL_IF_NEUTERED(this); |
4122 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
4123 | _ASSERTE(m_appdomain != NULL); |
4124 | _ASSERTE(!m_appdomain->IsNeutered()); |
4125 | |
4126 | if (m_vmHandle.IsNull()) |
4127 | { |
4128 | // handle has been disposed! |
4129 | return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED; |
4130 | } |
4131 | *pType = m_handleType; |
4132 | return S_OK; |
4133 | } // CordbHandleValue::GetHandleType |
4134 | |
4135 | // Dispose will cause handle to be recycled. |
4136 | // Arguments: none |
4137 | // Return Value: S_OK on success, CORDBG_E_HANDLE_HAS_BEEN_DISPOSED or errors from the |
4138 | // DB_IPCE_DISPOSE_HANDLE event |
4139 | |
4140 | // @dbgtodo Microsoft inspection: remove the dispose handle hresults when the IPC events are eliminated |
4141 | HRESULT CordbHandleValue::Dispose() |
4142 | { |
4143 | PUBLIC_REENTRANT_API_ENTRY(this); |
4144 | FAIL_IF_NEUTERED(this); |
4145 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
4146 | _ASSERTE(m_appdomain != NULL); |
4147 | _ASSERTE(!m_appdomain->IsNeutered()); |
4148 | |
4149 | HRESULT hr = S_OK; |
4150 | DebuggerIPCEvent event; |
4151 | CordbProcess *process; |
4152 | |
4153 | process = GetProcess(); |
4154 | |
4155 | // Process should still be alive because it would have neutered us if it became invalid. |
4156 | _ASSERTE(process != NULL); |
4157 | |
4158 | VMPTR_OBJECTHANDLE vmObjHandle = VMPTR_OBJECTHANDLE::NullPtr(); |
4159 | { |
4160 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
4161 | if (m_vmHandle.IsNull()) |
4162 | { |
4163 | // handle has been disposed! |
4164 | return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED; |
4165 | } |
4166 | |
4167 | vmObjHandle = m_vmHandle; |
4168 | ClearHandle(); // set m_pHandle to null. |
4169 | |
4170 | if (process->m_exiting) |
4171 | { |
4172 | // process is exiting. Don't do anything |
4173 | return S_OK; |
4174 | } |
4175 | } |
4176 | |
4177 | // recycle the handle to EE |
4178 | process->InitIPCEvent(&event, |
4179 | DB_IPCE_DISPOSE_HANDLE, |
4180 | false, |
4181 | m_appdomain->GetADToken()); |
4182 | |
4183 | event.DisposeHandle.vmObjectHandle = vmObjHandle; |
4184 | if (m_handleType == HANDLE_STRONG) |
4185 | { |
4186 | event.DisposeHandle.fStrong = TRUE; |
4187 | } |
4188 | else |
4189 | { |
4190 | event.DisposeHandle.fStrong = FALSE; |
4191 | } |
4192 | |
4193 | // Note: one-way event here... |
4194 | hr = process->SendIPCEvent(&event, sizeof(DebuggerIPCEvent)); |
4195 | |
4196 | hr = WORST_HR(hr, event.hr); |
4197 | |
4198 | return hr; |
4199 | } // CordbHandleValue::Dispose |
4200 | |
4201 | // get the type of the object to which the handle points |
4202 | // Arguments: |
4203 | // output: pType - the object type on success |
4204 | // Return Value: S_OK on success, CORDBG_E_HANDLE_HAS_BEEN_DISPOSED, CORDBG_E_CLASS_NOT_LOADED or synchronization errors on |
4205 | // failure |
4206 | HRESULT CordbHandleValue::GetType(CorElementType *pType) |
4207 | { |
4208 | PUBLIC_REENTRANT_API_ENTRY(this); |
4209 | VALIDATE_POINTER_TO_OBJECT(pType, CorElementType *); |
4210 | FAIL_IF_NEUTERED(this); |
4211 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
4212 | _ASSERTE(m_appdomain != NULL); |
4213 | _ASSERTE(!m_appdomain->IsNeutered()); |
4214 | |
4215 | HRESULT hr = S_OK; |
4216 | |
4217 | if (m_vmHandle.IsNull()) |
4218 | { |
4219 | return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED; |
4220 | } |
4221 | |
4222 | bool isBoxedVCObject = false; |
4223 | if ((m_type->m_pClass != NULL) && (m_type->m_elementType != ELEMENT_TYPE_STRING)) |
4224 | { |
4225 | EX_TRY |
4226 | { |
4227 | isBoxedVCObject = m_type->m_pClass->IsValueClass(); |
4228 | } |
4229 | EX_CATCH_HRESULT(hr); |
4230 | if (FAILED(hr)) |
4231 | return hr; |
4232 | } |
4233 | |
4234 | if (isBoxedVCObject) |
4235 | { |
4236 | // if we create the handle to a boxed value type, then the type is |
4237 | // E_T_CLASS. m_type is the underlying value type. That is incorrect to |
4238 | // return. |
4239 | // |
4240 | *pType = ELEMENT_TYPE_CLASS; |
4241 | return S_OK; |
4242 | } |
4243 | |
4244 | return m_type->GetType(pType); |
4245 | } // CordbHandleValue::GetType |
4246 | |
4247 | // get the size of the handle-- this will always return the size of the handle itself (just pointer size), so |
4248 | // it's not particularly interesting. |
4249 | // Arguments: |
4250 | // output: pSize - the size of the handle (on success). This must be non-null. Memory management belongs |
4251 | // to the caller. |
4252 | // Return Value: S_OK on success, E_INVALIDARG (if pSize is null), or CORDBG_E_HANDLE_HAS_BEEN_DISPOSED on failure |
4253 | HRESULT CordbHandleValue::GetSize(ULONG32 *pSize) |
4254 | { |
4255 | PUBLIC_REENTRANT_API_ENTRY(this); |
4256 | VALIDATE_POINTER_TO_OBJECT(pSize, ULONG32 *); |
4257 | FAIL_IF_NEUTERED(this); |
4258 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
4259 | _ASSERTE(m_appdomain != NULL); |
4260 | _ASSERTE(!m_appdomain->IsNeutered()); |
4261 | |
4262 | if (m_vmHandle.IsNull()) |
4263 | { |
4264 | return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED; |
4265 | } |
4266 | |
4267 | if (m_size > ULONG_MAX) |
4268 | { |
4269 | *pSize = ULONG_MAX; |
4270 | return (COR_E_OVERFLOW); |
4271 | } |
4272 | |
4273 | //return the size of reference |
4274 | *pSize = (ULONG)m_size; |
4275 | return S_OK; |
4276 | } // CordbHandleValue::GetSize |
4277 | |
4278 | // get the size of the handle-- this will always return the size of the handle itself (just pointer size), so |
4279 | // it's not particularly interesting. |
4280 | // Arguments: |
4281 | // output: pSize - the size of the handle (on success). This must be non-null. Memory management belongs |
4282 | // to the caller. |
4283 | // Return Value: S_OK on success, E_INVALIDARG (if pSize is null), or CORDBG_E_HANDLE_HAS_BEEN_DISPOSED on failure |
4284 | HRESULT CordbHandleValue::GetSize64(ULONG64 *pSize) |
4285 | { |
4286 | PUBLIC_REENTRANT_API_ENTRY(this); |
4287 | VALIDATE_POINTER_TO_OBJECT(pSize, ULONG64 *); |
4288 | FAIL_IF_NEUTERED(this); |
4289 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
4290 | _ASSERTE(m_appdomain != NULL); |
4291 | _ASSERTE(!m_appdomain->IsNeutered()); |
4292 | |
4293 | if (m_vmHandle.IsNull()) |
4294 | { |
4295 | return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED; |
4296 | } |
4297 | |
4298 | //return the size of reference |
4299 | *pSize = m_size; |
4300 | return S_OK; |
4301 | } // CordbHandleValue::GetSize |
4302 | |
4303 | // Get the target address of the handle |
4304 | // Arguments: |
4305 | // output: pAddress - handle address on success. This must be non-null and memory is managed by the caller |
4306 | // Return Value: S_OK on success or CORDBG_E_HANDLE_HAS_BEEN_DISPOSED or E_INVALIDARG on failure |
4307 | HRESULT CordbHandleValue::GetAddress(CORDB_ADDRESS *pAddress) |
4308 | { |
4309 | PUBLIC_REENTRANT_API_ENTRY(this); |
4310 | VALIDATE_POINTER_TO_OBJECT(pAddress, CORDB_ADDRESS *); |
4311 | FAIL_IF_NEUTERED(this); |
4312 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
4313 | _ASSERTE(m_appdomain != NULL); |
4314 | _ASSERTE(!m_appdomain->IsNeutered()); |
4315 | |
4316 | if (m_vmHandle.IsNull()) |
4317 | { |
4318 | return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED; |
4319 | } |
4320 | |
4321 | HRESULT hr = S_OK; |
4322 | EX_TRY |
4323 | { |
4324 | *pAddress = GetProcess()->GetDAC()->GetHandleAddressFromVmHandle(m_vmHandle); |
4325 | } |
4326 | EX_CATCH_HRESULT(hr); |
4327 | return hr; |
4328 | } // CordbHandleValue::GetAddress |
4329 | |
4330 | HRESULT CordbHandleValue::CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint) |
4331 | { |
4332 | return E_NOTIMPL; |
4333 | } // CreateBreakpoint |
4334 | |
4335 | // indicates whether a handle is null |
4336 | // Arguments: |
4337 | // output: pbNull - true iff the handle is null and pbNull is non-null.Memory is managed by the caller |
4338 | // Return Value: S_OK on success or CORDBG_E_HANDLE_HAS_BEEN_DISPOSED or E_INVALIDARG, CORDBG_E_BAD_REFERENCE_VALUE, |
4339 | // errors from read process memory. |
4340 | HRESULT CordbHandleValue::IsNull(BOOL *pbNull) |
4341 | { |
4342 | PUBLIC_REENTRANT_API_ENTRY(this); |
4343 | VALIDATE_POINTER_TO_OBJECT(pbNull, BOOL *); |
4344 | FAIL_IF_NEUTERED(this); |
4345 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
4346 | _ASSERTE(m_appdomain != NULL); |
4347 | _ASSERTE(!m_appdomain->IsNeutered()); |
4348 | |
4349 | HRESULT hr = S_OK; |
4350 | |
4351 | *pbNull = FALSE; |
4352 | |
4353 | if (m_vmHandle.IsNull()) |
4354 | { |
4355 | return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED; |
4356 | } |
4357 | |
4358 | |
4359 | // Only return true if handle is long weak handle and is disposed. |
4360 | if (m_handleType == HANDLE_WEAK_TRACK_RESURRECTION) |
4361 | { |
4362 | hr = RefreshHandleValue(); |
4363 | if (FAILED(hr)) |
4364 | { |
4365 | return hr; |
4366 | } |
4367 | |
4368 | if (m_info.objRef == NULL) |
4369 | { |
4370 | *pbNull = TRUE; |
4371 | } |
4372 | } |
4373 | else if (m_info.objRef == NULL) |
4374 | { |
4375 | *pbNull = TRUE; |
4376 | } |
4377 | |
4378 | // strong handle always return false for IsNull |
4379 | |
4380 | return S_OK; |
4381 | } // CordbHandleValue::IsNull |
4382 | |
4383 | // gets a copy of the value of the handle |
4384 | // Arguments: |
4385 | // output: pValue - handle { on success. This must be non-null and memory is managed by the caller |
4386 | // Return Value: S_OK on success or CORDBG_E_HANDLE_HAS_BEEN_DISPOSED or E_INVALIDARG, CORDBG_E_BAD_REFERENCE_VALUE, |
4387 | // errors from read process memory. |
4388 | HRESULT CordbHandleValue::GetValue(CORDB_ADDRESS *pValue) |
4389 | { |
4390 | PUBLIC_REENTRANT_API_ENTRY(this); |
4391 | VALIDATE_POINTER_TO_OBJECT(pValue, CORDB_ADDRESS *); |
4392 | FAIL_IF_NEUTERED(this); |
4393 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
4394 | _ASSERTE(m_appdomain != NULL); |
4395 | _ASSERTE(!m_appdomain->IsNeutered()); |
4396 | |
4397 | if (m_vmHandle.IsNull()) |
4398 | { |
4399 | return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED; |
4400 | } |
4401 | |
4402 | RefreshHandleValue(); |
4403 | *pValue = PTR_TO_CORDB_ADDRESS(m_info.objRef); |
4404 | return S_OK; |
4405 | } // CordbHandleValue::GetValue |
4406 | |
4407 | HRESULT CordbHandleValue::SetValue(CORDB_ADDRESS value) |
4408 | { |
4409 | // do not support SetValue on Handle |
4410 | return E_FAIL; |
4411 | } // CordbHandleValue::GetValue |
4412 | |
4413 | // get an ICDValue to represent the object to which the handle refers |
4414 | // Arguments: |
4415 | // output: ppValue - pointer to the ICDValue for the handle referent as long as ppValue is non-null |
4416 | // Return Value: S_OK on success or CORDBG_E_HANDLE_HAS_BEEN_DISPOSED or E_INVALIDARG, CORDBG_E_BAD_REFERENCE_VALUE, |
4417 | // errors from read process memory. |
4418 | HRESULT CordbHandleValue::Dereference(ICorDebugValue **ppValue) |
4419 | { |
4420 | HRESULT hr = S_OK; |
4421 | PUBLIC_REENTRANT_API_ENTRY(this); |
4422 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
4423 | FAIL_IF_NEUTERED(this); |
4424 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
4425 | _ASSERTE(m_appdomain != NULL); |
4426 | _ASSERTE(!m_appdomain->IsNeutered()); |
4427 | |
4428 | *ppValue = NULL; |
4429 | |
4430 | if (m_vmHandle.IsNull()) |
4431 | { |
4432 | return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED; |
4433 | } |
4434 | |
4435 | hr = RefreshHandleValue(); |
4436 | if (FAILED(hr)) |
4437 | { |
4438 | return hr; |
4439 | } |
4440 | |
4441 | if ((m_info.objRefBad) || (m_info.objRef == NULL)) |
4442 | { |
4443 | return CORDBG_E_BAD_REFERENCE_VALUE; |
4444 | } |
4445 | |
4446 | EX_TRY |
4447 | { |
4448 | hr = CordbReferenceValue::DereferenceCommon(m_appdomain, |
4449 | m_type, |
4450 | NULL, // don't support typed-by-refs |
4451 | &m_info, |
4452 | ppValue); |
4453 | } |
4454 | EX_CATCH_HRESULT(hr); |
4455 | return hr; |
4456 | } // CordbHandleValue::Dereference |
4457 | |
4458 | HRESULT CordbHandleValue::DereferenceStrong(ICorDebugValue **ppValue) |
4459 | { |
4460 | return E_NOTIMPL; |
4461 | } |
4462 | |
4463 | // CordbHeapValue3Impl::GetThreadOwningMonitorLock |
4464 | // If a managed thread owns the monitor lock on this object then *ppThread |
4465 | // will point to that thread and S_OK will be returned. The thread object is valid |
4466 | // until the thread exits. *pAcquisitionCount will indicate the number of times |
4467 | // this thread would need to release the lock before it returns to being |
4468 | // unowned. |
4469 | // If no managed thread owns the monitor lock on this object then *ppThread |
4470 | // and pAcquisitionCount will be unchanged and S_FALSE returned. |
4471 | // If ppThread or pAcquisitionCount is not a valid pointer the result is |
4472 | // undefined. |
4473 | // If any error occurs such that it cannot be determined which, if any, thread |
4474 | // owns the monitor lock on this object then a failing HRESULT will be returned |
4475 | HRESULT CordbHeapValue3Impl::GetThreadOwningMonitorLock(CordbProcess* pProcess, |
4476 | CORDB_ADDRESS remoteObjAddress, |
4477 | ICorDebugThread **ppThread, |
4478 | DWORD *pAcquisitionCount) |
4479 | { |
4480 | HRESULT hr = S_OK; |
4481 | EX_TRY |
4482 | { |
4483 | IDacDbiInterface *pDac = pProcess->GetDAC(); |
4484 | VMPTR_Object vmObj = pDac->GetObject(remoteObjAddress); |
4485 | MonitorLockInfo info = pDac->GetThreadOwningMonitorLock(vmObj); |
4486 | if(info.acquisitionCount == 0) |
4487 | { |
4488 | // unowned |
4489 | *ppThread = NULL; |
4490 | *pAcquisitionCount = 0; |
4491 | hr = S_FALSE; |
4492 | } |
4493 | else |
4494 | { |
4495 | RSLockHolder lockHolder(pProcess->GetProcessLock()); |
4496 | CordbThread* pThread = pProcess->LookupOrCreateThread(info.lockOwner); |
4497 | pThread->QueryInterface(__uuidof(ICorDebugThread), (VOID**) ppThread); |
4498 | *pAcquisitionCount = info.acquisitionCount; |
4499 | hr = S_OK; |
4500 | } |
4501 | } |
4502 | EX_CATCH_HRESULT(hr); |
4503 | return hr; |
4504 | } |
4505 | |
4506 | // A small helper for CordbHeapValue3Impl::GetMonitorEventWaitList that adds each enumerated thread to an array |
4507 | // Arguments: |
4508 | // vmThread - The thread to add |
4509 | // puserData - the array to add it to |
4510 | VOID ThreadEnumerationCallback(VMPTR_Thread vmThread, VOID* pUserData) |
4511 | { |
4512 | CQuickArrayList<VMPTR_Thread>* pThreadList = (CQuickArrayList<VMPTR_Thread>*) pUserData; |
4513 | pThreadList->Push(vmThread); |
4514 | } |
4515 | |
4516 | // CordbHeapValue3Impl::GetMonitorEventWaitList |
4517 | // Provides an ordered list of threads which are queued on the event associated |
4518 | // with a monitor lock. The first thread in the list is the first thread which |
4519 | // will be released by the next call to Monitor.Pulse, the next thread in the list |
4520 | // will be released on the following call, and so on. |
4521 | // If this list is non-empty S_OK will be returned, if it is empty S_FALSE |
4522 | // will be returned (the enumeration is still valid, just empty). |
4523 | // In either case the enumeration interface is only usable for the duration |
4524 | // of the current synchronized state, however the threads interfaces dispensed |
4525 | // from it are valid until the thread exits. |
4526 | // If ppThread is not a valid pointer the result is undefined. |
4527 | // If any error occurs such that it cannot be determined which, if any, threads |
4528 | // are waiting for the monitor then a failing HRESULT will be returned |
4529 | HRESULT CordbHeapValue3Impl::GetMonitorEventWaitList(CordbProcess* pProcess, |
4530 | CORDB_ADDRESS remoteObjAddress, |
4531 | ICorDebugThreadEnum **ppThreadEnum) |
4532 | { |
4533 | HRESULT hr = S_OK; |
4534 | RSSmartPtr<CordbThread> *rsThreads = NULL; |
4535 | EX_TRY |
4536 | { |
4537 | IDacDbiInterface *pDac = pProcess->GetDAC(); |
4538 | VMPTR_Object vmObj = pDac->GetObject(remoteObjAddress); |
4539 | CQuickArrayList<VMPTR_Thread> threads; |
4540 | pDac->EnumerateMonitorEventWaitList(vmObj, |
4541 | (IDacDbiInterface::FP_THREAD_ENUMERATION_CALLBACK)ThreadEnumerationCallback, (VOID*)&threads); |
4542 | |
4543 | rsThreads = new RSSmartPtr<CordbThread>[threads.Size()]; |
4544 | { |
4545 | RSLockHolder lockHolder(pProcess->GetProcessLock()); |
4546 | for(DWORD i = 0; i < threads.Size(); i++) |
4547 | { |
4548 | rsThreads[i].Assign(pProcess->LookupOrCreateThread(threads[i])); |
4549 | } |
4550 | } |
4551 | |
4552 | CordbThreadEnumerator* threadEnum = |
4553 | new CordbThreadEnumerator(pProcess, rsThreads, (DWORD)threads.Size()); |
4554 | pProcess->GetContinueNeuterList()->Add(pProcess, threadEnum); |
4555 | threadEnum->QueryInterface(__uuidof(ICorDebugThreadEnum), (VOID**)ppThreadEnum); |
4556 | if(threads.Size() == 0) |
4557 | { |
4558 | hr = S_FALSE; |
4559 | } |
4560 | } |
4561 | EX_CATCH_HRESULT(hr); |
4562 | delete [] rsThreads; |
4563 | return hr; |
4564 | } |
4565 | |